Cvičení: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14.
-
Nástroje
xargs
(aparallel
) -
find
-
Cvičení na
find
axargs
- Kontejnery
- Příklady práce s kontejnery
-
Proč máme všechny ty
systemctl
,dnf
,podman
,pip
, … - Úlohy před cvičením (deadline: začátek vašeho cvičení, týden 1. května – 5. května)
- Úlohy po cvičení (deadline: 21. května)
- Učební výstupy
- Seznam změn na této stránce
Hlavním tématem tohoto cvičení je používání kontejnerů: lehkých virtuálních
strojů, které jsou velmi užitečné pro testování a vývoj. Podíváme se ale
také na dvě užitečné utility, konkrétně xargs
a find
.
Témata jsou v podstatě nezávislá a můžete je číst v libovolném pořadí.
Nástroje xargs
(a parallel
)
xargs
ve své nejjednodušší podobě čte standardní vstup a převádí jej na
argumenty pro uživatelem zadaný program.
Předpokládejme, že existují následující soubory:
2023-03-16.txt 2023-03-24.txt 2023-04-01.txt 2023-04-09.txt
2023-03-17.txt 2023-03-25.txt 2023-04-02.txt 2023-04-10.txt
2023-03-18.txt 2023-03-26.txt 2023-04-03.txt 2023-04-11.txt
2023-03-19.txt 2023-03-27.txt 2023-04-04.txt 2023-04-12.txt
2023-03-20.txt 2023-03-28.txt 2023-04-05.txt 2023-04-13.txt
2023-03-21.txt 2023-03-29.txt 2023-04-06.txt 2023-04-14.txt
2023-03-22.txt 2023-03-30.txt 2023-04-07.txt
2023-03-23.txt 2023-03-31.txt 2023-04-08.txt
Jako malé cvičení napište jednořádkový příkaz shellu, který tyto soubory vytvoří.
Solution.Naším úkolem je odstranit soubory starší než 20 dní. V této verzi pouze provedeme echo příkazu, abychom nemuseli soubory při ladění našeho řešení znovu vytvářet.
cutoff_date="$( date -d "20 days ago" '+%Y%m%d' )"
for filename in 202[0-9]-[01][0-9]-[0-3][0-9].txt; do
date_num="$( basename "$filename" .txt | tr -d '-' )"
if [ "$date_num" -lt "$cutoff_date" ]; then
echo rm "$filename"
fi
done
To znamená, že program rm
by byl volán několikrát, přičemž by vždy
odstranil jen jeden soubor. Režie spojená se spouštěním nového procesu by
se mohla stát vážným úzkým hrdlem pro větší skripty (uvažujte například o
tisících souborů).
Bylo by mnohem lepší, kdybychom rm
zavolali jen jednou a zadali mu seznam
souborů, které má odstranit (tj. jako více argumentů).
Zde je řešením xargs
. Trochu program upravíme:
cutoff_date="$( date -d "20 days ago" '+%Y%m%d' )"
for filename in 202[0-9]-[01][0-9]-[0-3][0-9].txt; do
date_num="$( basename "$filename" .txt | tr -d '-' )"
if [ "$date_num" -lt "$cutoff_date" ]; then
echo "$filename"
fi
done | xargs echo rm
Místo toho, abychom soubor hned odstranili, vypíšeme pouze jeho název a
celou smyčku přesměrujeme rourou do xargs
, kde všechny jeho běžné
argumenty odkazují na program, který má být spuštěn.
Místo mnoha řádků s rm ...
uvidíme jen jeden dlouhý řádek s jediným
voláním rm
.
Další situací, kdy se xargs
může hodit, je sestavování složitého
příkazového řádku nebo situace, kdy by použití nahrazování příkazů ($( ... )
) způsobilo nečitelnost skriptu.
Záludné názvy souborů mohou samozřejmě stále způsobovat problémy, protože
xargs
předpokládá, že argumenty jsou odděleny bílými znaky. (Všimněte si,
že výše jsme byli v bezpečí, protože názvy souborů byly rozumné.) To lze
změnit pomocí --delimiter
.
Pokud do xargs
přesměrováváte výstup z vašeho programu, zvažte oddělení
položek nulovým bajtem (tj. terminátorem řetězce v jazyce C, \0
). To je
nejbezpečnější možnost, protože tento znak se nemůže objevit nikde uvnitř
žádného argumentu. A informujte o tom xargs
pomocí -0
nebo --null
.
Poznamenejme, že xargs
je dostatečně chytrý na to, aby si uvědomil, kdy by
byl příkazový řádek příliš dlouhý, a automaticky jej rozdělí (podrobnosti
viz manuál).
Je také dobré mít na paměti, že xargs
může příkaz provádět paralelně
(tj. rozdělit stdin na více částí a zavolat program vícekrát s různými
částmi) prostřednictvím -P
. Pokud jsou vaše shellové skripty pomalé, ale
máte dostatek procesorového výkonu, může vám to práci docela urychlit.
parallel
Tento program lze použít k paralelnímu provádění více příkazů, čímž se urychlí jejich provádění.
parallel
se chová téměř stejně jako xargs
, ale má mnohem lepší podporu
pro souběžné provádění jednotlivých úloh (nemíchání jejich výstupů,
provádění na vzdáleném stroji atd. atd.).
Rozdíly jsou poměrně dobře popsány v dokumentaci příkazu
parallel
.
Další podrobnosti naleznete v parallel_tutorial(1)
(ano, je to manuálová
stránka) a v parallel(1)
.
find
I když jsou ls(1)
a wildcardy dosti mocné, někdy potřebujeme vybrat
soubory pomocí složitějších kritérií. Zde je užitečný program find(1)
.
Bez argumentů vypíše všechny soubory v aktuálním adresáři, včetně souborů ve vnořených adresářích.
/
), pokud nevíte, co děláte (a
rozhodně ne na sdíleném počítači linux.ms.mff.cuni.cz
).
Pomocí parametru -name
můžete omezit vyhledávání na soubory odpovídající
zadanému wildcardu.
Následující příkaz vyhledá všechny soubory alpha.txt
v aktuálním adresáři
a v libovolném podadresáři (bez ohledu na hloubku).
find -name alpha.txt
Proč následující příkaz pro vyhledání všech souborů *.txt
nebude fungovat?
find -name *.txt
find
má mnoho voleb – nebudeme zde opakovat jeho manuálovou stránku, ale
zmíníme se o těch, které stojí za zapamatování.
-delete
okamžitě odstraní nalezené soubory. Velmi užitečné a velmi
nebezpečné.
-exec
spustí daný program pro každý nalezený soubor. Musíte použít {}
pro zadání názvu nalezeného souboru a příkaz musíte ukončit pomocí ;
(protože ;
ukončuje příkazy i v shellu, budete ho muset escapovat).
find -name '*.md' -exec wc -l {} \;
Všimněte si, že pro každý nalezený soubor dojde k novému vyvolání příkazu
wc
. To lze změnit změnou terminátoru příkazu (\;
) na +
. Podívejte se
na rozdíl mezi voláním následujících dvou příkazů:
find -name '*.md' -exec echo {} \;
find -name '*.md' -exec echo {} +
Upozornění
Ve výchozím nastavení vypíše find
jeden název souboru na řádek. Jméno
souboru však může obsahovat i znak nového řádku (!), a proto následující
idiom není 100% bezpečný.
find -options-for-find | while read filename; do
do_some_complicated_things_with "$filename"
done
Pokud chcete být opravdu bezpeční, použijte -print0
a IFS= read -r -d $'\0' filename
, neboť tak se použije jediný bezpečný oddělovač – \0
(vzpomeňte si, co jste měli v kurzu Arduina o řetězcích C – a jak se
ukončují –). Alternativně můžete výstup find -print0
přesměrovat do
xargs --null
.
Pokud však pracujete s vlastními soubory nebo je vzor bezpečný, výše uvedená
smyčka je v pořádku (jen nezapomeňte, že adresáře jsou také soubory a mohou
ve svém názvu obsahovat \n
).
Shell také umožňuje exportovat funkci a volat ji zevnitř xargs
. Vypadá to
ošklivě, ale je to bezpečný přístup, pokud chcete provádět složité operace
nad nalezenými soubory.
my_function() {
echo ""
echo "\$0 = $0"
echo "\$@ =" "$@"
}
export -f my_function
find . -print0 | xargs -0 -n 1 bash -c 'my_function "$@"' arg_zero arg_one
Připomeňme, že funkce lze definovat přímo v shellu a výše uvedené lze skutečně vytvořit interaktivně, aniž by se cokoliv muselo ukládat jako skript.
Cvičení na find
a xargs
Kontejnery
Kontejnery jsou dalším způsobem izolace. Zatím jsme viděli izolování projektu sandboxingem a mnoho z vás si vyzkoušelo virtualizovanou instalaci Linuxu.
Kontejnery jsou někde na pomezí.
Nabízejí izolované prostředí, které se v podstatě chová jako virtualizovaný
stroj.
Z implementačního hlediska jsou blíže virtuálním prostředím, protože procesy
uvnitř
kontejneru jsou viditelné z hostitele.
Můžeme si to představit tak, že kontejner dostane jeden adresář (který
obsahuje obvyklé podadresáře
jako /dev
, /proc
nebo /home
) a z něj nemůže uniknout.
Kvůli tomu mohou kontejnery spouštět jen aplikace napsané pro tentýž operační systém (na rozdíl od plnohodnotného virtuálního stroje).
Protože jsou kontejnery od hostitelského systému odděleny, jsou v mnoha situacích velmi užitečné. Pochopitelně je tu i možnost použít plně virtualizovaný stroj (např. VirtualBox nebo QEMU), ale kontejnery jsou lehčí a mají menší režii (např. rychlejší start).
Oddělení od hostitelského systému je poměrně velké: bez dodatečné konfigurace se kontejner nemůže dostat na hostitelův systém souborů a nemůže poslouchat na žádných portech (pro příchozí spojení). Může ale začít odchozí komunikaci (např. stahovat balíčky, které je potřeba nainstalovat). Kontejner může být též omezen množstvím paměti RAM, kterou může použít. Ve výchozím nastavení jsou procesy uvnitř kontejneru plánovány jako běžné procesy (tj. mají stejnou prioritu), je ale možné omezit i jejich přístup k CPU (čili je zpomalit jako úlohy s nízkou prioritou).
Typickým příkladem je potřeba spustit izolovaný server, který potřebujete pro vývoj. Zde si můžete představit třeba databázový nebo webový server. Určitě můžete takový server nainstalovat do systému (vzpomeňte si na cvičení 10). Tam ale není od systému nijak oddělený a odinstalace také není úplně přímočará. Vzpomeňte si, jak fungují virtuální prostředí: odstraněním jednoho adresáře odstraníme kompletně celé prostředí.
Podobně, odstranění kontejneru je jednoduchá a rychlá operace a nový můžete nastartovat během pár vteřin.
Používání kontejneru vám také umožní přesně určit, jak bude vše vypadat:
které procesy se spustí, na kterém portu bude poslouchat atd.
Tuto konfiguraci pak můžeme lehce kodifikovat (podobně jako
requirements.txt
) a díky tomu snadno zopakovat na jiném stroji.
Obrazy kontejnerů jsou také často používány, když potřebujete distribuovat složitou aplikaci, která vyžaduje běh několika služeb. Namísto poskytnutí detailního manuálu nebo obrazu disku pro VirtualBox můžete dodat kontejner, který již bude připraven ke spuštění. Uživatel pak spustí celý kontejner a ten si už sám uvnitř vyřeší zbytek a navenek zpřístupní cílovou službu. Například celý GitLab lze stáhnout a používat v kontejneru.
Docker a Podman
V tomto cvičení se podíváme na základy Linuxových kontejnerů postavených nad
Dockerem a Podmanem.
Obě implementace jsou v podstatě stejné.
Jejich hlavní příkazy (docker
a podman
) podporují úplně stejné argumenty
a mají skoro vždy úplně stejnou sémantiku.
Hlavním rozdílem je, že Docker je trochu starší (ale je stále ještě vyvíjen) a byl zamýšlen pro systémové kontejnery (např. pokud byste si chtěli pustit vlastní GitLab). Podman je o něco mladší a využívá nové vlastnosti Linuxového jádra, které mu dovolují spouštět kontejnery bez práv superuživatele (což je pořád ještě poměrně nová funkce Linuxu). Navíc, Podman se lépe integruje do zbytku systému.
Z tohoto pohledu je Podman perfektní volbou pro vývojáře. Potřebujete databázový server? Použijte Podman s tím správným kontejnerem a spusťte ho. Vaše databáze je připravená k použití. To vše bez potřeby práv superuživatele (často se setkáte s označením rootless mode).
Na druhou stranu, pokud máte o něco starší verzi Linuxu nebo kontejner vyžaduje specifické vlastnosti Dockeru, Docker může být lepší volbou.
Terminologie …
V tomto cvičení jsou důležité dva koncepty. Obraz (image) a kontejner (container). Jsou trochu podobné třídě a objektu (instanci) nebo programu a běžícímu procesu.
Obraz je něco jako pevný disk pro izolované prostředí. Obsahuje všechny potřebné soubory: spustitelné i datové soubory.
Abychom ho spustili, vytvoříme kontejner. Kontejner je spuštěn se stejným stavem jako obraz, ale má i běžící procesy, které mohou měnit jeho stav. Pokud o to explicitně nepožádáme, změny provedené kontejnerem nejsou uloženy zpátky na obraz: místo toho je kontejner spuštěn s kopií obrazu a mění tuto kopii.
Procesy v kontejneru jsou izolovány od okolí (hostitele) a kontejner nevidí procesy hostitele.
Na druhou stranu, procesy v kontejneru jsou viditelné na hostiteli. Kořenovému adresáři kontejneru odpovídá nějaký podadresář na hostiteli. ID uživatelů v kontejneru jsou přeložena na uživatelská ID hostitele. To samé platí o skupinách.
Docker/Podman obvykle spouští své procesy s privilegii kontejnerového
uživatele root
, který se navenek – v hostiteli – tváří jako obyčejný
uživatel (obvykle s velmi vysokým UID).
Distribuce a Alpine
Obrazy můžou být vytvořeny nad různými distribucemi. Díky tomu jsou kontejnery snadnou cestou, jak vyzkoušet váš program v různých distribucích bez nutnosti instalovat triple- (nebo více-) boot nebo se starat o několik virtuálních strojů.
Brzy uvidíte, že mnoho kontejnerů je postaveno nad distribucí Alpine Linux. To je minimalisticky navržená distribuce (velikostí i složitostí) – má okolo 6 MB a nemá žádnou složitou konfiguraci.
Alpine používá Apk (Alpine package manager) pro správu balíčků. Například následující příkaz nainstaluje curl (který není nainstalován ve výchozím stavu):
apk add curl
Nastavení Dockeru/Podmanu
Nainstalujte Docker nebo Podman.
Následující příkaz by vám měl pomoci rozhodnout, který z nich vlastně potřebujete.
grep cgroup /proc/filesystems
Pokud uvidíte jen následující řádku, váš kernel nezná cgroups v2, které potřebuje Podman.
nodev cgroup
Pokud ale uvidíte následující, máte cgroups v2 povolené a měli byste používat Podman.
nodev cgroup
nodev cgroup2
Pak pokračujte s instalací.
Nejnovější verze Fedory už přešly na cgroup v2 a instalace Podmanu je
jedinou možností.
Takže ho instalujte pomocí sudo dnf install podman
.
Všechny následující příklady budou používat příkaz podman
.
Pokud vaše distribuce Podman nepodporuje, nahraďte jej příkazem sudo docker
.
Podman: nastavení /etc/subuid
a /etc/subgid
Jak jsme vysvětlovali výše, Podman potřebuje určitý rozsah volných ID uživatelů a skupin, aby do nich mohl namapovat UID a GID z kontejneru.
Superuživatel může bloky UIDů/GIDů přidělovat obyčejným uživatelům, kteří je
pak mohou takto využívat.
Říká se tomu sub-UID/sub-GID a jejich nastavení je zaznamenáno v souborech
/etc/subuid
a /etc/subgid
.
Nejdříve si, prosím, zkontrolujte, zda-li váš /etc/subuid
neobsahuje něco
jako intro:100000:65536
. Pokud ano, máte už vše připravené a můžete zbytek
této sekce přeskočit.
Jinak se ujistěte, že tento soubor existuje, a vytvořte nové přiřazení
pomocí usermod
:
sudo touch /etc/subuid /etc/subgid
sudo usermod --add-subuids 100000-165536 --add-subgids 100000-165536 YOUR_LOGIN
Systémová (balíčková) aktualizace může občas Podman z různých důvodů rozbít.
Pokud se vám to stane, zkuste vždy nejdříve spustit podman system migrate
,
který obvykle vyřeší většinu
chyb spojených s přechodem na novější verzi.
Docker: spuštění služby
U Dockeru je potřeba se ujistit, že běží dockerd
. Obvykle by měl stačit
následující příkaz:
sudo package-manager-of-your-distribution install docker
sudo systemctl enable docker
sudo systemctl start docker
Základní test funkčnosti
Spusťte podman info
, čímž získáte základní informace o
systému. Pravděpodobně uvidíte něco jako:
host:
arch: amd64
...
cgroupManager: systemd
cgroupVersion: v2
conmon:
...
...
idMappings:
gidmap:
- container_id: 0
host_id: 1000
size: 1
- container_id: 1
host_id: 100000
size: 65536
uidmap:
- container_id: 0
host_id: 1000
size: 1
- container_id: 1
host_id: 100000
size: 65536
...
os: linux
...
store:
graphRoot: $HOME/.local/share/containers/storage
...
runRoot: /run/user/1000/containers
volumePath: $HOME/.local/share/containers/storage/volumes
version:
APIVersion: 3.0.0
...
Až budete ladit problémy s Podmanem, vždy vložte tuto informaci (bez dalších úprav)
do popisu Issue (pochopitelně, vkládejte text do ```
, nikoliv jako screenshot!).
Abyste ověřili, že můžete kontejnery spouštět, zkuste následující příkaz:
podman run --rm docker.io/library/alpine:latest cat /etc/os-release
Pokud uvidíte něco jako následující výpis, vše je připraveno. Jinak klidně otevřte Issue na Fóru a pokusíme se to nějak vyřešit (nezapomeňte říct, kterou distribuci používáte).
Trying to pull docker.io/library/alpine:latest...
Getting image source signatures
Copying blob f56be85fc22e done
Copying config 9ed4aefc74 done
Writing manifest to image destination
Storing signatures
NAME="Alpine Linux"
ID=alpine
VERSION_ID=3.17.3
PRETTY_NAME="Alpine Linux v3.17"
HOME_URL="https://alpinelinux.org/"
BUG_REPORT_URL="https://gitlab.alpinelinux.org/alpine/aports/-/issues"
První polovina výstupu se týká stažení obrazu. Teprve druhá polovina odpovídá výstupu příkazu. Neváhejte a spusťte výše uvedený příkaz ještě jednou (protože obraz je již stažen), abyste získali následující výstup:
NAME="Alpine Linux"
ID=alpine
VERSION_ID=3.17.3
PRETTY_NAME="Alpine Linux v3.17"
HOME_URL="https://alpinelinux.org/"
BUG_REPORT_URL="https://gitlab.alpinelinux.org/alpine/aports/-/issues"
Podman je částečně k dispozici i na počítačích v IMPAKTu a jeho instalace (i když s určitými omezeními) by měla být pro naše účely dostatečná.
Je však mnohem pohodlnější používat vlastní počítač.
podman
na linux.ms.mff.cuni.cz
, vždy odstraňte
nepoužívané obrazy. Zatímco systém má dostatek místa pro experimentování,
obrazy mohou snadno zaplnit celý disk. Až je nebudete potřebovat, použijte k
jejich odstranění příkazy podman images
a podman rmi IMAGE_ID
(další
podrobnosti viz níže).
Příprava na cvičení
Před zahájením dalších pokusů s Podmanem se ujistěte, že máte aktuální kopii repozitáře examples.
Budeme používat podadresář 12/
.
/tmp
, protože -v
by nefungovalo se soubory na svazku
souborového systému AFS.
Spuštění prvního kontejneru
První spuštění bude trochu komplexnější, abyste si mohli udělat představu o tom, co je možné. Podrobnosti si vysvětlíme v následujících kapitolách.
Předpokládejme, že se nacházíte v adresáři 12
v repozitáři
examples.
Následující příkaz spustí webový server Nginx.
podman run --rm --publish 8080:80/tcp -v ./web:/usr/share/nginx/html:ro docker.io/library/nginx:1.20.0
Zobrazí se podobný výstup jako v následujícím případě.
Trying to pull docker.io/library/nginx:1.20.0...
Getting image source signatures
Copying blob 525e372d6dee done
Copying blob 69692152171a done
Copying blob b141b026b9ce done
Copying blob 8d70dc384fb3 done
Copying blob 965615a5cec8 done
Copying blob 6e60219fdb98 done
Copying config 7ab27dbbfb done
Writing manifest to image destination
Storing signatures
/docker-entrypoint.sh: /docker-entrypoint.d/ is not empty, will attempt to perform configuration
/docker-entrypoint.sh: Looking for shell scripts in /docker-entrypoint.d/
/docker-entrypoint.sh: Launching /docker-entrypoint.d/10-listen-on-ipv6-by-default.sh
10-listen-on-ipv6-by-default.sh: info: Getting the checksum of /etc/nginx/conf.d/default.conf
10-listen-on-ipv6-by-default.sh: info: Enabled listen on IPv6 in /etc/nginx/conf.d/default.conf
/docker-entrypoint.sh: Launching /docker-entrypoint.d/20-envsubst-on-templates.sh
/docker-entrypoint.sh: Launching /docker-entrypoint.d/30-tune-worker-processes.sh
/docker-entrypoint.sh: Configuration complete; ready for start up
2021/05/18 13:15:55 [notice] 1#1: using the "epoll" event method
2021/05/18 13:15:55 [notice] 1#1: nginx/1.20.0
2021/05/18 13:15:55 [notice] 1#1: built by gcc 8.3.0 (Debian 8.3.0-6)
2021/05/18 13:15:55 [notice] 1#1: OS: Linux 5.10.16-arch1-1
2021/05/18 13:15:55 [notice] 1#1: getrlimit(RLIMIT_NOFILE): 524288:524288
2021/05/18 13:15:55 [notice] 1#1: start worker processes
2021/05/18 13:15:55 [notice] 1#1: start worker process 26
2021/05/18 13:15:55 [notice] 1#1: start worker process 27
2021/05/18 13:15:55 [notice] 1#1: start worker process 28
2021/05/18 13:15:55 [notice] 1#1: start worker process 29
Otevřete ve vašem prohlížeči http://localhost:8080/. Měla by se vám v něm zobrazit stránka NSWI177 Test Page.
Pokud se místo toho zobrazí 403 Forbidden, přidejte na konec argumentu
volby -v
znaky ,Z
. Příkaz by tedy obsahoval -v ./web:/usr/share/nginx/html:ro,Z
. To je potřeba (a obecně je to dobrý
postup), pokud pracujete na počítači s povoleným SELinuxem v režimu
vynucování (výchozí instalace Fedory, ale ne na USB discích od nás).
Podman ukončete stiskem Ctrl-C
.
Všimněte si, že běžící webový server Nginx vypisoval svůj log – tj. seznam zobrazených stránek – na stdout.
Nyní otevřete v prohlížeči stránku web/index.html
. Opět se zobrazí
NSWI177 Test Page, ale adresa URL bude ukazovat na váš místní souborový
systém (tj. file:///home/.../examples/14/web/index.html
).
Výše uvedený příklad ilustruje tři důležité funkce, které jsou u kontejnerů k dispozici:
- Webový server v kontejneru není třeba konfigurovat ani instalovat v rámci celého systému.
- Kontejner může naslouchat na portech hostitelského systému a předávat síťovou komunikaci dovnitř kontejneru.
- Kontejner může přistupovat k souborům hostitele a používat je.
Všechny tyto funkce jsou velmi dobré pro vývoj, testování i distribuci softwaru.
Stahování a kontrola obrazů
Při spuštění kontejneru je nejprve třeba získat jeho obraz. Ačkoli Podman
umí stáhnout obraz jako součást podpříkazu run
, někdy je užitečné získat
jej v samostatném kroku.
Příkaz podman images
vypíše seznam obrazů, které se nacházejí ve vašem
systému. Výstup může vypadat takto.
REPOSITORY TAG IMAGE ID CREATED SIZE
docker.io/library/alpine latest 9ed4aefc74f6 2 weeks ago 7.34 MB
docker.io/library/nginx 1.20.0 7ab27dbbfbdf 6 days ago 137 MB
docker.io/library/fedora 34 8d788d646766 2 weeks ago 187 MB
...
Sloupec repository odkazuje na on-line repozitář, ze kterého jsme obraz získali. Sloupec tag je v podstatě verze. Sloupec image id je jedinečná identifikace obrazu, obvykle se odvozuje z kryptografického hashe jeho obsahu. Zbývající sloupce jsou samovysvětlující.
Když spustíte podman pull IMAGE:TAG
, Podman stáhne obraz, aniž by spustil
kontejner. Pokud jako značku (TAG) použijete latest
, bude stažena
nejnovější dostupná verze.
Stáhněte docker.io/library/python:3-alpine
a zkontrolujte, zda se poté
objevil v podman images
.
Kratší názvy obrazů
Pokud do souboru /etc/containers/registries.conf.d/unqualified.conf
vložíte následující obsah, nebudete muset před každý název obrazu zadávat
docker.io/
. Říká se tomu unqualified search (nekvalifikované vyhledávání)
a pro každý název obrazu se vyzkouší jako první.
unqualified-search-registries = ["docker.io"]
Různé společnosti mohou mít svá vlastní úložiště. Pokud si přejete v případech, když není uveden plně kvalifikovaný název obrazu, vyzkoušet více různých úložišť , můžete jich zde nastavit několik.
Repozitář obrazů
Pokud vás zajímá, odkud obrazy pocházejí, podívejte se na https://hub.docker.com/. Každý tam může nahrát své obrazy, které pak mohou ostatní používat.
Podobně jako v případě Python package index, i zde
můžete narazit na škodlivé obrazy. Kontejnery alespoň běží izolovaně, takže
šance na špatné chování jsou trochu omezené (ve srovnání s pip install
,
který spouštíte v kontextu běžného uživatele).
Obrazy ze skupiny library
jsou oficiální obrazy schválené samotným
nástrojem Docker, a proto jsou relativně důvěryhodné.
Průběžný příklad
Po stažení obrazu z něj můžeme vytvořit kontejner.
Začneme obrazem Alpine, protože je velmi malý, a tudíž velmi rychlý.
podman run --interactive --tty alpine:latest /bin/sh
Pokud vše proběhlo v pořádku, měli byste vidět interaktivní prompt / #
a
při kontrole /etc/os-release
byste měli vidět následující text (čísla
verzí se mohou lišit):
NAME="Alpine Linux"
ID=alpine
VERSION_ID=3.13.5
PRETTY_NAME="Alpine Linux v3.13"
HOME_URL="https://alpinelinux.org/"
BUG_REPORT_URL="https://bugs.alpinelinux.org/"
Podpříkaz run
spustí kontejner ze zadaného obrazu. Pomocí --interactive
a --tty
(které se často spojují do jediného -it
) určíme, že chceme ke
kontejneru připojit terminál, abychom jej mohli používat interaktivně.
Poslední částí příkazu je program, který se má spustit.
Uvnitř kontejneru můžeme provádět libovolné příkazy. Jsme bezpečně uzavřeni a změny neovlivní hostitelský systém.
Nainstalujte curl
a zkontrolujte, zda máte funkční
přístup k síti.
Solution.
Otevřete druhý terminál, abychom mohli zkontrolovat, jak kontejner vypadá zvenčí.
Uvnitř kontejneru spusťte sleep 111
a v druhém terminálu (který je spuštěn
v hostiteli) spusťte ps -ef --forest
. Zobrazí se následující řádky:
student 1477313 1 0 16:29 ? 00:00:00 /usr/bin/conmon ...
student 1477316 1477313 0 16:29 pts/0 00:00:00 \_ /bin/sh
student 1477370 1477316 0 16:33 pts/0 00:00:00 \_ sleep 111
To potvrzuje, že procesy běžící uvnitř kontejneru jsou viditelné zvenčí.
Spusťte ps -ef
uvnitř kontejneru (nebo se podívejte do /proc
).
Co uvidíte? Je tam něco překvapivého?
Solution.
Spusťte také podman ps
. Ten vypíše seznam spuštěných kontejnerů.
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
643b5e7cea06 docker.io/library/alpine:latest /bin/sh 4 minutes ago Up 4 minutes ago practical_bohr
ID kontejneru je opět jedinečná identifikace, ostatní sloupce jsou samovysvětlující. Všimněte si, že vzhledem k tomu, že jsme nezadali název, Podman kontejneru přiřadil náhodný název.
Pokud relaci ukončíte uvnitř kontejneru (exit
nebo Ctrl-D
), vrátíte se
do hostitelského terminálu.
Znovu spusťte podman ps
. Výstup je prázdný: kontejner neběží. Pokud
přidáte --all
, uvidíte, že se STATUS
změnil.
Exited (130) 1 second ago
Všimněte si, že kdybychom znovu spustili podman run ...
, spustili bychom
nový kontejner. Vyzkoušejte si to nyní.
Životní cyklus kontejneru popíšeme později, pokud chcete kontejner odstranit
nyní, spusťte podman rm NAME
. Jako NAME
použijte náhodně přidělený
název nebo CONTAINER ID
.
Jednorázová spuštění
Příkazu podman run
můžete předat libovolný příkaz, který se má provést.
Pokud víte, že kontejner ihned poté odstraníte, můžete přidat --rm
, abyste
Podmanu řekli, že má kontejner po dokončení příkazu automaticky odstranit.
podman run --rm alpine:latest cat /etc/os-release
Pokud chcete předat složitější příkaz, je lepší použít sh -c
.
Změňte výše uvedený příkaz tak, že nejprve provedeme cd
do etc
a poté zavoláme cat os-release
.
Proč by nefungoval příkaz podman run --rm alpine:latest cd /etc && cat os-release
?
Solution.
Správa životního cyklu kontejneru
Kontejnery jsou vlastně dost podobné službám, o kterých jsme hovořili ve Cvičení 10.
Spuštění kontejneru
Po ukončení interaktivní relace byl kontejner ukončen. Pro jeho opětovné
spuštění můžeme zavolat podman start CONTAINER
.
Každý kontejner má tzv. vstupní bod, který se spustí při jeho spuštění. U kontejneru typu služba (např. s webovým serverem) by se služba spouštěla znovu.
V našem příkladu s Alpine je vstupním bodem /bin/sh
(shell), takže se nic
zajímavého nestane.
Zkontrolujte, že je kontejner spuštěn, pomocí podman ps
.
Připojení ke spuštěnému kontejneru
Když je kontejner spuštěn, můžeme se k němu připojit. podman attach
v
podstatě připojí stdout vstupního bodu k vašemu terminálu. S naším
kontejnerem Alpine můžeme opět spustit příkaz uvnitř kontejneru.
Můžeme také zavolat podman exec -it CONTAINER CMD
, který se připojí ke
spuštěnému kontejneru v novém terminálu (jako nová karta). V našem případě
bude fungovat spuštění následujícího příkazu (použijte název vašeho
kontejneru).
podman exec -it practical_bohr /bin/sh
Spusťte znovu ps -ef
uvnitř kontejneru.
Které procesy vidíte?
Solution.
Ukončení exec
nutého shellu nás vrátí zpět k hostiteli. Ukončení
attach
nutého shellu ukončí celý kontejner.
Kontejnery na pozadí (s názvy)
U kontejnerů typu služba (např. nginx
, který poskytuje webový server) je
často chceme spouštět v režimu démona – na pozadí.
To je možné pomocí volby --detach
příkazu run
.
Do příkazu přidáme také jméno webserver
, abychom na kontejner mohli snadno
odkazovat.
podman run --detach --name webserver --publish 8080:80/tcp -v ./web:/usr/share/nginx/html:ro nginx:1.20.0
Volby -v
a --publish
si vysvětlíme později.
Tento příkaz spustí kontejner a skončí. Webový server běží na pozadí. Zkontrolujte, zda máte opět přístup k http://localhost:8080/ v prohlížeči.
Takový kontejner můžete zastavit příkazem podman stop webserver
. Je to
něco podobného jako systemctl stop ...
. Není to náhoda.
Zkontrolujte, že po zastavení webového serveru http://localhost:8080/ již nefunguje.
Opětovné spuštění kontejneru je možné pomocí příkazu podman start webserver
.
start
, stop
a standardní výstup
Všimněte si, že jak start
, tak stop
vypisují na standardní výstup jméno
kontejneru, který byl spuštěn (zastaven). To je užitečné při provádění ve
skriptech; při interaktivním použití můžeme tento výstup jednoduše
ignorovat.
Úklidové akce
Jakmile s kontejnerem skončíme, můžeme jej odstranit (nejprve jej však
musíme zastavit
).
Spuštěním následujícího příkazu by byl kontejner webserver
zcela
odstraněn.
podman rm webserver
Pomocí podpříkazu rmi
můžete také odstranit stažené obrazy.
Například pro odstranění nginx:1.20.0
můžete spustit následující příkaz.
podman rmi nginx:1.20.0
Všimněte si, že Podman odmítne obraz odstranit, pokud jej používá existující kontejner. Připomeňme, že obrazy jsou naskládané na sebe, a proto Podman nemůže odstranit spodní vrstvy.
Omezení izolace
Kontejner je ve výchozím nastavení izolovaný svět. Pokud k němu chcete
přistupovat zvenčí, musíte se do něj exec
nout (pro práci v terminálu) nebo
zveřejnit jeho služby navenek.
Přesměrování portů (tzv. publikování portů)
U kontejnerů serverového typu (např. Nginx, který jsme použili výše) to
znamená vystavení některých portů hostitelskému počítači. To se provádí
pomocí argumentu --publish
, ve kterém určíte, který port na hostiteli
(např. 8080
) má být přesměrován do kontejneru: na který port a který
protokol (např. 80
a tcp
).
Argument --publish 8080:80/tcp
tedy znamená, že očekáváme, že kontejner
sám nabízí službu na svém portu 80
a chceme tento port (kontejneru)
zpřístupnit jako 8080
.
Kontejner nginx
můžeme spustit i bez --publish
, ale to neznamená, že to
má velký smysl. Proč?
Solution.
Připojování svazků
Další možností, jak narušit izolaci kontejneru, je připojit do kontejneru
určitý adresář. Existuje několik možností, jak to provést. My si ukážeme
parametr --volume
(nebo -v
).
/tmp
.
Parametr -v
přijímá tři argumenty (opět oddělené dvojtečkou): zdrojový
adresář na hostiteli, mapování uvnitř kontejneru a volby (options).
Náš příklad ./web:/usr/share/nginx/html:ro
tedy určuje, že místní
(hostitelský) adresář web
bude viditelný pod /usr/share/nginx/html
uvnitř kontejneru v režimu pouze pro čtení. Je to velmi podobné běžnému
připojování (mountování), které již znáte.
Pokud zadáte rw
místo ro
, můžete uvnitř kontejneru upravovat hostitelské
soubory.
Připojení svazku je užitečné pro jakýkoli kontejner typu služba. Typickým příkladem je databázový server. Spustíte kontejner a přidělíte mu připojený svazek. Do tohoto svazku (adresáře) bude ukládat vlastní databázi (datové soubory). Po ukončení kontejneru jsou tedy vaše data ve skutečnosti trvalá, protože byla uložena mimo kontejner.
To má obrovskou výhodu při testování aktualizací služeb. Zastavíte kontejner, vytvoříte zálohu datového adresáře a spustíte nový kontejner (s novější verzí) nad stejným datovým adresářem. Pokud vše funguje správně, můžete pokračovat. V opačném případě můžete nový kontejner zastavit, obnovit data ze zálohy a vrátit se ke staré verzi.
Velmi jednoduché a účinné.
Příklady práce s kontejnery
Proč máme všechny ty systemctl
, dnf
, podman
, pip
, …
V tuto chvíli by mohlo dojít k určitým nejasnostem, proč existuje tolik konceptů, které se v podstatě zabývají stejnou problematikou.
- K instalaci softwaru máme správce balíčků (
dnf install
). Některý software však můžeme instalovat také prostřednictvím správců specifických pro daný jazyk (pip install
). - Webový server lze spustit pomocí
systemctl start
nebo vytvořením kontejneru. - Máme virtuální prostředí pro vývoj softwaru, ale také kontejnery a plnohodnotné virtuální stroje.
- …
Pravdou je, že některé koncepty a nástroje jsou důsledkem historického vývoje, zatímco jiné řeší některé problémy z různých úhlů.
Klidně se k tomuto textu vraťte později, např. až trochu vstřebáte téma kontejnerů.
Úlohy před cvičením (deadline: začátek vašeho cvičení, týden 1. května – 5. května)
Následující úlohy musí být vyřešeny a odevzdány před příchodem na vaše cvičení. Pokud máte cvičení ve středu v 10.40, soubory musí být nahrány do vašeho projektu (repozitáře) na GitLabu nejpozději ve středu v 10.39.
Pro virtuální cvičení je deadline úterý 9:00 (každý týden, vždy ráno, bez ohledu na možné státní svátky apod.).
Všechny úlohy (pokud není explicitně uvedeno jinak) musí být odevzdány přes váš repozitář na úkoly. Pro většinu úloh existují automatické testy, které vám mohou pomoci zkontrolovat úplnost vašeho řešení (zde je popsáno jak číst jejich výsledky).
12/command.txt
(70 bodů, skupina admin
)
Obraz registry.gitlab.com/mffd3s/nswi177/labs-2023-command:latest
obsahuje
příkaz nswi177-task-command
(tj. tento image má v sobě spustitelný soubor
nazvaný nswi177-task-command
).
Spusťte tento příkaz s vaším uživatelským jménem na MFF GitLab a vložte jeho
výstup do souboru 12/command.txt
(bude to opět hexadecimální řetězec).
12/python.txt
(30 bodů, skupina devel
)
Nainstalujte následující balíček
Pythonu do
kontejneru (vzpomeňte si, že na instalování balíčku můžete přímo použít
pip
).
Připomeňte si, proč zde nemá smysl používat virtuální prostředí.
Po instalaci spusťte nově nainstalovaný program nswi177-lab12
a uložte
jeho výstup do souboru 12/python.txt
.
Pro instalaci doporučujeme použít obraz fedora:37
nebo Alpine. Možná ale
budete muset nejdříve nainstalovat python3
.
Úlohy po cvičení (deadline: 21. května)
Očekáváme, že následující úlohy vyřešíte po cvičení, tj. poté, co budete mít zpětnou vazbu k vašim řešením úloh před cvičením.
Všechny úlohy (pokud není explicitně uvedeno jinak) musí být odevzdány přes váš repozitář na úkoly. Pro většinu úloh existují automatické testy, které vám mohou pomoci zkontrolovat úplnost vašeho řešení (zde je popsáno jak číst jejich výsledky).
12/test-in-alpine.txt
(50 bodů, skupina git
)
Účelem této úlohy je ukázat, jak lze kontejnery snadno použít pro kontrolu toho, zda je váš projekt v rozumném stavu. I když používáte virtuální prostředí apod. je důležité ověřit, zda lze váš projekt nainstalovat do čistého prostředí.
Vaším úkolem je napsat do 12/test-in-alpine.txt
příkazy, které naklonují
fscat a
spustí jeho testy.
My pak spustíme váš skript pomocí níže uvedeného příkazu a zkontrolujeme, že byly testy spuštěny.
podman run --rm alpine:3.17 /bin/sh -c "$( cat 12/test-in-alpine.txt )"
Ujistěte se prosím, že nepřesměrováváte výstup BATS testů a že spouštíte
Pythononí testy s -v
, abychom ve výstupu viděli následující (...
je
místo, kde se mohou objevit další zprávy).
1..3
ok 1 Works with a tarball
ok 2 Failure on bad filesystem path
ok 3 Failure on bad filename path
...
tests/test_fscat.py::test_cat_from_tar PASSED [ 25%]
tests/test_fscat.py::test_raises_on_invalid_filesystem_path PASSED [ 50%]
tests/test_fscat.py::test_raises_on_invalid_filename_path PASSED [ 75%]
tests/test_fscat.py::test_raises_when_filename_is_directory PASSED [100%]
Také prosím zajistěte, aby váš skript obsahoval cestu k repozitáři v nezměněné podobě, abychom ji mohli nahradit naší cachí a urychlit si tak klonování.
https://gitlab.mff.cuni.cz/teaching/nswi177/2023/common/fscat.git
Budeme používat distribuci Alpine, takže se ujistěte, že soubor
12/test-in-alpine.txt
obsahuje také příkazy pro instalaci závislostí
(včetně py3-pip
).
Váš skript musí používat set -e
, aby byly detekovány chyby v testech
(konec při prvním selhaném příkazu).
Pro sledování toho, co se děje, je zcela v pořádku použít set -x
(doporučujeme použít tento přepínač jako první příkaz v souboru
12/test-in-alpine.txt
).
Důrazně doporučujeme, abyste tuto úlohu nejprve vyřešili interaktivně a poté
si pomocí příkazu history
prohlédli, jaké příkazy jste spustili, a z nich
sestavili výsledný skript.
Aktualizace: očekáváme, že Pythoní testy budou spuštěny jako pytest -v tests
(nebo nějak podobně).
12/webserver.txt
(50 bodů, skupina net
)
Původní úloha vyžadovalo (v určitých případech) nastavení zvláštních oprávnění pro SELinux na instalaci Fedory od nás, což je nad rámec tohoto předmětu.
Takže je úloha zredukována jen na vytvoření soubor 12/webserver.txt
ve
vašem repozitáři.
Omlouváme se za zmatek.
Učební výstupy
Učební výstupy podávají zhuštěný souhrn základních konceptů a dovedností, které byste měli umět vysvětlit a/nebo použít po každém cvičení. Také obsahují absolutní minimum, které je potřebné pro pochopení navazujících cvičení (a dalších předmětů).
Znalosti konceptů
Znalost konceptů znamená, že rozumíte významu a kontextu daného tématu a jste schopni témata zasadit do většího rámce. Takže, jste schopni …
-
vysvětlit, co je to kontejner
-
porovnat kontejner s virtuálním strojem a procesem
-
vysvětlit, kde se hodí izolace, kterou nabízí kontejnery
-
vysvětlit životní cyklus kontejneru
-
vysvětlit, proč použití virtuálních prostředí (nebo jiných typů sandboxů) není uvnitř kontejneru obvykle potřeba
-
vysvětlit rozdíl mezi běžícím kontejnerem a obrazem kontejneru
Praktické dovednosti
Praktické dovednosti se obvykle týkají použití daných programů pro vyřešení různých úloh. Takže, dokážete …
-
používat
xargs
-
použít
find
se základními predikáty (-name
,-type
) a akcemi (-exec
,-delete
) -
spustit v Podmanu interaktivní kontejner
-
spustit v Podmanu kontejner se službou
-
zpřístupnit (expose) porty kontejneru
-
připojit svazek dovnitř kontejneru
-
vymazat nepoužívané kontejnery a obrazy
Seznam změn na této stránce
-
2023-04-28: Přidány automatické testy a úlohy po cvičení.
-
2023-05-03: Aktualizace úloh po cvičení.
-
2023-05-09: Poznámka o spouštění pytest(ů).