GNU/Linux >> Znalost Linux >  >> Linux

Co se děje v zákulisí kontejneru Podman bez kořenů?

Vždy se vyplatí vědět, co se děje v zákulisí. Pojďme se podívat na to, co se děje pod kapotou bezkořenových kontejnerů Podman. Vysvětlíme si každou komponentu a poté rozebereme všechny příslušné kroky.

Příklad

V našem příkladu se pokusíme spustit kontejner, na kterém již běží Buildah, abychom vytvořili image kontejneru. Nejprve vytvoříme jednoduchý Dockerfile nazvaný Containerfile který stáhne obraz ubi8 a spustí příkaz, který vám řekne, že běžíte v kontejneru:

$ mkdir containers
$ cat > ~/Containerfile << _EOF
FROM ubi8
RUN echo “in buildah container”
_EOF

Dále spusťte kontejner pomocí následujícího příkazu Podman:

$ podman run --device /dev/fuse -v ~/Containerfile:/Containerfile:Z \
     -v ~/containers:/var/lib/containers:Z buildah buildah bud /

Tento příkaz přidá další zařízení /dev/fuse , který je nutný ke spuštění Buildah uvnitř kontejneru. Svazek připojujeme v Containerfile aby jej Buildah mohl najít a použít příznak SELinux :Z říct Podmanovi, aby to přeoznačil. Abychom zvládli skladování kontejnerů Buildah mimo kontejner, namontujeme také místní containers adresář, který jsem vytvořil výše. A nakonec spustíme příkaz Buildah.

Zde je skutečný výstup, který vidím při spuštění tohoto příkazu:

$ podman run -ti --device /dev/fuse -v ~/Containerfile:/Containerfile:Z -v ~/containers:/var/lib/containers:Z buildah/stable buildah bud /
Trying to pull docker.io/buildah/stable...
   denied: requested access to the resource is denied
Trying to pull registry.fedoraproject.org/buildah/stable...
   manifest unknown: manifest unknown
Trying to pull quay.io/buildah/stable...
Getting image source signatures
Copying blob 907e338ec93d done
Copying blob a3ed95caeb02 done
Copying blob a3ed95caeCob02 done
Copying blob a3ed95caeb02 skipped: already exists
Copying blob d318c91bf2a8 done
Copying blob e721a8015139 done
Copying blob a3ed95caeb02 done
Copying blob 8dd367492bc7 done
Writing manifest to image destination
Storing signatures
STEP 1: FROM ubi8
Getting image source signatures
Copying blob c65691897a4d done
Copying blob 641d7cc5cbc4 done
Copying config 11f9dba4d1 done
Writing manifest to image destination
Storing signatures
STEP 2: RUN echo "in buildah container"
in buildah container
STEP 3: COMMIT
Getting image source signatures
Copying blob 6866631b657e skipped: already exists
Copying blob 48905dae4010 skipped: already exists
Copying blob 5f70bf18a086 skipped: already exists
Copying config 9c54016647 done
Writing manifest to image destination
Storing signatures
9c5401664748e032b43b8674dba90e9b853d6b47b679d056cb2a1e3118f9dab7

Nyní se pojďme ponořit hluboko do toho, co se vlastně děje v rámci příkazu Podman.

Nastavení jmenných prostorů uživatele a připojení

Při nastavování uživatelských a připojovacích jmenných prostorů Podman nejprve zkontroluje, zda je již nakonfigurován uživatelský jmenný prostor. To se provádí sledováním, zda pro uživatele běží proces pozastavení. Role procesu pauzy je udržovat uživatelský jmenný prostor naživu, protože všechny kontejnery bez root musí být spuštěny ve stejném uživatelském jmenném prostoru. Pokud tomu tak není, některé věci (jako sdílení síťového jmenného prostoru z jiného kontejneru) by byly nemožné.

Uživatelský jmenný prostor je nutný, aby umožnil rootless připojit určité typy souborových systémů a přistupovat k více než jednomu UID a GID.

Pokud proces pozastavení existuje, pak je jeho uživatelský jmenný prostor připojen. Tato akce se provádí velmi brzy před spuštěním běhového prostředí Go, protože program s více vlákny nemůže změnit svůj uživatelský jmenný prostor. Pokud však proces pozastavení není existovat, pak Podman přečte /etc/subuid a /etc/subgid soubory, hledá uživatelské jméno nebo UID uživatele spouštějícího příkaz Podman. Jakmile Podman najde záznam, použije obsah i aktuální UID/GID uživatele k vygenerování uživatelského jmenného prostoru.

Pokud například uživatel běží jako UID 1000 a má položku USER:100000:65536 , Podman spustí aplikace setuid a setgid, /usr/bin/newuidmap a /usr/bin/newgidmap , pro konfiguraci uživatelského jmenného prostoru. Uživatelský jmenný prostor pak získá následující mapování:

0     3267      1
1     100000    65536

Všimněte si, že můžete zobrazit uživatelský jmenný prostor spuštěním:

$ podman unshare cat /proc/self/uid_map

Dále Podman vytvoří proces pozastavení, aby udržoval jmenný prostor naživu, takže všechny kontejnery mohou běžet ze stejného kontextu a vidět stejná připojení. Další proces Podman se přímo připojí k jmennému prostoru, aniž by jej bylo nutné nejprve vytvořit. Pokud však nelze uživatelský prostor vytvořit, Podman zkontroluje, zda lze příkaz stále spustit bez uživatelského jmenného prostoru. Některé příkazy jako podman version nepotřebuji jeden. V každém jiném případě příkaz bez uživatelského jmenného prostoru selže.

Poté Podman zpracuje možnosti příkazového řádku a ověří, že jsou správné. Můžete použít podman-help a podman run --help pro seznam dostupných možností a pro další popisy použijte manuálové stránky.

Nakonec Podman vytvoří jmenný prostor pro připojení k připojení úložiště kontejneru.

Vytažení obrázku

Při stahování obrázku Podman zkontroluje, zda je obrázek kontejneru buildah/stable existuje v místním kontejnerovém úložišti. Pokud ano, Podman nastaví síť (viz další část). Pokud však obrázek kontejneru neexistuje, Podman vytvoří seznam kandidátských obrázků k načtení pomocí vyhledávacích registrů definovaných v /etc/containers/registries.conf .

containers/image knihovna bude použita k vytažení těchto kandidátských obrázků jeden po druhém, v pořadí definovaném registries.conf . Bude použit první obrázek, který bude úspěšně načten.

  1. containers/image skript používá DNS k nalezení IP adresy registru.
  2. Tento skript TCP se připojuje k IP adrese prostřednictvím httpd port (80).
  3. container/image odešle požadavek HTTP na manifest /buildah/stable:latest obrázek kontejneru.
  4. Pokud skript nemůže najít obrázek, použije jako náhradu další registr a vrátí se ke kroku 1. Pokud však obrázek je nalezen, začne vytahovat každou vrstvu obrázku pomocí containers/image knihovna.

V tomto příkladu buildah/stable byl nalezen na quay.io/buildah/stable . containers/image skript zjistí, že v quay.io/buildah/stable je sedm vrstev a začne je všechny současně kopírovat z registru kontejnerů na hostitele. Jejich současné kopírování je efektivní.

Když je každá vrstva zkopírována do hostitele, Podman zavolá containers/storage knihovna. containers/storage skript znovu sestaví vrstvy v pořadí a pro každou vrstvu. Vytvoří překryvný přípojný bod v ~/.local/share/containers/storage nad předchozí vrstvou. Pokud neexistuje žádná předchozí vrstva, vytvoří se počáteční vrstva.

Poznámka: V rootless Podman ve skutečnosti používáme fuse-overlayfs spustitelný pro vytvoření vrstvy. Rootfull používá overlayfs jádra Řidič. V současnosti jádro neumožňuje uživatelům bez root připojovat překryvné souborové systémy, ale mohou připojovat souborové systémy FUSE.

Dále containers/storage rozbalí obsah vrstvy do nové úložné vrstvy. Vzhledem k tomu, že vrstvy jsou rozmazané, containers/storage přenese UID/GID souborů v tarballu do domovského adresáře. Všimněte si, že tento proces může selhat, pokud UID nebo GID zadané v souboru tar nebylo namapováno do prostoru jmen uživatelů. Viz Proč mi Podman bez kořenů nemůže stáhnout obrázek?

Vytvoření kontejneru

Nyní je čas, aby Podman vytvořil nový kontejner na základě obrázku. Aby toho dosáhl, Podman přidá kontejner do databáze a poté požádá containers/storage knihovny k vytvoření a připojení nového kontejneru v c/storage . Nová vrstva kontejneru funguje jako konečná vrstva pro čtení/zápis a je připojena k obrázku.

Nastavení sítě

Dále musíme nastavit síť. Aby toho dosáhl, Podman najde a spustí /usr/bin/slirp4netns k nastavení kontejnerové sítě. V Podmanu bez root nemůžeme vytvořit úplné, samostatné síťové propojení pro kontejnery, protože tato funkce není povolena uživatelům bez oprávnění root. V rootless Podman používáme slirp4netns pro konfiguraci hostitelské sítě a simulaci VPN pro kontejner.

Poznámka: V kořenových kontejnerech používá Podman ke konfiguraci mostu zásuvné moduly CNI.

Pokud uživatel zadal mapování portu jako -p 8080:80 , slirpnetns bude naslouchat v hostitelské síti na portu 8080 a umožní procesu kontejneru navázat se na port 80. slirp4netns příkaz vytvoří tap zařízení, které je vloženo do nového síťového jmenného prostoru, kde kontejner žije. Každý paket je načten zpět z slirp4netns a emuluje zásobník TCP/IP v uživatelském prostoru. Každé připojení mimo jmenný prostor kontejnerové sítě je převedeno na operaci soketu, kterou může provést neprivilegovaný uživatel ve jmenném prostoru hostitelské sítě.

Zpracování svazků

Aby mohl Podman manipulovat se svazky, čte celý zásobník kontejneru. Shromažďuje použité štítky SELinux a vytváří nový, nepoužitý štítek pro spuštění kontejneru pomocí opencontainers/selinux knihovna.

Protože uživatel zadal dva svazky, které se mají připojit do kontejneru, a požádal Podmana, aby obsah přeoznačil, Podman používá opencontainers/selinux pro rekurzivní použití štítku SELinux na zdrojové soubory/adresáře svazků. Podman poté použije opencontainers/runtime-tools Knihovna pro sestavení Open Containers Initiative (OCI) Runtime specifikace:

  1. Podman říká runtime-tools přidat do specifikace pevně zakódované výchozí hodnoty pro věci, jako jsou schopnosti, prostředí a jmenné prostory.
  2. Podman používá specifikaci OCI Image staženou z buildah/stable image k nastavení obsahu ve specifikaci, jako je pracovní adresář, vstupní bod a další proměnné prostředí.
  3. Podman převezme vstup od uživatele a použije runtime-tools knihovna k přidání polí do specifikací pro každý ze svazků a nastaví příkaz pro kontejner na buildah bud / .

V našem příkladu uživatel řekl Podmanovi, že chce použít zařízení /dev/fuse uvnitř nádoby. Na kořenovém kontejneru by Podman řekl běhovému prostředí OCI, aby vytvořilo /dev/fuse zařízení uvnitř kontejneru, ale uživatelé Podman bez root nemají povoleno vytvářet zařízení, takže Podman místo toho sdělí specifikaci OCI, aby svázala připojení /dev/fuse z hostitele do kontejneru.

Spuštění sledování kontejneru conmon

Jakmile jsou svazky zpracovány, Podman najde a spustí výchozí conmon pro kontejner /usr/bin/conmon . Tyto informace jsou načteny z /usr/share/containers/libpod.conf . Podman pak řekne conmon spustitelný pro použití běhového prostředí OCI také uvedeného v libpod.conf; obvykle /usr/bin/runc nebo /usr/bin/crun . Podman také říká conmon pro provedení podman container cleanup $CTRID pro kontejner, když kontejner opustí.

Conmon při monitorování kontejneru dělá následující:

  1. Conmon spustí běhové prostředí OCI, předá mu cestu k souboru se specifikací OCI a také ukáže na bod připojení vrstvy kontejneru v containers/storage . Tento bod připojení se nazývá rootfs.
  2. Conmon monitoruje kontejner, dokud se neopustí, a nahlásí jeho návratový kód.
  3. Conmon úchyty, když se uživatel připojí ke kontejneru, poskytují zásuvku pro streamování STDOUT a STDERR kontejneru.
  4. STDOUT a STDERR jsou také přihlášeny do souboru pro podman logs .

Po spuštění conmon , ale před spuštěním běhového prostředí OCI se Podman připojí k zásuvce „attach“, protože kontejner nebyl spuštěn s -d . Musíme to udělat před spuštěním kontejneru, jinak riskujeme ztrátu všeho, co kontejner zapsal do svých standardních streamů, než jsme připojili. Pokud tak učiníme před spuštěním kontejneru, získáme vše.

Spuštění běhového prostředí OCI

Běhové prostředí OCI načte soubor specifikace OCI a nakonfiguruje jádro pro spuštění kontejneru. To:

  1. Nastaví další jmenné prostory pro kontejner.
  2. Nakonfiguruje cgroups, pokud kontejner běží na cgroups V2 (cgroups V1 nepodporuje cgroups bez root).
  3. Nastaví štítek SELinux pro spuštění kontejneru.
  4. Čte seccomp.json soubor (výchozí /usr/share/containers/seccomp.json ) a nastaví pravidla seccomp.
  5. Nastaví proměnné prostředí.
  6. Bind připojí dva zadané svazky do cest v rootfs. Pokud cílová cesta v rootfs neexistuje, pak OCI runtime vytvoří cílový adresář.
  7. Přepne root na rootfs (udělá rootfs / uvnitř kontejneru).
  8. Rozdělí proces kontejneru.
  9. Spustí všechny programy háku OCI a předá jim rootfs a také PID 1 kontejneru.
  10. Provede příkaz zadaný uživatelem buildah bud / s PID kontejneru 1.
  11. Ukončí běhové prostředí OCI a ponechá conmon ke sledování kontejneru.

A nakonec conmon hlásí úspěch zpět Podmanovi.

Spuštění buildah primární proces kontejneru

Nyní k poslední skupině kroků. Začíná, když kontejner spustí počáteční proces Buildah. (Protože jsme v našem příkladu použili Buildah.) Buildah sdílí základní containers/image a containers/storage knihovny s Podmanem, takže se ve skutečnosti řídí většinou výše definovaných kroků, které Podman použil pro stahování obrázků a generování svých kontejnerů.

Podman se připojuje k conmon socket a pokračuje ve čtení/zápisu STDOUT do conmon . Všimněte si, že pokud uživatel zadal Podmanův -d příznak, Podman by odešel, ale conmon bude pokračovat ve sledování kontejneru.

Když proces kontejneru skončí, jádro odešle SIGCHLD do conmon proces. Na druhé straně conmon :

  1. Zaznamená výstupní kód kontejneru.
  2. Zavře soubor protokolu kontejneru.
  3. Zavře STDOUT/STDERR příkazu Podman.
  4. Provede podman container cleanup $CTRID příkaz.

Vyčištění kontejneru Podman poté odstraní slirp4netns síti a řekne containers/storage pro odpojení všech upevňovacích bodů kontejneru. Pokud uživatel zadal --rm pak je nádoba místo toho zcela odstraněna. Vrstva kontejneru je odstraněna z containers/storage a definice kontejneru je odstraněna z DB.

Protože původní příkaz Podman běžel v popředí, Podman čeká na conmon pro ukončení získá výstupní kód z kontejneru a poté skončí s výstupním kódem kontejneru.

Zabalení

Doufejme, že vám toto vysvětlení pomůže pochopit všechna ta kouzla k tomu dochází pod krytem při spuštění příkazu Podman bez root.

Nové kontejnery? Stáhněte si Containers Primer a naučte se základy linuxových kontejnerů.


Linux
  1. Co je uživatel Linuxu?

  2. Zákulisí s linuxovými kontejnery

  3. Proč nemůže bezkořenový Podman vytáhnout můj obrázek?

  1. Jaký je rozdíl mezi linuxovým kontejnerem a obrázkem?

  2. Jaké je PID v hostiteli procesu běžícího uvnitř kontejneru Docker?

  3. Jaký je uživatel debian-+?

  1. Spuštění rootless Podman jako uživatel bez root

  2. Používání souborů a zařízení v kontejnerech Podman rootless

  3. Jak ladit problémy se svazky namontovanými na kontejnerech bez kořenů