Wie ich Docker in Docker zum Laufen brachte und was dabei herauskam

Hallo zusammen! In meinem im vorherigen Artikel, habe ich versprochen, über das Ausführen von Docker in Docker und die praktischen Aspekte dieser Vorgehensweise zu berichten. Es ist an der Zeit, mein Versprechen einzulösen. Ein erfahrener DevOps-Engineer könnte einwenden, dass diejenigen, die Docker in Docker benötigen, einfach den Socket des Docker-Daemons vom Host ins Container weiterleiten und das in 99 % der Fälle ausreicht. Aber beeilt euch nicht, mich mit Cookies zu bewerfen, denn es geht um das tatsächliche Ausführen von Docker in Docker. Diese Lösung bietet viele mögliche Anwendungsgebiete, und dieses Artikel behandelt eines davon, also macht es euch bequem und haltet die Hände gerade vor euch.

Wie ich Docker in Docker zum Laufen brachte und was dabei herauskam

Anfang

Alles begann an einem regnerischen Septemberabend, als ich einen für 5 $ gemieteten Server bei Digital Ocean säuberte, der aufgrund der Überfüllung mit Docker-Images und -Containern mit 24 GB verfügbarem Speicherplatz völlig fest hing. Ironischerweise waren all diese Images und Container transient und nur erforderlich, um die Funktionalität meiner Anwendung jedes Mal zu testen, wenn eine neue Version einer Bibliothek oder eines Frameworks veröffentlicht wurde. Ich versuchte, Shell-Skripte zu schreiben und Cron-Jobs zur Bereinigung von Müll einzurichten, aber das half nicht: Jedes Mal endete es unweigerlich damit, dass der Speicherplatz meines Servers verbraucht war und der Server (im besten Fall) festhing. Irgendwann stieß ich auf einen Artikel darüber, wie man Jenkins in einem Container ausführt und wie es Build-Pipelines über einen an den Docker-Daemon weitergeleiteten Socket erstellen und löschen kann. Die Idee gefiel mir, aber ich beschloss, weiterzugehen und mit dem direkten Start von Docker innerhalb von Docker zu experimentieren. Damals schien es mir logisch, Docker-Images herunterzuladen und Container für all die Anwendungen zu erstellen, die ich zum Testen benötigte, innerhalb eines anderen Containers (nennen wir ihn Staging-Container). Die Idee bestand darin, den Staging-Container mit dem Flag -rm zu starten, das alle Containerinhalte automatisch beim Stoppen löscht. Ich durchstöberte das Docker-Image von Docker selbst ("https://hub.docker.com/_/docker), aber es stellte sich als zu umständlich heraus und ich konnte es einfach nicht zum Laufen bringen, wie ich es wollte. Ich wollte den gesamten Prozess selbst durchlaufen.

Praxis. Erfahrungen

Ich hatte mir das Ziel gesetzt, den Container so zum Laufen zu bringen, wie ich es brauchte, und setzte meine Experimente fort, deren Folge eine Vielzahl an Erfahrungen war. Das Ergebnis meines Selbstexperiments war der folgende Algorithmus:

  1. Wir starten den Docker-Container im interaktiven Modus.

    docker run --privileged -it docker:18.09.6

    Beachten Sie die Version des Containers; ein Schritt nach rechts oder links und Ihr Docker-In-Docker wird zum Kürbis. Tatsächlich bricht es ziemlich oft mit neuen Versionen.
    Wir müssen sofort in die Shell gelangen.

  2. Lassen Sie uns prüfen, welche Container laufen (Antwort: keine), aber führen wir trotzdem den Befehl aus:

    docker ps

    Sie werden ein wenig überrascht sein, aber der Docker-Daemon läuft anscheinend nicht einmal:

    error during connect: Get http://docker:2375/v1.40/containers/json: dial tcp: lookup docker on 
    192.168.65.1:53: no such host

  3. Lassen Sie uns ihn selbst starten:

    dockerd &

    Eine weitere unangenehme Überraschung:

    failed to start daemon: Fehler bei der Initialisierung des Netzwerkcontrollers: Fehler beim Abrufen der Controller-Instanz: konnte NAT-Kette DOCKER nicht erstellen: Iptables nicht gefunden

  4. Wir installieren die Pakete iptables und bash (mit bash arbeitet es sich einfach besser als mit sh):

    apk add --no-cache iptables bash

  5. Wir starten bash. Endlich sind wir wieder in unserer gewohnten Shell.

  6. Lass uns Docker noch einmal starten:

    dockerd &

    Wir sollten eine lange Liste von Logs sehen, die endet mit:

    INFO[2019-11-25T19:51:19.448080400Z] Daemon hat die Initialisierung abgeschlossen          
    INFO[2019-11-25T19:51:19.474439300Z] API hört auf /var/run/docker.sock

  7. Drücke Enter. Wir sind wieder in bash.

Ab diesem Moment können wir versuchen, weitere Container innerhalb unseres Docker-Containers zu starten, aber was ist, wenn wir einen weiteren Docker-Container innerhalb unseres Docker-Containers starten wollen oder wenn etwas schiefgeht und der Container „abstürzt“? Alles von vorne beginnen.

Eigenes DinD-Container und neue Experimente

Wie ich Docker in Docker zum Laufen brachte und was dabei herauskam
Um die oben beschriebenen Schritte nicht immer wiederholen zu müssen, habe ich meinen eigenen DinD-Container erstellt:

https://github.com/alekslitvinenk/dind

Die funktionierende DinD-Lösung gab mir die Möglichkeit, Docker rekursiv innerhalb von Docker auszuführen und mutigere Experimente durchzuführen.
Einen solchen (erfolgreichen) Versuch, MySQL und Node.js zu starten, werde ich jetzt beschreiben.
Die Ungeduldigen können sich ansehen, wie es hier war.

Video abspielen

Also, lass uns anfangen:

  1. Wir starten DinD im interaktiven Modus. In dieser Version von DinD müssen wir manuell alle Ports, die von unseren untergeordneten Containern verwendet werden können, zuordnen (ich arbeite bereits daran).

    docker run --privileged -it 
    -p 80:8080 
    -p 3306:3306 alekslitvinenk/dind

    Wir landen in der Shell, von wo aus wir sofort mit dem Starten der untergeordneten Container beginnen können.

  2. MySQL starten:

    docker run --name mysql -e MYSQL_ROOT_PASSWORD=strongpassword -d -p 3306:3306 mysql

  3. Wir verbinden uns mit der Datenbank, so wie wir uns lokal verbinden würden. Wir stellen sicher, dass alles funktioniert.

  4. Wir starten den zweiten Container:

    docker run -d --rm -p 8080:8080 alekslitvinenk/hello-world-nodejs-server

    Bitte beachten Sie, dass das Port-Mapping hier genau 8080:8080ist, da wir bereits den Port 80 vom Host auf den Port 8080 im übergeordneten Container gemappt haben.

  5. Wir gehen zu localhost im Browser und stellen sicher, dass der Server mit «Hello World!» antwortet.

In meinem Fall war das Experiment mit geschachtelten Docker-Containern ziemlich positiv, und ich werde das Projekt weiterentwickeln und für das Staging nutzen. Ich denke, dass dies eine viel leichtere Lösung ist als Kubernetes und Jenkins X. Aber das ist nur meine subjektive Meinung.

Ich denke, das war's für den heutigen Artikel. Im nächsten Artikel werde ich ausführlicher über Experimente mit dem rekursiven Start von Docker in Docker und das Einbinden von Verzeichnissen in tiefere Container berichten.

P.S. Wenn Sie dieses Projekt nützlich finden, setzen Sie bitte einen Stern auf GitHub, fork it und erzählen Sie Ihren Freunden davon.

Edit1 Ich habe die Fehler korrigiert und den Fokus auf 2 Videos gelegt.

Quelle: habr.com

Zuverlässiges Webhosting mit DDoS-Schutz, VPS- und VDS-Server kaufen 🔥 Zuverlässiges Webhosting mit DDoS-Schutz, VPS- und VDS-Server kaufen | ProHoster