
Docker-in-Docker është një mjedis i virtualizuar Docker-daemon, i cili ekzekutohet brenda vetë kontejnerit për ndërtimin e imazheve të kontejnerit. Qëllimi kryesor i krijimit të Docker-in-Docker ishte të ndihmonte në zhvillimin e vetë Docker. Shumë njerëz e përdorin atë për të drejtuar Jenkins CI. Në fillim duket normale, por pastaj lindin probleme që mund të shmangen duke instaluar Docker në kontejnerin Jenkins CI. Ky artikull shpjegon se si ta bëni këtë. Nëse jeni të interesuar për zgjidhjen përfundimtare pa detaje, thjesht lexoni seksionin e fundit të artikullit "Zgjidhja e problemit".

Docker-in-Docker: "I Mirë"
Më shumë se dy vjet më parë, unë kam vendosur në Docker --privileged dhe shkrova . Qëllimi ishte të ndihmonte ekipin kryesor të zhvillonte Docker më shpejt. Para se të kishte Docker-in-Docker, cikli tipik i zhvillimit ishte kështu:
- hackity hack;
- ndërtimi (build);
- ndalimi i Docker-daemonit të ekzekutuar;
- ekzekutimi i një Docker-daemoni të ri;
- testimi;
- riprojci; eksperimentimi.
Nëse dëshironit të bëni një ndërtim të bukur dhe të riprodhueshëm (dmth. brenda kontejnerit), atëherë bëhej më e komplikuar:
- hackity hack;
- të siguroheni që të keni një version funksional të Docker;
- të ndërtosh një Docker të ri me Docker-in e vjetër;
- ndaleni Docker-daemonin;
- ekzekutoni një Docker-daemon të ri;
- testoni;
- ndaloni Docker-daemonin e ri;
- përsëritni.
Me ardhjen e Docker-in-Docker, procesi u thjeshtua:
- hackity hack;
- ndërtimi + ekzekutimi në një hap;
- riprojci; eksperimentimi.
A nuk është kështu shumë më mirë?

Docker-in-Docker: "I Keq"
Megjithatë, përkundër mendimit të zakonshëm, Docker-in-Docker nuk është 100% e bërë nga yje, ponies dhe njëhorna. Po flas për disa probleme që zhvilluesi duhet t’i dijë.
Një nga to ka të bëjë me LSM (modulet e sigurisë Linux), si AppArmor dhe SELinux: kur nisni një kontejner, "Docker-i brendshëm" mund të përpiqet të aplikojë profile sigurie që do të bien në konflikt ose do të ngatërrojnë "Docker-in e jashtëm". Ky është problemi më i vështirë që duhet zgjidhur kur përpiqeni të bashkoni implementimin origjinal të flamurit –privileged. Ndryshimet e mia funksionuan, dhe të gjitha testet do të kalonin në makinën time Debian dhe në makinat virtuale testuese Ubuntu, por do të kolapsin dhe do të digjën në makinën e Maikll Krosbit (sa më kujtohet, ai kishte Fedora). Nuk mund të kujtoj shkakun e saktë të problemit, por ndoshta ndodhi sepse Maik është një njeri i mençur, i cili punon me SELINUX=enforce (unë përdora AppArmor), dhe ndryshimet e mia nuk e merrnin parasysh profilin SELinux.
Docker-in-Docker: "E keqe"
Problemi i dytë ka të bëjë me drejtuesit e ruajtjes së Docker. Kur nisni Docker-in-Docker, Docker-i i jashtëm funksionon mbi një sistem të zakonshëm skedari (EXT4, BTRFS ose çfarëdo tjetër që keni), ndërsa Docker-i brendshëm funksionon mbi sistemin e kopjimit në shkrim (AUFS, BTRFS, Device Mapper, etj., në varësi të asaj çfarë është konfiguruar të përdoret nga Docker-i i jashtëm). Kjo sjell shumë kombinime që nuk do të funksionojnë. Për shembull, nuk do të mund të ekzekutoni AUFS mbi AUFS.
Nëse po përdorni BTRFS mbi BTRFS, fillimisht duhet të funksionojë, por sa më shumë nënkategoritë të krijohen, nuk do të mund të hiqni nënkategorinë prind parent subvolume. Moduli Device Mapper nuk ka hapësira emërtimi, prandaj, nëse disa instanca Docker-i e përdorin atë në të njëjtën makinë, të gjitha do të jenë në gjendje të shohin (dhe të ndikojnë në) imazhet e njëri-tjetrit dhe të pajisjet e kopjimit të kontejnerëve. Kjo është keq.
Ekzistojnë rrugë alternative për zgjidhjen e shumë prej këtyre problemeve. Për shembull, nëse dëshironi të përdorni AUFS në Docker-in e brendshëm, thjesht shndërroni dosjen /var/lib/docker në atë, dhe gjithçka do të jetë në rregull. Docker ka shtuar disa hapësira emërtimi bazë në emrat e synuar të Device Mapper, kështu që nëse disa thirrje Docker-i realizohen në të njëjtën makinë, ato nuk do të "shkelin" njëra-tjetrën.
Megjithatë, një konfigurim i tillë nuk është fare i lehtë, siç mund ta shihni nga këta në repo dind në GitHub.
Docker-in-Docker: bëhet edhe më keq
Çfarë ndodh me cache-n e ndërtimit? Kjo gjithashtu mund të jetë mjaft e komplikuar. Njerëzit shpesh më pyesin, “nëse po përdor Docker-in-Docker, si mund të përdor imazhet që ndodhen në host-in tim, përveç se të rikthej gjithçka në Docker-in tim të brendshëm”?
Disa njerëz të ndërmarrë kanë provuar të lidhin /var/lib/docker nga host-i në kontejnerin Docker-in-Docker. Ndonjëherë ata ndajnë /var/lib/docker me disa kontejnerë.

Doni të dëmtoni të dhënat? Sepse kjo është pikërisht ajo që do të dëmtojë të dhënat tuaja!
Docker-daemon ishte qartësisht i dizajnuar për të pasur qasje ekskluzive në /var/lib/docker. Asgjë tjetër nuk duhet "të prekë, të bëjë thirrje ose të shqetësojë" skedat e Docker-it që ndodhen në këtë dosje.
Pse është kështu? Sepse kjo është një nga mësimet më të vështira që mësuam gjatë zhvillimit të dotCloud. Motori i kontejnerëve dotCloud funksiononte me disa procese që aksesonin paralelisht /var/lib/dotcloud. Hile të tilla si zëvendësimi atomik i skedarëve (në vend të editimit në vend), "shpërndarja" e kodit me bllokime rekomanduese dhe të detyrueshme dhe eksperimente të tjera me sisteme të sigurta si SQLite dhe BDB, nuk funksiononin gjithmonë. Kur ne ri-dizajnua mekanizmin tonë të kontejnerëve, i cili përfundimisht u shndërrua në Docker, një nga vendimet kryesore të dizajnit ishte të grumbulloheshin të gjitha operacionet me kontejnerët nën një daemon të vetëm, për të përfunduar të gjithë këtë budallallëk të qasjes së përbashkët.
Mos më kuptoni gabim: është e mundur të bëni diçka të mirë, të besueshme dhe të shpejtë, që përfshin disa procese dhe menaxhim modern paralel. Por ne mendojmë se është më e thjeshtë dhe më e lehtë të shkruani dhe mbani kod duke përdorur Docker si lojtarin e vetëm.
Kjo do të thotë se nëse ndani katalogun /var/lib/docker midis disa instancave të Docker-it, do të keni probleme. Sigurisht, kjo mund të funksionojë, veçanërisht në fazat e hershme të testimit. “Dëgjo, mama, unë mund ta nisim ubuntu me ‘docker’!” Por provoni të bëni diçka më të komplikuar, si të nxirrni të njëjtin imazh nga dy instanca të krijuara ndaras dhe do të shihni si digjet bota.
Kjo do të thotë se nëse sistemi juaj CI bën ndërtesa dhe rindezje, çdo herë kur ristartoni kontejnerin Docker-in-Docker rrezikoni të riktheni në cache një bombë bërthamore. Kjo nuk është aspak e këndshme!
Zgjidhja e problemit
Le të bëjmë një hap prapa. A ju nevojitet vërtet Docker-in-Docker apo thjesht doni të keni mundësinë të drejtoni Docker, domethënë të ndërtoni dhe ekzekutoni kontejnerë dhe imazhe nga sistemi juaj CI, ndërsa vetë ky sistem CI është në një kontejner?
Beso se shumicës së njerëzve u nevojitet opsioni i fundit, do të thotë që ata duan që një sistem CI, si Jenkins, të jetë në gjendje të drejtojë kontejnerë. Dhe mënyra më e thjeshtë për ta bërë këtë është thjesht të vendosni socket-in e Docker në kontejnerin tuaj CI, duke e lidhur atë me flagun -v.
Thënë thjesht, kur të drejtoni kontejnerin tuaj CI (Jenkins ose ndonjë tjetër), në vend që të thyej diçka bashkë me Docker-in-Docker, filloni atë me komandën:
docker run -v /var/run/docker.sock:/var/run/docker.sock ...Tani ky kontejner do të ketë akses në socket-in e Docker dhe, për pasojë, do të jetë në gjendje të drejtojë kontejnerë. Përveç se në vend të ekzekutimit të kontejnerëve "fëmijë" ai do të drejtojë kontejnerë "prindër".
Provojeni këtë, duke përdorur imazhin zyrtar docker (i cili përmban skedarin binar të Docker):
docker run -v /var/run/docker.sock:/var/run/docker.sock
-ti dockerKjo duket dhe funksionon si Docker-in-Docker, por nuk është Docker-in-Docker: kur ky kontejner të krijojë kontejnerë shtesë, ata do të krijohen në Dockerin e nivelit më të lartë. Nuk do të përjetoni efekte anësore të thellësisë, dhe cache i ndërtimit do të ndahet për thirrje të shumta.
Shënim: versionet e mëparshme të këtij artikulli këshillonin që të lidhni skedarin binar të Docker nga hosti në kontejner. Tani kjo është bërë e pasigurt, pasi mekanizmi i Docker nuk zgjerohet më në biblioteka statike ose pothuajse statike.
Pra, nëse dëshironi të përdorni Docker nga Jenkins CI, keni 2 opsione:
instalimi i Docker CLI duke përdorur sistemin bazë të paketimit të imazhit (dmth. nëse imazhi juaj bazohet në Debian, përdorni paketat .deb), duke përdorur API Docker.
Pak reklamë 🙂
Faleminderit që po qëndroni me ne. Ju pëlqen artikujt tanë? Doni të shihni më shumë materiale interesante? Na mbështesni duke bërë një porosi ose duke rekomanduar tek miqtë tuaj, , një analog unik i serverëve entry-level që e kemi shpikur për Ju: (opcionet me RAID1 dhe RAID10, deri në 24 bërthama dhe deri në 40GB DDR4 janë të disponueshme).
Dell R730xd dyfish më i lirë në qendër të të dhënave Equinix Tier IV në Amsterdam? Vetëm te ne në Holandë! Dell R420 — 2x E5-2430 2.2Ghz 6C 128GB DDR3 2x960GB SSD 1Gbps 100TB — nga $99! Lexoni rreth
Burimi: habr.com
