Container revolutionieren die Softwareentwicklung, indem sie Prozesse wie virtuelle Maschinen effizient simulieren. Doch was steckt hinter diesem faszinierenden Konzept? In einem Artikel erklärt Ken Muse die grundlegenden Schichten von Container-Images.
Container sind erstaunlich. Sie ermöglichen es einfachen Prozessen, wie virtuelle Maschinen zu agieren. Unter dieser Eleganz verbirgt sich eine Reihe von Mustern und Praktiken, die letztlich alles zum Funktionieren bringen. An der Wurzel des Designs stehen die Schichten (Layer). Schichten sind die grundlegende Methode zur Speicherung und Verteilung der Inhalte eines containerisierten Dateisystems. Das Design ist sowohl überraschend einfach als auch sehr leistungsstark.
Beim Erstellen eines Images verwenden Sie typischerweise eine Dockerfile, um die Inhalte des Containers zu definieren. Sie enthält eine Reihe von Befehlen wie:
FROM scratch
RUN echo "hello" > /work/message.txt
COPY content.txt /work/content.txt
RUN rm -rf /work/message.txt
Unter der Haube wird die Container-Engine diese Befehle in der Reihenfolge ausführen und für jeden einen Layer erstellen. Aber was passiert da wirklich? Am einfachsten ist es, sich jede Schicht als ein Verzeichnis vorzustellen, das alle modifizierten Dateien enthält.
FROM scratch bedeutet, dass dieser Container ohne Inhalte startet und dies die erste Schicht ist, die durch ein leeres Verzeichnis /img/layer1 repräsentiert werden kann.
Die zweite Schicht wird durch die Ausführung des nächsten Dockerfile-Befehls erstellt, der eine Datei in /work/message.txt schreibt. Diese Inhalte werden in /img/layer2/work/message.txt geschrieben.
Die dritte Schicht entsteht durch die Anwendung des nächsten Befehls, der die Datei content.txt vom Host in dieses Verzeichnis kopiert.
Um diese Layer zu teilen, ist der einfachste Ansatz, eine komprimierte .tar.gz für jedes Verzeichnis zu erstellen. Um die Gesamtdateigröße zu reduzieren, werden alle Dateien, die unveränderte Kopien von Daten aus einer vorherigen Schicht sind, entfernt. Ein "whiteout file" könnte als Platzhalter verwendet werden, um zu verdeutlichen, wann eine Datei gelöscht wurde.
Das Erstellen vieler Images auf diese Weise resultiert in vielen layer1-Verzeichnissen. Um sicherzustellen, dass der Name einzigartig ist, wird die komprimierte Datei auf der Grundlage eines Hashwerts des Inhalts benannt. Dies ähnelt der Funktionsweise von Git. Es identifiziert identische Inhalte und erkennt gleichzeitig jede Beschädigung der Dateien während des Downloads. Der Hashwert des Inhalts muss mit dem Dateinamen übereinstimmen, andernfalls ist die Datei beschädigt.
Um die Ergebnisse reproduzierbar zu machen, ist eine weitere Sache erforderlich - eine Datei, die erklärt, wie die Schichten angeordnet sind (ein Manifest). Das Manifest identifiziert, welche Dateien heruntergeladen werden und in welcher Reihenfolge sie entpackt werden müssen. Dies ermöglicht das Wiederherstellen der Verzeichnisstrukturen. Es bietet außerdem einen wichtigen Vorteil: Schichten können zwischen Bildern wiederverwendet und geteilt werden, was die lokalen Speicheranforderungen minimiert.
Wenn eine Container-Instanz läuft, benötigt sie ein Dateisystem, das bereitgestellt werden muss. In erster Linie muss ein Directory mit allen Dateien, die verfügbar sein müssen, erstellt werden. Im Wesentlichen umfasst der Prozess des Erstellens eines Snapshots das Herunterladen des Manifests und das Erstellen einer Liste von Schichten. Ein "diff applier" ist dafür verantwortlich, die Inhalte der komprimierten Schichtendatei zu entpacken und die Änderungen auf das aktive Snapshot anzuwenden.