Wie ich Docker innerhalb von Docker gestartet habe und was dabei herauskam

Hallo zusammen! In meinem vorherigen Artikel, ich versprach, über den Start von Docker in Docker und die praktischen Aspekte dieser Tätigkeit zu berichten. Es ist an der Zeit, mein Versprechen einzulösen. Ein erfahrener DevOps wird wahrscheinlich einwenden, dass diejenigen, die Docker innerhalb von Docker benötigen, einfach den Docker-Daemon-Socket vom Host in den Container weiterleiten, und das ausreichend ist in 99 % der Fälle. Aber beeilen Sie sich nicht, Kekse nach mir zu werfen, denn es geht hier um den tatsächlichen Start von Docker innerhalb von Docker. Diese Lösung hat viele mögliche Anwendungsmöglichkeiten und dieser Artikel behandelt eine davon, also machen Sie es sich bequem und strecken Sie Ihre Hände vor sich aus.

Wie ich Docker innerhalb von Docker gestartet habe und was dabei herauskam

Einleitung

Alles begann an einem regnerischen Septemberabend, als ich eine für 5 $ gemietete Maschine bei Digital Ocean reinigte, die aufgrund der Überflutung mit Docker-Images und -Containern den gesamten verfügbaren Speicherplatz von 24 Gigabyte blockierte. Die Ironie war, dass all diese Images und Container transient waren und nur benötigt wurden, um die Funktionsfähigkeit meiner Anwendung zu testen, jedes Mal wenn eine neue Version einer Bibliothek oder eines Frameworks herauskam. Ich versuchte, Shell-Skripte zu schreiben und einen Cron-Plan für die Bereinigung von Müll einzurichten, aber das half nicht: jedes Mal endete es unvermeidlich damit, dass der Speicherplatz meines Servers aufgezehrt wurde und der Server (im besten Fall) blockiert war. Irgendwann stieß ich auf einen Artikel darüber, wie man Jenkins in einem Container ausführt und wie er Build-Pipelines über den an ihn weitergeleiteten Docker-Daemon-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. Es schien mir logisch, die Docker-Images herunterzuladen und Container aller Anwendungen zu erstellen, die ich für Tests benötigte, innerhalb eines anderen Containers (lassen Sie uns diesen Staging-Container nennen). Die Idee war, den Staging-Container mit dem Flag -rm zu starten, das automatisch den gesamten Container mit seinem gesamten Inhalt beim Stoppen löscht. Ich recherchierte mit dem Docker-Image von Docker selbst, aber es stellte sich als zu sperrig heraus und ich konnte es nicht dazu bringen, so zu funktionieren, wie ich es brauchte, und ich wollte den ganzen Weg selbst gehen.https://hub.docker.com/_/docker), aber es war zu klobig und ich konnte es nicht dazu bringen, so zu arbeiten, wie ich es brauchte, und ich wollte den gesamten Weg selbst gehen.

Praxis. Rückschläge

Ich hatte mir das Ziel gesetzt, den Container so zum Arbeiten zu bringen, wie ich es wollte, und führte weiterhin meine Experimente durch, deren Ergebnis eine unzählbare Menge an Problemen war. Das Ergebnis meiner Selbstqualifizierung war der folgende Algorithmus:

  1. Starten Sie 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 DinD verwandelt sich in einen Kürbis. Tatsächlich bricht alles ziemlich oft mit der Veröffentlichung einer neuen Version zusammen.
    Wir müssen sofort in die Shell gelangen.

  2. Versuchen wir herauszufinden, welche Container aktiv sind (Antwort: keine), aber lassen Sie uns den Befehl trotzdem ausführen:

    docker ps

    Sie werden ein wenig überrascht sein, aber es stellt sich heraus, dass der Docker-Daemon nicht einmal läuft:

    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 &

    Noch eine unangenehme Überraschung:

    failed to start daemon: Error initializing network controller: error obtaining controller instance: failed 
    to create NAT chain DOCKER: Iptables nicht gefunden

  4. Wir installieren die Pakete iptables und bash (in bash lässt es sich angenehmer arbeiten als in sh):

    apk add --no-cache iptables bash

  5. Starten Sie bash. Endlich sind wir wieder in der gewohnten Shell.

  6. Wir versuchen, Docker ein weiteres Mal zu 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. Wir drücken Enter. Wir sind wieder in bash.

Ab diesem Moment können wir versuchen, andere Container innerhalb unseres Docker-Containers zu starten, aber was, wenn wir noch einen Docker-Container innerhalb unseres Docker-Containers starten möchten oder wenn etwas schiefgeht und der Container 'ausfällt'? Alles von vorne beginnen.

Eigener DinD-Container und neue Experimente

Wie ich Docker innerhalb von Docker gestartet habe 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 erlaubte es mir, Docker rekursiv innerhalb von Docker zu starten und gewagtere Experimente durchzuführen.
Ein solches (gelungenes) Experiment mit der Ausführung von MySQL und Node.js werde ich jetzt beschreiben.
Die Ungeduldigen können sehen, wie es war, hier.

Video abspielen

Also, fangen wir an:

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

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

    Wir gelangen in den Bash, von wo aus wir sofort mit dem Starten der untergeordneten Container beginnen können.

  2. Wir starten MySQL:

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

  3. Wir verbinden uns mit der Datenbank, als würden wir uns lokal verbinden. 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 Port 80 vom Host in den übergeordneten Container auf Port 8080 gemappt haben.

  5. Gehen Sie im Browser zu localhost und stellen Sie sicher, dass der Server "Hello World!" antwortet.

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

Ich denke, das war alles für den heutigen Artikel. Im nächsten Artikel werde ich ausführlicher über Experimente mit rekursivem Docker-in-Docker-Start und das Einbinden von Verzeichnissen in verschachtelte Container berichten.

P.S. Wenn Sie dieses Projekt nützlich finden, setzen Sie ihm bitte einen Stern auf GitHub, machen Sie einen Fork und erzählen Sie es Ihren Freunden.

Edit1 Ich habe Fehler korrigiert und auf 2 Videos fokussiert.

Quelle: habr.com

60GB SSD 8Gb DDR4