Gândiți-vă bine înainte de a utiliza Docker-in-Docker pentru CI sau mediul de testare

Gândiți-vă bine înainte de a utiliza Docker-in-Docker pentru CI sau mediul de testare

Docker-in-Docker este un mediu demon Docker virtualizat care rulează în interiorul containerului însuși pentru a construi imagini ale containerului. Scopul principal al creării Docker-in-Docker a fost de a ajuta la dezvoltarea Docker în sine. Mulți oameni îl folosesc pentru a rula Jenkins CI. Acest lucru pare normal la început, dar apoi apar probleme care pot fi evitate prin instalarea Docker într-un container Jenkins CI. Acest articol vă spune cum să faceți acest lucru. Dacă sunteți interesat de soluția finală fără detalii, trebuie doar să citiți ultima secțiune a articolului, „Rezolvarea problemei”.

Gândiți-vă bine înainte de a utiliza Docker-in-Docker pentru CI sau mediul de testare

Docker-in-Docker: „Bine”

Acum mai bine de doi ani am introdus Docker pavilion – privilegiat și scris prima versiune a lui dind. Scopul a fost de a ajuta echipa de bază să dezvolte mai rapid Docker. Înainte de Docker-in-Docker, ciclul de dezvoltare tipic arăta astfel:

  • hackity hack;
  • construi;
  • oprirea unui demon Docker care rulează;
  • lansarea unui nou daemon Docker;
  • testare;
  • repeta ciclul.

Dacă ați vrut să faceți un ansamblu frumos, reproductibil (adică într-un recipient), atunci a devenit mai complicat:

  • hackity hack;
  • asigurați-vă că rulează o versiune funcțională a Docker;
  • construiți un nou Docker cu vechiul Docker;
  • opriți demonul Docker;
  • porniți un nou daemon Docker;
  • Test;
  • opriți noul demon Docker;
  • repeta.

Odată cu apariția Docker-in-Docker, procesul a devenit mai simplu:

  • hackity hack;
  • asamblare + lansare într-o singură etapă;
  • repeta ciclul.

Nu e mult mai bine așa?

Gândiți-vă bine înainte de a utiliza Docker-in-Docker pentru CI sau mediul de testare

Docker-in-Docker: „Rău”

Cu toate acestea, contrar credinței populare, Docker-in-Docker nu este 100% vedete, ponei și unicorni. Ceea ce vreau să spun este că există mai multe probleme de care un dezvoltator trebuie să fie conștient.

Unul dintre ele se referă la LSM-uri (module de securitate Linux) precum AppArmor și SELinux: atunci când rulează un container, „dockerul intern” poate încerca să aplice profiluri de securitate care vor intra în conflict sau vor încurca „dockerul extern”. Aceasta este cea mai dificilă problemă de rezolvat atunci când încercați să îmbinați implementarea originală a steagului –privileged. Modificările mele au funcționat și toate testele aveau să treacă pe mașina mea Debian și pe mașinile virtuale de testare Ubuntu, dar se prăbușeau și se ardeau pe mașina lui Michael Crosby (din câte îmi amintesc avea Fedora). Nu-mi amintesc cauza exactă a problemei, dar poate să fi fost pentru că Mike este un tip înțelept care lucrează cu SELINUX=enforce (am folosit AppArmor) și modificările mele nu au luat în considerare profilurile SELinux.

Docker-in-Docker: „Rău”

A doua problemă este cu driverele de stocare Docker. Când rulați Docker-in-Docker, Docker extern rulează pe un sistem de fișiere obișnuit (EXT4, BTRFS sau orice aveți), iar Docker intern rulează pe un sistem de copiere la scriere (AUFS, BTRFS, Device Mapper). , etc.). , în funcție de ceea ce este configurat pentru a utiliza Docker extern). Acest lucru creează multe combinații care nu vor funcționa. De exemplu, nu veți putea rula AUFS peste AUFS.

Dacă rulați BTRFS peste BTRFS, ar trebui să funcționeze la început, dar odată ce există subvolume imbricate, ștergerea subvolumului părinte va eșua. Modulul Device Mapper nu are spațiu de nume, așa că dacă mai multe instanțe Docker îl rulează pe aceeași mașină, toate vor putea să vadă (și să influențeze) imaginile una pe cealaltă și pe dispozitivele de rezervă ale containerului. Asta e rău.

Există soluții pentru a rezolva multe dintre aceste probleme. De exemplu, dacă doriți să utilizați AUFS în Docker intern, doar transformați folderul /var/lib/docker într-un volum și veți fi bine. Docker a adăugat câteva spații de nume de bază la numele țintei Device Mapper, astfel încât, dacă mai multe apeluri Docker rulează pe aceeași mașină, acestea să nu calce unul pe celălalt.

Cu toate acestea, o astfel de configurare nu este deloc simplă, așa cum se poate vedea din acestea articole în depozitul dind de pe GitHub.

Docker-in-Docker: Se înrăutățește

Ce zici de build cache? Acest lucru poate fi, de asemenea, destul de dificil. Oamenii mă întreabă adesea „dacă rulez Docker-in-Docker, cum pot folosi imaginile găzduite pe gazda mea în loc să trag totul înapoi în Docker-ul meu intern”?

Unii oameni întreprinzători au încercat să lege /var/lib/docker de la gazdă la un container Docker-in-Docker. Uneori partajează /var/lib/docker cu mai multe containere.

Gândiți-vă bine înainte de a utiliza Docker-in-Docker pentru CI sau mediul de testare
Doriți să vă corupați datele? Pentru că tocmai asta vă va deteriora datele!

Daemonul Docker a fost în mod clar conceput pentru a avea acces exclusiv la /var/lib/docker. Nimic altceva nu ar trebui să „atingă, să împingă sau să împingă” fișierele Docker aflate în acest folder.

De ce este așa? Pentru că acesta este rezultatul uneia dintre cele mai grele lecții învățate în timpul dezvoltării dotCloud. Motorul containerului dotCloud a rulat având mai multe procese care accesau /var/lib/dotcloud simultan. Trucurile viclene, cum ar fi înlocuirea fișierelor atomice (în loc de editarea la locul său), codul de piper cu blocări consultative și obligatorii și alte experimente cu sisteme securizate precum SQLite și BDB nu au funcționat întotdeauna. Când ne reproiectam motorul de containere, care în cele din urmă a devenit Docker, una dintre marile decizii de proiectare a fost să consolidăm toate operațiunile containerului sub un singur daemon pentru a elimina toate prostiile de concurență.

Nu mă înțelege greșit: este complet posibil să faci ceva bun, fiabil și rapid care implică procese multiple și control paralel modern. Dar credem că este mai simplu și mai ușor să scrieți și să mențineți cod folosind Docker ca singur player.

Aceasta înseamnă că dacă partajați directorul /var/lib/docker între mai multe instanțe Docker, veți avea probleme. Desigur, acest lucru poate funcționa, mai ales în etapele incipiente ale testării. „Ascultă, mamă, pot rula ubuntu ca docker!” Dar încercați ceva mai complex, cum ar fi să trageți aceeași imagine din două cazuri diferite și veți vedea lumea arzând.

Aceasta înseamnă că, dacă sistemul dvs. CI realizează construcții și reconstruiri, de fiecare dată când reporniți containerul Docker-in-Docker, riscați să aruncați o nucleară în memoria cache. Nu este deloc cool!

Soluția

Să facem un pas înapoi. Chiar aveți nevoie de Docker-in-Docker sau doriți doar să puteți rula Docker și să construiți și să rulați containere și imagini din sistemul dvs. CI în timp ce acel sistem CI este într-un container?

Pun pariu că majoritatea oamenilor doresc această din urmă opțiune, adică vor ca un sistem CI precum Jenkins să poată rula containere. Și cel mai simplu mod de a face acest lucru este să introduceți pur și simplu un socket Docker în containerul dvs. CI și să îl asociați cu steag-ul -v.

Mai simplu spus, când rulați containerul CI (Jenkins sau altul), în loc să piratați ceva împreună cu Docker-in-Docker, începeți-l cu linia:

docker run -v /var/run/docker.sock:/var/run/docker.sock ...

Acest container va avea acum acces la socket-ul Docker și, prin urmare, va putea rula containere. Cu excepția faptului că, în loc să ruleze containere „copii”, va lansa containere „frați”.

Încercați acest lucru folosind imaginea oficială Docker (care conține binarul Docker):

docker run -v /var/run/docker.sock:/var/run/docker.sock 
           -ti docker

Arată și funcționează ca Docker-in-Docker, dar nu este Docker-in-Docker: atunci când acest container creează containere suplimentare, acestea vor fi create în Docker de nivel superior. Nu veți experimenta efectele secundare ale imbricației, iar memoria cache de asamblare va fi partajată în mai multe apeluri.

Notă: versiunile anterioare ale acestui articol recomandau conectarea binarului Docker de la gazdă la container. Acest lucru a devenit acum nesigur, deoarece motorul Docker nu mai acoperă bibliotecile statice sau aproape statice.

Deci, dacă doriți să utilizați Docker de la Jenkins CI, aveți 2 opțiuni:
instalarea CLI Docker folosind sistemul de ambalare a imaginii de bază (adică, dacă imaginea dvs. se bazează pe Debian, utilizați pachete .deb), folosind API-ul Docker.

Câteva reclame 🙂

Vă mulțumim că ați rămas cu noi. Vă plac articolele noastre? Vrei să vezi mai mult conținut interesant? Susține-ne plasând o comandă sau recomandând prietenilor, cloud VPS pentru dezvoltatori de la 4.99 USD, un analog unic al serverelor entry-level, care a fost inventat de noi pentru tine: Întregul adevăr despre VPS (KVM) E5-2697 v3 (6 nuclee) 10GB DDR4 480GB SSD 1Gbps de la 19 USD sau cum să partajezi un server? (disponibil cu RAID1 și RAID10, până la 24 de nuclee și până la 40 GB DDR4).

Dell R730xd de 2 ori mai ieftin în centrul de date Equinix Tier IV din Amsterdam? Numai aici 2 x Intel TetraDeca-Core Xeon 2x E5-2697v3 2.6GHz 14C 64GB DDR4 4x960GB SSD 1Gbps 100 TV de la 199 USD in Olanda! Dell R420 - 2x E5-2430 2.2Ghz 6C 128GB DDR3 2x960GB SSD 1Gbps 100TB - de la 99 USD! Citește despre Cum se construiește infrastructura corp. clasa cu folosirea serverelor Dell R730xd E5-2650 v4 in valoare de 9000 euro pentru un ban?

Sursa: www.habr.com

Adauga un comentariu