Docker architektúra

Začnem s tým, čo všetci poznáme - Docker. Konkrétne jeho architektúrou. Tá je pre mňa výbornou mapou vo svete kontajnerov. Pomáha mi zorientovať sa, ktorá technológia, kde zapadá.

Docker architektura

Ako prvé si treba uvedomiť, že Docker je celý ekosystém pre kontajnery. Docker, ktorý mame nainštalovaný, je z pohľadu kódu kompozícia niekoľkých veľkých projektov: Moby, Containerd a Runc.

Klientska aplikácia a Dockerd server

Klientska aplikácia a Dockerd server sú súčasťou projektu Moby. Na prvý pohľad to vyzerá ako klasická klient-server architektúra. Klientom je známy docker príkaz a serverom je Dockerd, s ktorým klient komunikuje cez REST API.

Dockerd pôvodne zastrešoval všetko potrebné ako zostavovanie obrazov z Dockerfie a ich distribúciu, nastavenie siete, životný cyklus kontajnerov. Neskôr však došlo k oddeleniu životného cyklu kontajnerov do samostatného projektu - Containerd.

Containerd server

Containerd je dnes v úlohe akéhosi režiséra. Jeho hlavnou úlohou je starať sa o kontajnery. Udržuje si dáta a metadáta o každom kontajneri a pripravuje všetko potrebné pre ich beh. Treba zdôrazniť, že Containerd však nie je zodpovedný za samotný beh jedného kontajnera. O to sa stará iná časť - tzv. Behové prostredie (runtime).

Containerd chce byť neutrálny a držať sa tzv. OCI štandardu. Preto neprebral všetku funkcionalitu od Dockerd. Príkadom je zostavenie obrazu z Dockerfile súboru. Táto funkcionalita je výhradne záležitosť Dockerd.

Containerd používa na integrácie gRCP protokol a unix soket /var/run/containerd.sock. S Containerd vieme komunikovať aj priamo pomocou príkazu ctr.

$ ctr image pull docker.io/library/postgres:latest

$ ctr run -d -env POSTGRES_PASSWORD=pwd docker.io/library/postgres:latest pg

$ ctr container list
CONTAINER    IMAGE                                RUNTIME
pg           docker.io/library/postgres:latest    io.containerd.runc.v2

Práve pri prvom príkaze som si všimol mierne “schizofrenickú” situáciu. Tento obraz totižto neuvidíme cez príkaz:

$ docker images --all

Toto je spôsobené tým, že distribúcia obrazov je implementovana dnes v Dockerd a aj Containerd. Keď som sa hrabal v kóde Containerd, zistil som že pull a push je implementovaná priamo v ctr. Prakticky nie je súčasťou Containerd servera.

Behové prostrenie - runtime

Behové prostredie je zodpovedné za beh jedného konkrétneho kontajnera. Je to miesto, kde sa odohráva celá ta izolačná mágia. Referenčnou implementáciou behového prostredia je Runc projekt. Runc je dostupná po nainštalovaní Dockeru na Linux systémoch.

$ runc --root /run/docker/runtime-runc/moby list

Runc nevie pracovať s obrazmi. Je na príliš nízkej úrovni. Ak chceme naštartovať kontajner cez Runc, musíme mu pripraviť celý súborový systém kontajnera a konfiguráciu behového prostredia. Poďme si vytvoriť ručne svoj kontajner:

$ mkdir mojkontajner
$ cd mojkontajner
$ mkdir rootfs

Cez Docker vyexportujeme obsah obrazu a skopírujeme celý obsah do rootfs adresára:

$ docker create --name pg2 postgres:latest
$ docker export pg2 | tar -xvf ./rootfs2.tar --directory ./rootfs

Už len vytvoríme konfiguráciu behového prostredia config.json a môžeme spustiť náš prvý ručne vytvorený kontajner:

$ runc spec
$ runc run mojkontajner

Voilà! Výsledkom by mal byť spustený shell kontajnera. To je spôsobené práve konfiguráciou config.json, v ktorom je časť:

"args": [
    "sh"
],

Podložka behového prostredia - shim

Runc je klasická terminálová aplikácia. Aby ju mohol Containerd používať pohodlne, tak sa začala používať malá integračná podložka - shim. Podložky sú obľúbeny koncept vo svete kontajnerov. Konrétne sa používa containerd-shim-runc-v2. Jej úlohou je prepojiť Containerd a Runc skrz gRPC. Samotná podložka nie je súčasťou Runc projektu, ale Containerd projektu - toto bolo pre mňa zo začiatku mätúce.

Register

Register je úložisko, ktoré slúži na distribúciu obrazov a je dostupný cez svoje REST API. Register je v podstate HTTP server, kde sú uložené obrazy.

Aby to nebolo až také jednoduché, tak obraz z pohľadu registra nie je jeden monolitický súbor. Obraz je rozdelený na manifest obrazu a bulk dáta. Získanie obrazu tak prebieha nasledovným spôsobom.

Docker pul

K tomu, aby sme vôbec mohli použiť API, musíme mať autorizačný token. V prípade verejných registrov Dockeru nie je potreba mať účet. Token je vystavený autorizačným serverom. Potom môžeme získať manifest obrazu. Z manifestu sa vyčíta zoznam bulk-ov a tie sa začnú sťahovať do systému. Dôvodom je vrstvenie, ktorému sa budem venovať v samostatnom článku. Súčasťou Moby projektu je skript download-frozen-image-v2.sh, v ktorom je tento proces pekne vidieť.

Záver

Pre nás programátorov má Docker architektúra veľmi zaujímavý príbeh evolúcie systému. Docker začínal ako Python skript. Neskôr bol prepísaný do Go a jeho architektúra pripomínala monolit. Až v poslednej fáze došlo k jeho štiepeniu na niekoľko menších “mikro-služieb”. To štiepenie nenastalo, lebo si niekto zmyslel, že je to takto architektonicky krajšie. Nastalo z dôvodu urýchlenia vývoja kontajnerových technológii a zachovania neutrality v rámci OpenSource sveta. Každá časť dnes predstavuje osobitnú doménu s osobitnou komunitou.

Už len pikoška na záver. Mena kontajnerov ako busy-allen alebo sad-batrik su dielom generátora pkg/namesgenerator/names-generator.go v Moby projekte.

<