Docker-in-Docker je virtualizované prostředí démona Docker běžící v samotném kontejneru za účelem vytváření obrazů kontejneru. Hlavním účelem vytvoření Docker-in-Docker bylo pomoci s vývojem samotného Dockeru. Mnoho lidí jej používá ke spuštění Jenkins CI. Zpočátku se to zdá normální, ale pak nastanou problémy, kterým se lze vyhnout instalací Dockeru do kontejneru Jenkins CI. Tento článek vám řekne, jak to udělat. Pokud vás zajímá konečné řešení bez podrobností, stačí si přečíst poslední část článku „Řešení problému“.
Docker-in-Docker: "Dobré"
Před více než dvěma lety jsem vložil do Docker
- hackity hack;
- stavět;
- zastavení běžícího démona Docker;
- spuštění nového démona Docker;
- testování;
- opakování cyklu.
Pokud jste chtěli vytvořit krásnou, reprodukovatelnou sestavu (to znamená v kontejneru), bylo to složitější:
- hackity hack;
- ujistěte se, že běží funkční verze Dockeru;
- vytvořit nový Docker se starým Dockerem;
- zastavit démona Docker;
- spustit nového démona Docker;
- test;
- zastavit nového démona Docker;
- opakovat.
S příchodem Docker-in-Docker se proces zjednodušil:
- hackity hack;
- montáž + spuštění v jedné fázi;
- opakování cyklu.
Není to takhle mnohem lepší?
Docker-in-Docker: "Špatný"
Na rozdíl od všeobecného přesvědčení však Docker-in-Docker nejsou 100% hvězdy, poníci a jednorožci. Chci říct, že existuje několik problémů, kterých si vývojář musí být vědom.
Jeden z nich se týká LSM (Linuxových bezpečnostních modulů), jako jsou AppArmor a SELinux: při spuštění kontejneru se může „interní Docker“ pokusit použít bezpečnostní profily, které budou v konfliktu nebo matoucí „externí Docker“. Toto je nejobtížněji řešitelný problém při pokusu o sloučení původní implementace příznaku –privileged. Moje změny fungovaly a všechny testy prošly na mém počítači Debian a testovacích virtuálních počítačích Ubuntu, ale na počítači Michaela Crosbyho (měl Fedoru, jak si vzpomínám) se zhroutily a spálily. Nepamatuji si přesnou příčinu problému, ale mohlo to být způsobeno tím, že Mike je moudrý člověk, který pracuje s SELINUX=enforce (použil jsem AppArmor) a moje změny nebraly v úvahu profily SELinux.
Docker-in-Docker: "Zlo"
Druhý problém je s ovladači úložiště Docker. Když spustíte Docker-in-Docker, externí Docker běží nad běžným souborovým systémem (EXT4, BTRFS nebo cokoli jiného) a interní Docker běží nad systémem kopírování při zápisu (AUFS, BTRFS, Device Mapper , atd.). v závislosti na tom, co je nakonfigurováno pro použití externího Dockeru). Vznikne tak mnoho kombinací, které nebudou fungovat. Například nebudete moci spouštět AUFS nad AUFS.
Pokud spustíte BTRFS nad BTRFS, mělo by to zpočátku fungovat, ale jakmile budou vnořené podsvazky, odstranění nadřazeného podsvazku se nezdaří. Modul Device Mapper nemá žádný jmenný prostor, takže pokud jej na stejném počítači provozuje více instancí Dockeru, všechny budou moci vidět (a ovlivňovat) obrazy na sobě navzájem a na zařízeních pro zálohování kontejnerů. Je to špatné.
Mnoho z těchto problémů lze vyřešit. Pokud chcete například používat AUFS v interním Dockeru, stačí přeměnit složku /var/lib/docker na svazek a budete v pořádku. Docker přidal některé základní jmenné prostory k cílovým názvům Device Mapper, takže pokud na stejném počítači běží více volání Dockeru, nebudou na sebe šlapat.
Takové nastavení však není vůbec jednoduché, jak je z nich patrné
Docker-in-Docker: Zhoršuje se to
A co mezipaměť sestavení? To může být také docela obtížné. Lidé se mě často ptají: „Pokud používám Docker-in-Docker, jak mohu použít obrázky hostované na mém hostiteli namísto stahování všeho zpět do mého interního Dockeru“?
Někteří podnikaví lidé se pokusili svázat /var/lib/docker z hostitele s kontejnerem Docker-in-Docker. Někdy sdílejí /var/lib/docker s více kontejnery.
Chcete poškodit svá data? Protože to je přesně to, co poškodí vaše data!
Démon Docker byl jasně navržen tak, aby měl výhradní přístup k /var/lib/docker. Nic jiného by se nemělo „dotýkat, šťouchat ani šťourat“ žádné soubory Dockeru umístěné v této složce.
proč tomu tak je? Protože toto je výsledek jedné z nejtěžších lekcí získaných při vývoji dotCloud. Kontejnerový engine dotCloud běžel tak, že k /var/lib/dotcloud přistupovalo současně několik procesů. Chytré triky, jako je atomické nahrazování souborů (místo úprav na místě), opepření kódu pomocí poradních a povinných zámků a další experimenty se zabezpečenými systémy, jako je SQLite a BDB, ne vždy fungovaly. Když jsme předělávali náš kontejnerový engine, ze kterého se nakonec stal Docker, jedním z velkých návrhových rozhodnutí bylo sjednotit všechny kontejnerové operace pod jediného démona, aby se odstranily všechny nesmysly o souběžnosti.
Nechápejte mě špatně: je zcela možné vyrobit něco dobrého, spolehlivého a rychlého, co zahrnuje více procesů a moderní paralelní řízení. Ale myslíme si, že je jednodušší a snazší psát a udržovat kód pomocí Dockeru jako jediného hráče.
To znamená, že pokud sdílíte adresář /var/lib/docker mezi více instancemi Dockeru, budete mít problémy. To samozřejmě může fungovat, zvláště v raných fázích testování. "Poslouchej, mami, můžu spustit ubuntu jako docker!" Ale zkuste něco složitějšího, jako je vytažení stejného obrázku ze dvou různých instancí, a uvidíte, jak svět hoří.
To znamená, že pokud váš systém CI provádí sestavení a přestavby, pokaždé, když restartujete kontejner Docker-in-Docker, riskujete, že do jeho mezipaměti spadne atomovka. To není vůbec cool!
Řešení
Udělejme krok zpět. Opravdu potřebujete Docker-in-Docker nebo jen chcete mít možnost spouštět Docker a sestavovat a spouštět kontejnery a obrazy z vašeho systému CI, zatímco samotný systém CI je v kontejneru?
Vsadím se, že většina lidí chce druhou možnost, což znamená, že chtějí, aby systém CI jako Jenkins mohl provozovat kontejnery. Nejjednodušší způsob, jak toho dosáhnout, je jednoduše vložit soket Docker do vašeho kontejneru CI a přiřadit jej k parametru -v.
Jednoduše řečeno, když spustíte svůj kontejner CI (Jenkins nebo jiný), místo toho, abyste něco hackovali spolu s Docker-in-Docker, začněte to řádkem:
docker run -v /var/run/docker.sock:/var/run/docker.sock ...
Tento kontejner bude mít nyní přístup k soketu Docker, a proto bude moci spouštět kontejnery. Až na to, že místo spouštění „dětských“ kontejnerů spustí „sourozenecké“ kontejnery.
Zkuste to pomocí oficiálního obrazu dockeru (který obsahuje binární soubor Docker):
docker run -v /var/run/docker.sock:/var/run/docker.sock
-ti docker
Vypadá a funguje jako Docker-in-Docker, ale není to Docker-in-Docker: když tento kontejner vytvoří další kontejnery, budou vytvořeny v Dockeru nejvyšší úrovně. Nezaznamenáte vedlejší účinky vnoření a mezipaměť sestavení bude sdílena v rámci více volání.
Poznámka: Předchozí verze tohoto článku doporučovaly propojit binární soubor Docker z hostitele s kontejnerem. To se nyní stalo nespolehlivé, protože Docker engine již nepokrývá statické nebo téměř statické knihovny.
Pokud tedy chcete používat Docker od Jenkins CI, máte 2 možnosti:
instalace Docker CLI pomocí základního systému balení obrazu (tj. pokud je váš obraz založen na Debianu, použijte balíčky .deb) pomocí rozhraní Docker API.
Nějaké inzeráty 🙂
Děkujeme, že s námi zůstáváte. Líbí se vám naše články? Chcete vidět více zajímavého obsahu? Podpořte nás objednávkou nebo doporučením přátelům,
Dell R730xd 2krát levnější v datovém centru Equinix Tier IV v Amsterdamu? Pouze zde
Zdroj: www.habr.com