Kontejnery

Podman/Docker, namespaces, cgroups.

Kontejner je jen izolovaný proces běžící na jádře tvého hostitele — ne malý virtuální počítač. Nemá vlastní jádro, nebootuje, nemá „svůj hardware": sdílí jádro s tebou a od zbytku systému ho oddělují dvě funkce Linuxu — namespaces (co proces vidí) a cgroups (kolik smí spotřebovat). Až tohle pochopíš, přestaneš od kontejnerů čekat věci, které umí jen VM, a naopak oceníš, proč jsou tak rychlé a lehké.


Pár pojmů na úvod

  • Kontejner = izolovaný proces (nebo skupina procesů) na sdíleném jádře hostitele. Tváří se, že má systém sám pro sebe, ale pod ním je pořád tvoje jedno jádro Linux.
  • Image (obraz) = neměnná šablona, ze které kontejner vzniká — zabalený souborový systém + metadata (co spustit). Image se nemění; kontejner je jeho běžící instance.
  • Namespaces = funkce jádra, která procesu izoluje pohled — vidí jen svoje procesy, svou síť, své mounty. Nic „mimo" pro něj neexistuje.
  • cgroups (control groups) = funkce jádra, která omezuje zdroje — kolik CPU a RAM proces dostane. Namespaces řeší co vidí, cgroups řeší kolik sní.
  • Registry = sklad hotových image ke stažení (Docker Hub, quay.io). Odtud pullneš nginx, postgres, python… místo abys je stavěl od nuly.
  • Podman / Docker = nástroje, kterými kontejnery ovládáš. Docker je nejrozšířenější; Podman (rootless, bez démona) je běžný v ekosystému RHEL/Fedora. CLI mají skoro totožné.

Kontejner vs virtuální stroj — hlavní rozdíl

Nejčastější omyl začátečníka je, že kontejner = odlehčená VM. Není. Virtuální stroj emuluje celý počítač: má vlastní jádro, bootuje, běží v něm kompletní operační systém nad hypervizorem. Kontejner nic z toho nemá — je to obyčejný proces, který jádro hostitele jen „schová" do svých namespaces a omezí cgroupami.

Důsledky, které z toho plynou:

  • Rychlost a velikost — kontejner nabootuje za zlomek vteřiny a váží megabajty, ne gigabajty, protože nenese celý OS ani jádro.
  • Hustota — na jednom stroji rozjedeš stovky kontejnerů; VM jsou o řád těžší.
  • Slabší izolace — protože sdílejí jádro, díra v jádře může teoreticky prasknout hranici mezi kontejnery. VM je oddělená silněji (ale za cenu režie). K bezpečnosti se vrátíme níž.

Image vs kontejner

Tenhle rozdíl si zapamatuj natvrdo, protože se v něm pletou i pokročilí:

  • Image je neměnná šablona — jako instalační ISO nebo „recept". Leží v úložišti a nikdy se nemění.
  • Kontejner je běžící (nebo zastavená) instance vytvořená z image — jako uvařené jídlo z receptu. Z jednoho image spustíš klidně deset kontejnerů najednou, každý žije vlastní život.
podman pull nginx          # stáhne image nginx z registry
podman images              # vypíše lokálně dostupné image
podman run -d nginx        # vytvoří a spustí kontejner z image
podman ps                  # vypíše běžící kontejnery
podman ps -a               # i zastavené

💡 Když smažeš kontejner, image zůstane. Když smažeš image, musíš ho příště znovu pullnout nebo postavit. Jsou to dvě různé věci na dvou různých místech.


Podman vs Docker

Docker kontejnery zpopularizoval a jeho CLI se stalo standardem. Má ale dvě vlastnosti, které Podman řeší lépe:

  • Démon — Docker běží jako pořád zapnutá služba (dockerd) pod rootem; všechno jde přes ni. Podman žádného démona nemá — spustí kontejner jako obyčejný podproces a skončí.
  • Rootless — Podman umí běžet bez roota, pod tvým uživatelem. Kontejner tak nemá práva do celého systému, i kdyby se z něj něco „utrhlo".

Nejlepší zpráva: CLI je kompatibilní. Skoro všude, kde návod píše docker, můžeš dát podman:

podman run -d -p 8080:80 nginx    # to samé jako `docker run ...`
alias docker=podman                # a spousta návodů „prostě jede"

V příkladech používáme podman, ale s Dockerem je postup skoro identický (docker run … funguje stejně).


Prakticky: porty a data

Kontejner je izolovaný, takže ven není vidět nic, dokud to sám nepropojíš. Dvě věci potřebuješ skoro pořád — pustit dovnitř síť (port) a přežít data (volume).

Porty (-p) — namapuješ port hostitele na port uvnitř kontejneru, jinak se k službě nedostaneš:

podman run -d -p 8080:80 nginx
# host:8080  →  kontejner:80  → v prohlížeči otevřeš http://localhost:8080

Volumes (-v) — připojí složku hostitele (nebo pojmenovaný svazek) dovnitř kontejneru. Bez toho je souborový systém kontejneru dočasný:

podman run -d -v ./data:/var/lib/postgresql/data:Z postgres
# cokoli DB zapíše do /var/lib/... skončí ve složce ./data na hostiteli

⚠️ Když na systému běží SELinux (enforcing — typicky Fedora/RHEL), přidej k bind-mountu příznak :Z (privátní label jen pro tenhle kontejner) nebo :z (sdílený mezi kontejnery). Bez něj kontejner do složky nesmí a spadne na „permission denied", i když práva (rwx) vypadají správně.

🚩 Souborový systém kontejneru žije jen po dobu jeho existence. Když kontejner smažeš, jeho data zmizí s ním. Cokoli má přežít (databáze, uploady, logy), MUSÍ jít do volume. Tohle je asi nejčastější bolestivá lekce začátečníků — viz Failure modes.


Containerfile — vlastní image z receptu

Když ti hotový image nestačí, napíšeš si vlastní. Recept se jmenuje Containerfile (u Dockeru Dockerfile, obsah je stejný) — je to textový seznam kroků, jak image postavit:

FROM node:22            # z čeho vyjít (základní image)
WORKDIR /app            # pracovní složka uvnitř
COPY . .                # zkopíruj kód z hostitele do image
RUN npm ci              # spusť příkaz při stavbě
CMD ["node", "server.js"]   # co spustit, když kontejner poběží
podman build -t moje-app .   # postaví image podle Containerfile v aktuální složce
podman run -p 3000:3000 moje-app

Kouzlo je v tom, že celé prostředí je popsané v jednom souboru — kdokoli s tímhle receptem postaví bit po bitu stejný image. Tím se dostáváme k tomu, proč vůbec kontejnery používat.


Proč vůbec kontejnery

  • Konzistence dev ↔ prod — image nese aplikaci i její prostředí (verze runtime, knihovny, systémové balíčky). Konec „u mě to jede, na serveru ne" — na serveru běží stejný image.
  • Izolace — dvě aplikace, které chtějí různé verze Pythonu nebo knihoven, si nelezou do zelí; každá má svůj kontejner.
  • Rychlé nasazení — nasazení = stáhnout image a spustit. Žádné ruční „doinstaluj tohle, nastav tamto". Rollback = spustit předchozí verzi image.
  • Čistý hostitel — nemusíš na systém instalovat deset různých databází a runtime; všechno žije zabalené v kontejnerech a po smazání nezůstane bordel.

Failure modes — časté omyly začátečníků

  • „Kontejner je bezpečný jako VM." Není — sdílí jádro s hostitelem, takže izolace je slabší. Nespouštěj v něm slepě nedůvěryhodný kód s očekáváním pancéřové hranice. Pomáhá rootless Podman.
  • Root uvnitř kontejneru. U rootful Dockeru proces běží jako root a při špatné konfiguraci (nebo díře v jádře) to může být root i na hostiteli. Rootless Podman naopak root uvnitř mapuje přes user namespace na tvého neprivilegovaného uživatele, takže host-root to není — proto ho preferuj (plus USER bez práv v image).
  • „Smazal jsem kontejner a přišel o databázi.“ Data bez volume jsou dočasná. Vždy si ujasni, co má přežít, a dej to do -v — jinak podman rm = konec dat.
  • „Build funguje u mě.“ Když ostatním pošleš jen zdrojáky bez image nebo Containerfile, postaví si něco jiného. Sdílej image (registry) nebo aspoň Containerfile, ne zabalený stav ze svého disku.
  • Obří image. FROM ubuntu + hromada balíčků = gigabajtové image, které se pomalu stahují. Vyjdi z menšího základu (alpine, -slim), uklízej po sobě, nezanášej do image build nástroje.
  • Tag latest. nginx:latest dnes a za měsíc může být jiná verze — build přestane být reprodukovatelný. Přišpendli konkrétní verzi (nginx:1.27), ať víš, co ti kde běží.

🛠️ Cvičení

  1. Kontejner vs VM vlastními slovy. Vysvětli na dvě věty, proč kontejner nabootuje za zlomek vteřiny a VM ne. Kde je v tom „vlastní jádro"?
  2. Spusť si web. Stáhni image nginx, spusť z něj kontejner a namapuj ho na port hostitele tak, abys ho otevřel v prohlížeči na http://localhost:8080. Jaký -p zápis použiješ?
  3. Image vs kontejner. Spusť ze stejného image nginx dva kontejnery zároveň a vypiš si běžící kontejnery. Kolik je image a kolik kontejnerů? Vysvětli ten poměr.
  4. Ztráta dat. Rozjeď kontejner, vytvoř v něm soubor (bez volume), kontejner smaž a spusť znovu. Je soubor tam? Proč? Jak bys to zařídil, aby přežil?
  5. Přečti si Containerfile. Najdi u libovolného projektu Containerfile/Dockerfile a popiš, co dělá řádek FROM, COPY, RUN a CMD. Který se spouští při stavbě a který až při běhu?
Náčrt řešení — rozbal, až si cvičení zkusíš sám
  1. Kontejner vs VM — kontejner je jen proces na už běžícím jádře hostitele, takže se „spustí" jako každý jiný program, okamžitě. VM musí nastartovat vlastní jádro a celý operační systém nad hypervizorem, což je plnohodnotný boot. Pozor: proto je kontejner malý a rychlý, ale izolovaný slaběji — sdílí jádro.
  2. Spuštění nginxpodman pull nginx, pak podman run -d -p 8080:80 nginx. Zápis -p 8080:80 znamená „port 8080 na hostiteli → port 80 uvnitř kontejneru". V prohlížeči otevřeš http://localhost:8080. Pozor: pořadí je HOST:KONTEJNER, splést si je znamená, že se nikam nedostaneš.
  3. Image vs kontejnerpodman run -d nginx spustíš dvakrát, pak podman ps. Máš jeden image a dva kontejnery — image je neměnná šablona, kontejner je běžící instance z ní. Poměr klidně 1 image : N kontejnerů. Pozor: smazání jednoho kontejneru se druhého ani image nedotkne.
  4. Ztráta dat — soubor tam není. Souborový systém kontejneru žije jen po dobu jeho existence; podman rm ho zahodí. Aby soubor přežil, musíš při spuštění připojit volume (-v ./data:/cesta/uvnitr) — zapisovat do něj, a data pak leží na hostiteli. Pozor: přežije jen to, co je uvnitř cesty volume, nic jiného.
  5. ContainerfileFROM určuje základní image, ze kterého se staví; COPY nakopíruje soubory z hostitele do image; RUN spustí příkaz při stavbě (např. instalace závislostí) a zapeče výsledek do image; CMD říká, co se spustí až při běhu kontejneru. Pozor: RUN = build-time, CMD = run-time — pletou se nejčastěji.

🧠 Otázky & odpovědi

Jaký je rozdíl mezi kontejnerem a virtuálním strojem?

Virtuální stroj emuluje celý počítač — má vlastní jádro, bootuje a běží v něm kompletní operační systém nad hypervizorem. Kontejner je oproti tomu jen izolovaný proces na sdíleném jádře hostitele: nemá vlastní jádro, nebootuje, jádro ho jen schová do svých namespaces a omezí cgroupami. Proto je kontejner mnohem rychlejší a lehčí (megabajty, start za zlomek vteřiny), ale izolovaný slaběji, protože o jádro se dělí s hostitelem i s ostatními kontejnery.

Co jsou namespaces a cgroups a jak spolu souvisí?

Jsou to dvě funkce jádra Linux, na kterých kontejnery stojí. Namespaces izolují pohled procesu — vidí jen svoje procesy, svou síť, své mounty, jako by byl na systému sám. cgroups izolují zdroje — určují, kolik CPU a paměti proces smí spotřebovat. Zkráceně: namespaces řeší co proces vidí, cgroups řeší kolik toho sní. Teprve dohromady dají dojem samostatného systému, i když je to pořád jen proces na tvém jádře.

Jaký je rozdíl mezi image a kontejnerem?

Image je neměnná šablona — zabalený souborový systém plus metadata o tom, co spustit; leží v úložišti a nikdy se sám nemění (jako recept nebo instalační ISO). Kontejner je běžící instance vytvořená z image (jako uvařené jídlo z receptu). Z jednoho image spustíš klidně deset kontejnerů najednou a každý žije vlastním životem. Když kontejner smažeš, image zůstane; když smažeš image, musíš ho příště znovu stáhnout nebo postavit.

Proč používat Podman místo Dockeru?

Podman řeší dvě slabiny klasického Dockeru. Za prvé nemá démona — Docker běží jako pořád zapnutá root služba, přes kterou jde všechno; Podman jen spustí kontejner jako obyčejný podproces a skončí. Za druhé umí běžet rootless, pod tvým uživatelem, takže kontejner nemá práva do celého systému, i kdyby se z něj něco utrhlo. A protože je CLI kompatibilní, skoro všude, kde návod píše docker, můžeš dát podman a funguje to stejně.

Proč data v kontejneru zmizí a jak je zachovat?

Protože souborový systém kontejneru je dočasný — žije jen po dobu existence toho kontejneru. Jakmile kontejner smažeš (podman rm), zmizí s ním i všechno, co si dovnitř zapsal. Aby data přežila, musíš připojit volume přes -v — buď složku hostitele, nebo pojmenovaný svazek. Cokoli aplikace zapíše do cesty volume, skončí mimo kontejner a přežije jeho smazání i restart. Proto databáze, uploady a logy patří vždy do volume, ne do „vnitřku" kontejneru.