
Docker-in-Docker este un mediu virtualizat Docker daemon, care rulează în containerul însuși pentru a construi imagini de container. Scopul principal al creării Docker-in-Docker a fost să ajute la dezvoltarea Docker-ului însuși. Multe persoane îl folosesc pentru a rula Jenkins CI. La început, pare normal, dar apoi apar probleme care pot fi evitate instalând Docker în containerul Jenkins CI. Acest articol explică cum să faceți acest lucru. Dacă sunteți interesat de soluția finală fără detalii, citiți pur și simplu ultima secțiune a articolului „Soluția problemei”.

Docker-in-Docker: „Bun”
Acum mai bine de doi ani am introdus Docker –privileged și am scris . Scopul era să ajut echipa de bază să dezvolte Docker mai repede. Înainte de Docker-in-Docker, ciclul tipic de dezvoltare era următorul:
- hackity hack;
- construire (build);
- oprirea daemonului Docker în execuție;
- pornirea unui nou daemon Docker;
- testare;
- repetați ciclul.
Dacă doriți să realizați o construcție frumoasă și reproducibilă (adică în container), devenea mai complicat:
- hackity hack;
- verificarea faptului că o versiune funcțională Docker este pornită;
- construirea unui nou Docker cu vechiul Docker;
- oprirea daemonului Docker;
- pornirea unui nou daemon Docker;
- testare;
- oprirea noului daemon Docker;
- repetați.
Odată cu apariția Docker-in-Docker, procesul s-a simplificat:
- hackity hack;
- construire + pornire într-o singură etapă;
- repetați ciclul.
Nu-i așa că este mult mai bine așa?

Docker-in-Docker: „Rău”
Totuși, contrar opiniei populare, Docker-in-Docker nu este 100% din stele, ponei și unicorni. Cu alte cuvinte, există câteva probleme de care trebuie să fie conștient dezvoltatorul.
Una dintre ele se referă la LSM (modul de securitate Linux), cum ar fi AppArmor și SELinux: atunci când pornești un container, „Docker intern” poate încerca să aplice profiluri de securitate care vor intra în conflict sau vor confunda „Docker extern”. Aceasta este cea mai complicată problemă pe care a trebuit să o rezolvăm în încercarea de a combina implementarea originală a flag-ului –privileged. Modificările mele au funcționat, iar toate testele ar fi trecut pe mașina mea Debian și pe mașinile virtuale de test Ubuntu, dar ar fi eșuat pe mașina lui Michael Crosby (cât îmi amintesc, el avea Fedora). Nu îmi amintesc exact cauza problemei, dar este posibil să fi apărut deoarece Mike este un om înțelept care lucrează cu SELINUX=enforce (eu am folosit AppArmor) și modificările mele nu au ținut cont de profilurile SELinux.
Docker-in-Docker: „Rea”
A doua problemă se referă la driverele de stocare Docker. Când pornești Docker-in-Docker, Docker extern funcționează deasupra sistemului de fișiere obișnuit (EXT4, BTRFS sau oricare altul pe care îl ai), iar Docker intern funcționează deasupra sistemului de copiere la scriere (AUFS, BTRFS, Device Mapper etc., în funcție de ceea ce este setat să utilizeze Docker extern). Combinarea acestor două duce la multe combinații care nu vor funcționa. De exemplu, nu poți rula AUFS deasupra AUFS.
Dacă rulezi BTRFS deasupra BTRFS, inițial ar trebui să funcționeze, dar odată ce apar subvolumele imbricate, nu vei putea șterge subvolumul părinte. Modulul Device Mapper nu are spațiu de nume, așa că, dacă mai multe instanțe Docker îl folosesc pe aceeași mașină, toate vor putea vedea (și influența) imaginile una asupra celeilalte și asupra dispozitivelor de backup ale containerelor. Asta este rău.
Există soluții pentru multe dintre aceste probleme. De exemplu, dacă vrei să folosești AUFS în Docker intern, transformă pur și simplu folderul /var/lib/docker într-un volum, și totul va fi în regulă. Docker a adăugat câteva spații de nume de bază la numele țintă ale Device Mapper, așa că dacă mai multe apeluri Docker sunt efectuate pe aceeași mașină, acestea nu se vor „pestea” unele pe celelalte.
Cu toate acestea, această configurație nu este deloc simplă, după cum se poate vedea din acestea în depozitul dind de pe GitHub.
Docker-in-Docker: devine și mai rău
Cum rămâne cu cache-ul de build? Aceasta poate fi, de asemenea, destul de complicat. Oameni adesea mă întreabă „dacă rulez Docker-in-Docker, cum pot folosi imaginile situate pe gazda mea, în loc să le descarc din nou în Docker-ul meu intern”?
Unii oameni întreprinzători au încercat să leagă /var/lib/docker de pe gazdă în containerul Docker-in-Docker. Uneori, ei partajează /var/lib/docker între mai multe containere.

Vrei să îți corupi datele? Pentru că asta este exact ce îți va distruge datele!
Docker daemon a fost clar proiectat pentru a avea acces exclusiv la /var/lib/docker. Nimic altceva nu ar trebui să «atingă, să ciocnească sau să pipăie» fișierele Docker din acest folder.
De ce este așa? Pentru că este rezultatul uneia dintre cele mai dificile lecții învățate în timpul dezvoltării dotCloud. Motorul de containere dotCloud a funcționat având mai multe procese accesând simultan /var/lib/dotcloud. Trucuri ingenioase, cum ar fi înlocuirea atomică a fișierelor (în loc de editarea la fața locului), «piperizarea» codului cu blocaje recomandate și obligatorii și alte experimente cu sisteme sigure, cum ar fi SQLite și BDB, nu au funcționat întotdeauna. Când am refăcut motorul nostru de containere, care a devenit în cele din urmă Docker, una dintre principalele decizii de proiectare a fost să centralizăm toate operațiunile cu containere sub un singur daemon, pentru a pune capăt acestei confuzii privind accesul simultan.
Nu mă înțelegeți greșit: este absolut posibil să faci ceva bun, fiabil și rapid, care să includă mai multe procese și management paralel modern. Dar credem că este mai simplu și mai ușor să scrii și să întreții codul, folosind Docker ca singur jucător.
Aceasta înseamnă că, dacă împarți directorul /var/lib/docker între mai multe instanțe Docker, vei avea probleme. Desigur, asta poate funcționa, mai ales în etapele timpurii de testare. „Ascultă, mama, pot rula ubuntu cu 'docker'!” Dar încearcă să faci ceva mai complex, cum ar fi să extragi aceeași imagine din două instanțe diferite, și vei vedea cum se transformă lumea în scrum.
Aceasta înseamnă că, dacă sistemul dumneavoastră CI efectuează construcții și reconstrucții, de fiecare dată când reporniți containerul Docker-in-Docker, riscați să resetați o bombă cu neutroni în cache-ul său. Asta nu este deloc ideal!
Rezolvarea problemei
Hai să facem un pas înapoi. Aveți cu adevărat nevoie de Docker-in-Docker sau doriți pur și simplu să aveți capacitatea de a rula Docker, adică să construiți și să porniți containere și imagini din sistemul dumneavoastră CI, în timp ce acest sistem CI se află într-un container?
Sunt convins că majoritatea oamenilor au nevoie de ultima opțiune, adică vor ca un sistem CI, cum ar fi Jenkins, să poată rula containere. Cel mai simplu mod de a face acest lucru este să conectați socket-ul Docker în containerul dumneavoastră CI, legându-l cu flag-ul -v.
Cu alte cuvinte, când rulați containerul CI (Jenkins sau altul), în loc să încercați să modificați ceva cu Docker-in-Docker, porniți-l cu următoarea comandă:
docker run -v /var/run/docker.sock:/var/run/docker.sock ...Acum acest container va avea acces la socket-ul Docker și, prin urmare, va putea să ruleze containere. Cu excepția faptului că, în loc de a porni containere "fete", va lansa containere "frate".
Încercați asta folosind imaginea oficială docker (care conține binarul Docker):
docker run -v /var/run/docker.sock:/var/run/docker.sock
-ti dockerAcesta arată și funcționează ca Docker-in-Docker, dar nu este Docker-in-Docker: când acest container va crea containere suplimentare, acestea vor fi create în Docker-ul de nivel superior. Nu veți experimenta efecte secundare ale imbricării, iar cache-ul de construcție va fi partajat pentru mai multe apeluri.
Notă: versiunile anterioare ale acestui articol au sfătuit să legați binarul Docker de pe gazdă la container. Acum aceasta a devenit nesigur, deoarece mecanismul Docker nu se mai aplică la biblioteci statice sau aproape statice.
Astfel, dacă doriți să utilizați Docker din Jenkins CI, aveți 2 opțiuni:
instalarea Docker CLI folosind sistemul de bază de pachetare a imaginii (de exemplu, dacă imaginea dumneavoastră este bazată pe Debian, folosiți pachetele .deb), utilizarea Docker API.
Puțin publicitate 🙂
Mulțumim că rămâneți cu noi. Vă plac articolele noastre? Doriți să vedeți mai multe materiale interesante? Susțineți-ne, efectuând o comandă sau recomandându-ne prietenilor, , un echivalent unic pentru serverele entry-level, care a fost creat de noi pentru voi: (sunt disponibile opțiuni cu RAID1 și RAID10, până la 24 nuclee și până la 40GB DDR4).
Dell R730xd la jumătate de preț în centrul de date Equinix Tier IV din Amsterdam? Numai la noi în Olanda! Dell R420 — 2x E5-2430 2.2Ghz 6C 128GB DDR3 2x960GB SSD 1Gbps 100TB — de la 99 $! Citiți despre
Sursa: habr.com
