Wie ich Docker in Docker ausgeführt habe und was dabei herausgekommen ist

Hallo alle! In seinem vorheriger ArtikelIch habe versprochen, über die Ausführung von Docker in Docker und die praktischen Aspekte der Verwendung dieser Lektion zu sprechen. Es ist Zeit, Ihr Versprechen zu halten. Ein erfahrener Entwickler wird wahrscheinlich einwenden, dass diejenigen, die Docker in Docker benötigen, einfach den Docker-Daemon-Socket vom Host in den Container weiterleiten und dies in 99 % der Fälle ausreichen wird. Aber beeilen Sie sich nicht, mich mit Keksen zu bewerfen, denn wir werden darüber sprechen, wie Docker tatsächlich in Docker ausgeführt wird. Für diese Lösung gibt es viele Anwendungsmöglichkeiten, und in diesem Artikel geht es um eine davon. Lehnen Sie sich also zurück und strecken Sie Ihre Arme vor sich aus.

Wie ich Docker in Docker ausgeführt habe und was dabei herausgekommen ist

Hauptseite

Alles begann an einem regnerischen Septemberabend, als ich die Maschine säuberte, die ich für 5 US-Dollar bei Digital Ocean gemietet hatte. Sie war eingefroren, weil Docker alle 24 Gigabyte verfügbaren Speicherplatz mit seinen Bildern und Containern gefüllt hatte. Die Ironie bestand darin, dass all diese Bilder und Container vorübergehend waren und nur dazu benötigt wurden, die Leistung meiner Anwendung jedes Mal zu testen, wenn eine neue Version einer Bibliothek oder eines Frameworks veröffentlicht wurde. Ich habe versucht, Shell-Skripte zu schreiben und einen Cron-Zeitplan einzurichten, um den Müll zu bereinigen, aber es hat nicht geholfen: Jedes Mal endete es unweigerlich damit, dass der Speicherplatz meines Servers aufgebraucht wurde und der Server (bestenfalls) hängen blieb. Irgendwann stieß ich auf einen Artikel darüber, wie man Jenkins in einem Container ausführt und wie es Build-Pipelines über einen dorthin weitergeleiteten Docker-Daemon-Socket erstellen und löschen kann. Die Idee gefiel mir, aber ich beschloss, noch einen Schritt weiter zu gehen und zu experimentieren, indem ich Docker direkt in Docker ausführte. Damals schien es mir eine völlig logische Lösung zu sein, Docker-Images herunterzuladen und Container für alle Anwendungen zu erstellen, die ich zum Testen in einem anderen Container (nennen wir es einen Staging-Container) benötigte. Die Idee bestand darin, einen Staging-Container mit dem Flag -rm zu starten, das beim Stoppen automatisch den gesamten Container und seinen gesamten Inhalt löscht. Ich habe am Docker-Image von Docker selbst herumgebastelt (https://hub.docker.com/_/docker), aber es erwies sich als zu umständlich und ich schaffte es nie, es so zum Laufen zu bringen, wie ich es brauchte, und ich wollte den ganzen Weg selbst gehen.

Üben. Kegel

Ich machte mich daran, den Behälter so zu gestalten, wie ich es brauchte, und setzte meine Experimente fort, was zu einer Vielzahl von Knospen führte. Das Ergebnis meiner Selbstquälerei war der folgende Algorithmus:

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

    docker run --privileged -it docker:18.09.6

    Achten Sie auf die Version des Behälters, machen Sie einen Schritt nach rechts oder nach links und Ihr DinD verwandelt sich in einen Kürbis. Tatsächlich kommt es recht häufig zu Problemen, wenn eine neue Version veröffentlicht wird.
    Wir müssen sofort in die Hülle eindringen.

  2. Wir versuchen herauszufinden, welche Container ausgeführt werden (Antwort: keine), aber führen wir den Befehl trotzdem aus:

    docker ps

    Sie werden ein wenig überrascht sein, aber es stellt sich heraus, dass der Docker-Daemon noch 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 es uns selbst ausführen:

    dockerd &

    Noch eine unangenehme Überraschung:

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

  4. Installieren Sie die Pakete iptables und bash (alles ist in Bash angenehmer zu arbeiten als in SH):

    apk add --no-cache iptables bash

  5. Lasst uns Bash starten. Endlich sind wir wieder in der gewohnten Hülle

  6. Versuchen wir, Docker erneut zu starten:

    dockerd &

    Wir sollten ein langes Protokollblatt sehen, das mit Folgendem endet:

    INFO[2019-11-25T19:51:19.448080400Z] Daemon has completed initialization          
    INFO[2019-11-25T19:51:19.474439300Z] API listen on /var/run/docker.sock

  7. Drücken Sie Enter. Wir sind zurück in der Party.

Von nun an können wir versuchen, andere Container in unserem Docker-Container zu starten, aber was ist, wenn wir einen anderen Docker-Container in unserem Docker-Container starten möchten oder etwas schief geht und der Container abstürzt? Das ganze nochmal von vorne.

Eigener DinD-Container und neue Experimente

Wie ich Docker in Docker ausgeführt habe und was dabei herausgekommen ist
Um die oben genannten Schritte nicht immer wieder 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 innerhalb von Docker rekursiv auszuführen und abenteuerlichere Experimente durchzuführen.
Ich werde jetzt ein solches (erfolgreiches) Experiment mit der Ausführung von MySQL und Nodejs beschreiben.
Die Ungeduldigsten können hier sehen, wie es war

Also, fangen wir an:

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

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

    Wir gelangen in die Bash, von wo aus wir sofort mit dem Starten untergeordneter Container beginnen können.

  2. Starten Sie MySQL:

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

  3. Wir stellen auf die gleiche Weise eine Verbindung zur Datenbank her, wie wir eine lokale Verbindung herstellen würden. Sorgen wir dafür, dass alles funktioniert.

  4. Starten Sie den zweiten Container:

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

    Bitte beachten Sie, dass die Portzuordnung exakt sein wird 8080:8080, da wir Port 80 vom Host zum übergeordneten Container bereits dem Port 8080 zugeordnet haben.

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

In meinem Fall verlief das Experiment mit verschachtelten Docker-Containern durchaus positiv und ich werde das Projekt weiterentwickeln und für das Staging nutzen. Mir scheint, dass dies eine viel einfachere Lösung ist als Kubernetes und Jenkins X. Aber das ist meine subjektive Meinung.

Ich denke, das ist alles für den heutigen Artikel. Im nächsten Artikel werde ich Experimente mit der rekursiven Ausführung von Docker in Docker und dem Mounten von Verzeichnissen tief in verschachtelte Container ausführlicher beschreiben.

PS Wenn Sie dieses Projekt nützlich finden, geben Sie ihm bitte einen Stern auf GitHub, forken Sie es und erzählen Sie es Ihren Freunden.

Edit1 Fehler korrigiert, konzentriert auf 2 Videos

Source: habr.com

Kommentar hinzufügen