
Docker-in-Docker is a virtualized Docker daemon environment running inside the container to build container images. The primary goal of creating Docker-in-Docker was to assist in developing Docker itself. Many people use it to run Jenkins CI. At first, this seems normal, but then problems arise that can be avoided by installing Docker in the Jenkins CI container. This article discusses how to do that. If you're interested in the final solution without details, just read the last section of the article 'Problem Solution'.

Docker-in-Docker: 'Good'
More than two years ago, I included in Docker āprivileged and wrote . The goal was to help the core team develop Docker faster. Before Docker-in-Docker, the typical development cycle was like this:
- hackity hack;
- build;
- stop the running Docker daemon;
- start a new Docker daemon;
- test;
- repeat the cycle.
However, if you wanted to make a nice, reproducible build (in a container), it became more complex:
- hackity hack;
- make sure a working version of Docker is running;
- build new Docker with old Docker;
- stop the Docker daemon;
- start the new Docker daemon;
- test;
- stop the new Docker daemon;
- repeat.
With Docker-in-Docker, the process has become simpler:
- hackity hack;
- build + run in one step;
- repeat the cycle.
Isn't it much better this way?

Docker-in-Docker: 'Bad'
However, contrary to popular belief, Docker-in-Docker is not 100% stars, ponies, and unicorns. I mean, there are several issues that a developer needs to be aware of.
Een van deze betreft LSM (Linux-beveiligingsmodules), zoals AppArmor en SELinux: wanneer je een container start, kan de "interne Docker" proberen beveiligingsprofielen toe te passen die in conflict komen met of verwarrend zijn voor de "externe Docker". Dit is het moeilijkste probleem dat moet worden opgelost bij het proberen te combineren van de oorspronkelijke implementatie van de -privileged vlag. Mijn wijzigingen werkten, en alle tests zouden ook op mijn Debian-machine en testvirtuele machines van Ubuntu zijn geslaagd, maar ze zouden falen en inbranden op de machine van Michael Crosby (voor zover ik me herinner, had hij Fedora). Ik kan me de exacte oorzaak van het probleem niet herinneren, maar het kan zijn dat het kwam omdat Mike ā een wijze man ā werkt met SELINUX=enforce (ik gebruikte AppArmor), en mijn wijzigingen hielden geen rekening met de SELinux-profielen.
Docker-in-Docker: "Slecht"
Het tweede probleem heeft betrekking op de Docker-opslagdrivers. Wanneer je Docker-in-Docker draait, werkt de externe Docker bovenop een normale bestandssysteem (EXT4, BTRFS of een andere die je hebt), terwijl de interne Docker werkt bovenop een copy-on-write systeem (AUFS, BTRFS, Device Mapper, enz., afhankelijk van wat is ingesteld voor de externe Docker). Dit leidt tot tal van combinaties die niet zullen werken. Bijvoorbeeld, je kunt geen AUFS bovenop AUFS draaien.
Als je BTRFS bovenop BTRFS draait, zou dit in eerste instantie moeten werken, maar zodra er geneste subvolumes verschijnen, kun je het bovenliggende subvolume niet verwijderen. De Device Mapper-module heeft geen naamruimten, dus als meerdere Docker-instanties deze op één machine gebruiken, kunnen ze elkaar beïnvloeden (en veranderen) in de beeldbestanden en de containerback-upapparaten. Dit is problematisch.
Er zijn omwegen om veel van deze problemen op te lossen. Bijvoorbeeld, als je AUFS wilt gebruiken in de interne Docker, verander dan gewoon de map /var/lib/docker in een volume en alles zou goed moeten zijn. Docker heeft enkele basisnaamruimten toegevoegd aan de doel-namen voor de Device Mapper, zodat als meerdere Docker-aanroepen op ƩƩn machine worden gedaan, ze elkaar niet zullen "aanvallen".
Toch is zo'n configuratie verre van eenvoudig, zoals je kunt zien uit deze in de dind-repository op GitHub.
Docker-in-Docker: het wordt nog erger
Wat betreft de build-cache? Dat kan ook behoorlijk ingewikkeld zijn. Mensen vragen me vaak: āAls ik Docker-in-Docker draai, hoe kan ik dan beelden gebruiken die op mijn host staan in plaats van alles opnieuw te downloaden in mijn interne Docker?ā
Sommige ondernemende mensen hebben geprobeerd /var/lib/docker van de host te koppelen aan de Docker-in-Docker-container. Soms delen ze /var/lib/docker met meerdere containers.

Wil je gegevens corrupt maken? Want dat is precies wat dit zal doen!
De Docker-daemon is duidelijk ontworpen voor exclusieve toegang tot /var/lib/docker. Niets anders zou toegang mogen hebben tot de Docker-bestanden in deze map.
Waarom is dat zo? Omdat dit het resultaat is van een van de moeilijkste lessen die zijn geleerd tijdens de ontwikkeling van dotCloud. De container-engine van dotCloud werkte met meerdere processen die tegelijkertijd toegang hadden tot /var/lib/dotcloud. Sluwe trucs, zoals atomair bestanden vervangen (in plaats van ter plaatse te bewerken), het āpeperenā van code met aanbevolen en verplichte vergrendelingen, en andere experimenten met veilige systemen zoals SQLite en BDB, werkten niet altijd. Toen we onze container-engine herschreven, die uiteindelijk Docker werd, was een van de belangrijkste ontwerpeisen dat alle containeroperaties onder ƩƩn daemon moesten worden gebracht om al deze rompslomp van gelijktijdige toegang te beĆ«indigen.
Begrijp me niet verkeerd: het is heel goed mogelijk om iets goeds, betrouwbaar en snel te maken met meerdere processen en modern parallel management. Maar wij denken dat het eenvoudiger en gemakkelijker is om code te schrijven en te onderhouden door Docker als enige speler te gebruiken.
Dit betekent dat als je de map /var/lib/docker deelt tussen meerdere Docker-instanties, je problemen zult ondervinden. Natuurlijk kan dit werken, vooral in de vroege stadia van testen. 'Hoor eens, mam, ik kan 'docker' ubuntu draaien!' Maar probeer iets complexers te doen, zoals dezelfde afbeelding uit twee verschillende instanties te halen, en je zult zien hoe de wereld in vuur en vlam staat.
Dit betekent dat als uw CI-systeem builds en rebuilds uitvoert, u elke keer dat u de Docker-in-Docker-container opnieuw opstart het risico loopt een 'time bomb' in zijn cache te resetten. Dat is beslist niet ideaal!
Oplossing van het probleem
Laten we een stap terugnemen. Heeft u echt Docker-in-Docker nodig of wilt u gewoon Docker kunnen draaien, namelijk containers en images vanuit uw CI-systeem bouwen en draaien, terwijl dat CI-systeem zich in een container bevindt?
Ik wed dat de meeste mensen de laatste optie willen, dat wil zeggen dat ze willen dat een CI-systeem, zoals Jenkins, containers kan draaien. En de eenvoudigste manier om dit te doen is door de Docker-socket in uw CI-container te steken, gekoppeld met de flag -v.
In simpele bewoordingen, wanneer u uw CI-container (Jenkins of een andere) opstart, begin het dan niet met het hacken van iets samen met Docker-in-Docker, maar start het met de volgende regel:
docker run -v /var/run/docker.sock:/var/run/docker.sock ...Nu heeft deze container toegang tot de Docker-socket en kan dus containers draaien. Behalve dat in plaats van 'kind' containers te draaien, het 'broederlijke' containers zal draaien.
Probeer dit met de officiƫle docker image (die het Docker-binaire bestand bevat):
docker run -v /var/run/docker.sock:/var/run/docker.sock
-ti dockerDit ziet eruit en werkt als Docker-in-Docker, maar het is geen Docker-in-Docker: wanneer deze container extra containers aanmaakt, worden die op een hoger niveau in Docker aangemaakt. U zult geen bijwerkingen van geneste containers ervaren en de build-cache zal gedeeld worden voor meerdere aanroepen.
Opmerking: eerdere versies van dit artikel adviseerden om het binaire bestand van Docker van de host naar de container te koppelen. Dit is nu onbetrouwbaar geworden, omdat de Docker-mechanisme niet langer van toepassing is op statische of bijna statische bibliotheken.
Dus, als u Docker uit Jenkins CI wilt gebruiken, hebt u 2 opties:
Installeer de Docker CLI met behulp van het basale image packaging systeem (d.w.z. als uw image op Debian is gebaseerd, gebruik .deb pakketten) of gebruik de Docker API.
Een beetje reclame š
Bedankt dat je bij ons blijft. Houd je van onze artikelen? Wil je meer interessante inhoud zien? Ondersteun ons door een bestelling te plaatsen of ons aan vrienden aan te bevelen, , een unieke variant van entry-level servers, die we voor jou hebben bedacht: (opties beschikbaar met RAID1 en RAID10, tot 24 cores en tot 40GB DDR4).
Dell R730xd is 2 keer goedkoper in datacenter Equinix Tier IV in Amsterdam? Alleen bij ons in Nederland! Dell R420 ā 2x E5-2430 2.2Ghz 6C 128GB DDR3 2x960GB SSD 1Gbps 100TB ā vanaf $99! Lees over hoe
Bron: habr.com
