Überlegen Sie sorgfältig, bevor Sie Docker-in-Docker für CI oder Testumgebungen verwenden

Überlegen Sie sorgfältig, bevor Sie Docker-in-Docker für CI oder Testumgebungen verwenden

Docker-in-Docker ist eine virtualisierte Docker-Daemon-Umgebung, die im Container selbst ausgeführt wird, um Container-Images zu erstellen. Der Hauptzweck der Entwicklung von Docker-in-Docker bestand darin, die Entwicklung von Docker selbst zu unterstützen. Viele Leute verwenden es, um Jenkins CI auszuführen. Das erscheint zunächst normal, doch dann treten Probleme auf, die durch die Installation von Docker in einem Jenkins-CI-Container vermieden werden können. In diesem Artikel erfahren Sie, wie das geht. Wenn Sie an der endgültigen Lösung ohne Details interessiert sind, lesen Sie einfach den letzten Abschnitt des Artikels „Das Problem lösen“.

Überlegen Sie sorgfältig, bevor Sie Docker-in-Docker für CI oder Testumgebungen verwenden

Docker-in-Docker: „Gut“

Vor mehr als zwei Jahren habe ich Docker installiert Flagge –privilegiert und schrieb erste Version von Dind. Ziel war es, dem Kernteam dabei zu helfen, Docker schneller zu entwickeln. Vor Docker-in-Docker sah der typische Entwicklungszyklus so aus:

  • Hackity-Hack;
  • bauen;
  • Stoppen eines laufenden Docker-Daemons;
  • Starten eines neuen Docker-Daemons;
  • Testen
  • Wiederholen Sie den Zyklus.

Wenn Sie eine schöne, reproduzierbare Baugruppe erstellen wollten (also in einem Container), dann wurde es komplizierter:

  • Hackity-Hack;
  • Stellen Sie sicher, dass eine funktionierende Version von Docker ausgeführt wird.
  • Erstellen Sie einen neuen Docker mit dem alten Docker.
  • Stoppen Sie den Docker-Daemon.
  • Starten Sie einen neuen Docker-Daemon.
  • prüfen;
  • Stoppen Sie den neuen Docker-Daemon.
  • wiederholen.

Mit der Einführung von Docker-in-Docker ist der Prozess einfacher geworden:

  • Hackity-Hack;
  • Montage + Start in einer Phase;
  • Wiederholen Sie den Zyklus.

Ist es so nicht viel besser?

Überlegen Sie sorgfältig, bevor Sie Docker-in-Docker für CI oder Testumgebungen verwenden

Docker-in-Docker: „Schlecht“

Entgegen der landläufigen Meinung besteht Docker-in-Docker jedoch nicht zu 100 % aus Sternen, Ponys und Einhörnern. Ich meine damit, dass es mehrere Probleme gibt, die ein Entwickler beachten muss.

Eines davon betrifft LSMs (Linux-Sicherheitsmodule) wie AppArmor und SELinux: Beim Ausführen eines Containers versucht der „interne Docker“ möglicherweise, Sicherheitsprofile anzuwenden, die den „externen Docker“ in Konflikt bringen oder verwirren. Dies ist das am schwierigsten zu lösende Problem, wenn versucht wird, die ursprüngliche Implementierung des Flags –privileged zusammenzuführen. Meine Änderungen haben funktioniert und alle Tests haben auf meinem Debian-Rechner und den Ubuntu-Test-VMs bestanden, aber auf Michael Crosbys Rechner (er hatte Fedora, soweit ich mich erinnere) stürzten sie ab und brannten. Ich kann mich nicht an die genaue Ursache des Problems erinnern, aber es könnte daran liegen, dass Mike ein kluger Kerl ist, der mit SELINUX=enforce arbeitet (ich habe AppArmor verwendet) und meine Änderungen SELinux-Profile nicht berücksichtigt haben.

Docker-in-Docker: „Böse“

Das zweite Problem betrifft die Docker-Speichertreiber. Wenn Sie Docker-in-Docker ausführen, läuft der externe Docker auf einem regulären Dateisystem (EXT4, BTRFS oder was auch immer Sie haben) und der interne Docker auf einem Copy-on-Write-System (AUFS, BTRFS, Device Mapper). usw.). , je nachdem, was für die Verwendung von externem Docker konfiguriert ist). Dadurch entstehen viele Kombinationen, die nicht funktionieren. Beispielsweise können Sie AUFS nicht zusätzlich zu AUFS ausführen.

Wenn Sie BTRFS zusätzlich zu BTRFS ausführen, sollte es zunächst funktionieren, aber sobald es verschachtelte Subvolumes gibt, schlägt das Löschen des übergeordneten Subvolumes fehl. Das Device Mapper-Modul hat keinen Namensraum. Wenn es also von mehreren Docker-Instanzen auf demselben Computer ausgeführt wird, können alle die Images untereinander und auf den Container-Backup-Geräten sehen (und beeinflussen). Das ist schlecht.

Für viele dieser Probleme gibt es Workarounds. Wenn Sie beispielsweise AUFS im internen Docker verwenden möchten, wandeln Sie einfach den Ordner /var/lib/docker in ein Volume um, und schon ist alles in Ordnung. Docker hat den Device Mapper-Zielnamen einige Basis-Namespaces hinzugefügt, sodass sich diese nicht gegenseitig beeinträchtigen, wenn mehrere Docker-Aufrufe auf demselben Computer ausgeführt werden.

Allerdings ist ein solcher Aufbau gar nicht so einfach, wie man hier sehen kann Artikel im dind-Repository auf GitHub.

Docker-in-Docker: Es kommt noch schlimmer

Was ist mit dem Build-Cache? Das kann auch ziemlich schwierig sein. Ich werde oft gefragt: „Wenn ich Docker-in-Docker verwende, wie kann ich dann auf meinem Host gehostete Bilder verwenden, anstatt alles zurück in meinen internen Docker zu ziehen?“

Einige unternehmungslustige Leute haben versucht, /var/lib/docker vom Host an einen Docker-in-Docker-Container zu binden. Manchmal teilen sie /var/lib/docker mit mehreren Containern.

Überlegen Sie sorgfältig, bevor Sie Docker-in-Docker für CI oder Testumgebungen verwenden
Möchten Sie Ihre Daten beschädigen? Denn genau das schadet Ihren Daten!

Der Docker-Daemon wurde eindeutig so konzipiert, dass er exklusiven Zugriff auf /var/lib/docker hat. Nichts anderes sollte die in diesem Ordner befindlichen Docker-Dateien „berühren, anstupsen oder anstoßen“.

Warum ist das so? Denn dies ist das Ergebnis einer der schwierigsten Lektionen, die wir bei der Entwicklung von dotCloud gelernt haben. Die dotCloud-Container-Engine wurde ausgeführt, indem mehrere Prozesse gleichzeitig auf /var/lib/dotcloud zugegriffen haben. Raffinierte Tricks wie die atomare Dateiersetzung (anstelle der In-Place-Bearbeitung), das Aufpeppen von Code mit beratenden und obligatorischen Sperren sowie andere Experimente mit sicheren Systemen wie SQLite und BDB funktionierten nicht immer. Als wir unsere Container-Engine neu gestalteten, aus der schließlich Docker wurde, bestand eine der großen Designentscheidungen darin, alle Containeroperationen unter einem einzigen Daemon zu konsolidieren, um den ganzen Parallelitäts-Unsinn zu beseitigen.

Verstehen Sie mich nicht falsch: Es ist durchaus möglich, mit mehreren Prozessen und moderner Parallelsteuerung etwas Gutes, Zuverlässiges und Schnelles zu schaffen. Aber wir glauben, dass es einfacher und einfacher ist, Code zu schreiben und zu verwalten, wenn man Docker als einzigen Player verwendet.

Das bedeutet, dass es zu Problemen kommt, wenn Sie das Verzeichnis /var/lib/docker zwischen mehreren Docker-Instanzen teilen. Dies kann natürlich funktionieren, insbesondere in den frühen Testphasen. „Hör zu, Ma, ich kann Ubuntu als Docker ausführen!“ Aber versuchen Sie es mit etwas Komplexerem, wie zum Beispiel dem Abrufen desselben Bildes aus zwei verschiedenen Instanzen, und Sie werden sehen, wie die Welt brennt.

Wenn Ihr CI-System also Builds und Rebuilds durchführt, riskieren Sie bei jedem Neustart Ihres Docker-in-Docker-Containers, einen Nuke in seinen Cache zu werfen. Das ist überhaupt nicht cool!

Die Lösung

Machen wir einen Schritt zurück. Benötigen Sie wirklich Docker-in-Docker oder möchten Sie einfach nur in der Lage sein, Docker auszuführen und Container und Images von Ihrem CI-System aus zu erstellen und auszuführen, während sich das CI-System selbst in einem Container befindet?

Ich wette, die meisten Leute wünschen sich die letztere Option, das heißt, sie möchten, dass ein CI-System wie Jenkins Container ausführen kann. Und der einfachste Weg, dies zu tun, besteht darin, einfach einen Docker-Socket in Ihren CI-Container einzufügen und ihn mit dem Flag -v zu verknüpfen.

Einfach ausgedrückt: Wenn Sie Ihren CI-Container (Jenkins oder andere) ausführen, beginnen Sie ihn mit der Zeile, anstatt etwas zusammen mit Docker-in-Docker zu hacken:

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

Dieser Container hat nun Zugriff auf den Docker-Socket und kann daher Container ausführen. Anstatt „untergeordnete“ Container auszuführen, werden jedoch „geschwisterliche“ Container gestartet.

Versuchen Sie dies mit dem offiziellen Docker-Image (das die Docker-Binärdatei enthält):

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

Es sieht aus und funktioniert wie Docker-in-Docker, ist aber kein Docker-in-Docker: Wenn dieser Container zusätzliche Container erstellt, werden diese im Docker der obersten Ebene erstellt. Die Nebenwirkungen der Verschachtelung treten nicht auf und der Assemblycache wird von mehreren Aufrufen gemeinsam genutzt.

Hinweis: In früheren Versionen dieses Artikels wurde empfohlen, die Docker-Binärdatei vom Host mit dem Container zu verknüpfen. Dies ist mittlerweile unzuverlässig geworden, da die Docker-Engine keine statischen oder nahezu statischen Bibliotheken mehr abdeckt.

Wenn Sie also Docker von Jenkins CI nutzen möchten, haben Sie zwei Möglichkeiten:
Installieren der Docker-CLI mithilfe des grundlegenden Image-Paketsystems (d. h. wenn Ihr Image auf Debian basiert, verwenden Sie .deb-Pakete) mithilfe der Docker-API.

Einige Anzeigen 🙂

Vielen Dank, dass Sie bei uns geblieben sind. Gefallen Ihnen unsere Artikel? Möchten Sie weitere interessante Inhalte sehen? Unterstützen Sie uns, indem Sie eine Bestellung aufgeben oder an Freunde weiterempfehlen. Cloud-VPS für Entwickler ab 4.99 $, ein einzigartiges Analogon von Einstiegsservern, das von uns für Sie erfunden wurde: Die ganze Wahrheit über VPS (KVM) E5-2697 v3 (6 Kerne) 10 GB DDR4 480 GB SSD 1 Gbit/s ab 19 $ oder wie teilt man sich einen Server? (verfügbar mit RAID1 und RAID10, bis zu 24 Kerne und bis zu 40 GB DDR4).

Dell R730xd 2-mal günstiger im Equinix Tier IV-Rechenzentrum in Amsterdam? Nur hier 2 x Intel TetraDeca-Core Xeon 2x E5-2697v3 2.6 GHz 14C 64 GB DDR4 4 x 960 GB SSD 1 Gbit/s 100 TV ab 199 $ in den Niederlanden! Dell R420 – 2x E5-2430 2.2 GHz 6C 128 GB DDR3 2 x 960 GB SSD 1 Gbit/s 100 TB – ab 99 $! Lesen über Wie baut man ein Infrastrukturunternehmen auf? Klasse mit dem Einsatz von Dell R730xd E5-2650 v4 Servern im Wert von 9000 Euro für einen Cent?

Source: habr.com

Kommentar hinzufügen