Denk goed na voordat u Docker-in-Docker voor CI of testomgeving gebruikt

Denk goed na voordat u Docker-in-Docker voor CI of testomgeving gebruikt

Docker-in-Docker is een gevirtualiseerde Docker-daemonomgeving die binnen de container zelf draait om containerimages te bouwen. Het belangrijkste doel van het maken van Docker-in-Docker was om Docker zelf te helpen ontwikkelen. Veel mensen gebruiken het om Jenkins CI uit te voeren. Dit lijkt in eerste instantie normaal, maar dan ontstaan ​​er problemen die kunnen worden vermeden door Docker in een Jenkins CI-container te installeren. In dit artikel leest u hoe u dit kunt doen. Als je geïnteresseerd bent in de uiteindelijke oplossing zonder details, lees dan het laatste deel van het artikel, ‘Het probleem oplossen’.

Denk goed na voordat u Docker-in-Docker voor CI of testomgeving gebruikt

Docker-in-Docker: "Goed"

Ruim twee jaar geleden heb ik Docker geïnstalleerd vlag –bevoorrecht en schreef eerste versie van dind. Het doel was om het kernteam te helpen Docker sneller te ontwikkelen. Vóór Docker-in-Docker zag de typische ontwikkelingscyclus er als volgt uit:

  • hackity-hack;
  • bouwen;
  • een actieve Docker-daemon stoppen;
  • het lanceren van een nieuwe Docker-daemon;
  • testen;
  • herhaal de cyclus.

Als je een mooie, reproduceerbare assemblage wilde maken (dat wil zeggen in een container), dan werd het ingewikkelder:

  • hackity-hack;
  • zorg ervoor dat er een werkende versie van Docker actief is;
  • bouw nieuwe Docker met oude Docker;
  • stop Docker-daemon;
  • start een nieuwe Docker-daemon;
  • test;
  • stop de nieuwe Docker-daemon;
  • herhalen.

Met de komst van Docker-in-Docker is het proces eenvoudiger geworden:

  • hackity-hack;
  • montage + lancering in één fase;
  • herhaal de cyclus.

Is het niet veel beter op deze manier?

Denk goed na voordat u Docker-in-Docker voor CI of testomgeving gebruikt

Docker-in-Docker: "Slecht"

In tegenstelling tot wat vaak wordt gedacht, is Docker-in-Docker echter niet 100% sterren, pony's en eenhoorns. Wat ik bedoel is dat er verschillende problemen zijn waar een ontwikkelaar zich bewust van moet zijn.

Eén daarvan betreft LSM's (Linux-beveiligingsmodules) zoals AppArmor en SELinux: bij het draaien van een container kan de "interne Docker" proberen beveiligingsprofielen toe te passen die de "externe Docker" zullen conflicteren of verwarren. Dit is het moeilijkste probleem om op te lossen als je probeert de oorspronkelijke implementatie van de vlag –privileged samen te voegen. Mijn veranderingen werkten en alle tests zouden doorgaan op mijn Debian-machine en Ubuntu-test-VM's, maar ze zouden crashen en branden op de machine van Michael Crosby (hij had Fedora zoals ik me herinner). Ik kan me de exacte oorzaak van het probleem niet herinneren, maar het kan zijn dat Mike een wijze kerel is die met SELINUX=enforce werkt (ik gebruikte AppArmor) en mijn wijzigingen hielden geen rekening met SELinux-profielen.

Docker-in-Docker: "kwaad"

Het tweede probleem betreft de Docker-opslagstuurprogramma's. Wanneer u Docker-in-Docker uitvoert, draait externe Docker bovenop een normaal bestandssysteem (EXT4, BTRFS, of wat u ook heeft) en draait interne Docker bovenop een copy-on-write-systeem (AUFS, BTRFS, Device Mapper , enz.). , afhankelijk van wat is geconfigureerd om externe Docker te gebruiken). Hierdoor ontstaan ​​er veel combinaties die niet zullen werken. U kunt bijvoorbeeld geen AUFS bovenop AUFS uitvoeren.

Als u BTRFS bovenop BTRFS uitvoert, zou het in eerste instantie moeten werken, maar zodra er geneste subvolumes zijn, zal het verwijderen van het bovenliggende subvolume mislukken. De Device Mapper-module heeft geen naamruimte, dus als meerdere Docker-instanties deze op dezelfde machine draaien, kunnen ze allemaal de afbeeldingen op elkaar en op de containerback-upapparaten zien (en beïnvloeden). Dit is slecht.

Er zijn oplossingen om veel van deze problemen op te lossen. Als je bijvoorbeeld AUFS in de interne Docker wilt gebruiken, verander dan gewoon de map /var/lib/docker in een volume en alles komt goed. Docker heeft een aantal basisnaamruimten toegevoegd aan de doelnamen van Device Mapper, zodat als er meerdere Docker-aanroepen op dezelfde machine worden uitgevoerd, deze niet op elkaar zullen trappen.

Een dergelijke opstelling is echter helemaal niet eenvoudig, zoals hieruit blijkt artikels in de dind-repository op GitHub.

Docker-in-Docker: het wordt nog erger

Hoe zit het met de build-cache? Dit kan ook behoorlijk lastig zijn. Mensen vragen mij vaak: "Als ik Docker-in-Docker gebruik, hoe kan ik dan afbeeldingen gebruiken die op mijn host worden gehost in plaats van alles terug te halen naar mijn interne Docker"?

Sommige ondernemende mensen hebben geprobeerd /var/lib/docker van de host te binden aan een Docker-in-Docker-container. Soms delen ze /var/lib/docker met meerdere containers.

Denk goed na voordat u Docker-in-Docker voor CI of testomgeving gebruikt
Wilt u uw gegevens beschadigen? Want dit is precies wat uw gegevens zal beschadigen!

De Docker-daemon is duidelijk ontworpen om exclusieve toegang te hebben tot /var/lib/docker. Niets anders mag Docker-bestanden in deze map "aanraken, porren of prikken".

Waarom is dit zo? Omdat dit het resultaat is van een van de moeilijkste lessen die we hebben geleerd tijdens de ontwikkeling van dotCloud. De dotCloud-containerengine draaide doordat meerdere processen tegelijkertijd toegang hadden tot /var/lib/dotcloud. Sluwe trucs zoals atomaire bestandsvervanging (in plaats van in-place editing), code voorzien van adviserende en verplichte vergrendelingen en andere experimenten met veilige systemen zoals SQLite en BDB werkten niet altijd. Toen we onze containerengine, die uiteindelijk Docker werd, opnieuw gingen ontwerpen, was een van de grote ontwerpbeslissingen het consolideren van alle containeractiviteiten onder één enkele daemon om een ​​einde te maken aan alle concurrency-onzin.

Begrijp me niet verkeerd: het is heel goed mogelijk om iets goeds, betrouwbaars en snels te maken, waarbij meerdere processen en moderne parallelle besturing betrokken zijn. Maar we denken dat het eenvoudiger en gemakkelijker is om code te schrijven en te onderhouden met Docker als enige speler.

Dit betekent dat als u de map /var/lib/docker deelt tussen meerdere Docker-instanties, u problemen zult ondervinden. Dit kan natuurlijk werken, vooral in de vroege testfasen. "Luister, mama, ik kan ubuntu draaien als havenarbeider!" Maar probeer iets complexers, zoals hetzelfde beeld uit twee verschillende instanties halen, en je zult de wereld zien branden.

Dit betekent dat als uw CI-systeem builds en herbouwingen uitvoert, u elke keer dat u uw Docker-in-Docker-container opnieuw opstart, het risico loopt een kernbom in de cache te laten vallen. Dit is helemaal niet cool!

Probleemoplossend

Laten we een stapje terug doen. Heb je Docker-in-Docker echt nodig of wil je gewoon Docker kunnen draaien en containers en images kunnen bouwen en draaien vanuit je CI-systeem terwijl dat CI-systeem zelf in een container zit?

Ik wed dat de meeste mensen de laatste optie willen, wat betekent dat ze willen dat een CI-systeem zoals Jenkins containers kan draaien. En de eenvoudigste manier om dit te doen is door simpelweg een Docker-socket in uw CI-container te plaatsen en deze te koppelen aan de vlag -v.

Simpel gezegd: wanneer u uw CI-container (Jenkins of iets anders) uitvoert, in plaats van iets samen met Docker-in-Docker te hacken, begint u deze met de regel:

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

Deze container krijgt nu toegang tot de Docker-socket en kan daardoor containers draaien. Behalve dat het in plaats van ‘kind’-containers te gebruiken,’ zuster-containers zal lanceren.

Probeer dit met behulp van de officiële docker-image (die het Docker-binaire bestand bevat):

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

Het ziet eruit en werkt als Docker-in-Docker, maar het is niet Docker-in-Docker: wanneer deze container extra containers maakt, worden deze gemaakt in de Docker op het hoogste niveau. U zult geen last hebben van de bijwerkingen van nesten en de assemblagecache wordt over meerdere aanroepen gedeeld.

Opmerking: in eerdere versies van dit artikel werd geadviseerd om het Docker-binaire bestand van de host naar de container te koppelen. Dit is nu onbetrouwbaar geworden omdat de Docker-engine niet langer statische of bijna-statische bibliotheken ondersteunt.

Als je Docker van Jenkins CI wilt gebruiken, heb je dus 2 opties:
het installeren van de Docker CLI met behulp van het standaard image-verpakkingssysteem (dat wil zeggen, als uw image gebaseerd is op Debian, gebruik dan .deb-pakketten), met behulp van de Docker API.

Sommige advertenties 🙂

Bedankt dat je bij ons bent gebleven. Vind je onze artikelen leuk? Wil je meer interessante inhoud zien? Steun ons door een bestelling te plaatsen of door vrienden aan te bevelen, cloud VPS voor ontwikkelaars vanaf $ 4.99, een unieke analoog van servers op instapniveau, die door ons voor u is uitgevonden: De hele waarheid over VPS (KVM) E5-2697 v3 (6 kernen) 10 GB DDR4 480 GB SSD 1 Gbps vanaf $ 19 of hoe een server te delen? (beschikbaar met RAID1 en RAID10, tot 24 cores en tot 40GB DDR4).

Dell R730xd 2x goedkoper in Equinix Tier IV datacenter in Amsterdam? Alleen hier 2 x Intel TetraDeca-Core Xeon 2x E5-2697v3 2.6GHz 14C 64GB DDR4 4x960GB SSD 1Gbps 100 TV vanaf $199 in Nederland! Dell R420 - 2x E5-2430 2.2Ghz 6C 128GB DDR3 2x960GB SSD 1Gbps 100TB - vanaf $99! Lees over Hoe infrastructuur corp te bouwen. klasse met het gebruik van Dell R730xd E5-2650 v4-servers ter waarde van 9000 euro voor een cent?

Bron: www.habr.com

Voeg een reactie