Aufbau von Docker Container-Images: Union Filesystem und Speicherung erklärt

Docker ist eine weit verbreitete Plattform, um Anwendungen mithilfe von Containern zu isolieren und zu betreiben. Ein wichtiger Bestandteil von Docker sind die sogenannten Container-Images, die die Blaupause für die Ausführung der Container darstellen. In diesem Blog-Beitrag werfen wir einen genaueren Blick auf den Aufbau von Docker-Images, das zugrundeliegende Union-Filesystem sowie die Speicherung und Verwaltung von Images.

Was ist ein Docker-Image?

Ein Docker-Image ist eine unveränderliche Vorlage, die alle notwendigen Komponenten enthält, um eine Anwendung auszuführen. Dazu gehören unter anderem:

  • Der Anwendungscode
  • Systembibliotheken
  • Abhängigkeiten (Dependencies)
  • Konfigurationseinstellungen

Im Grunde genommen stellt ein Docker-Image ein vorbereitetes Dateisystem dar, das bei der Erstellung eines Containers verwendet wird. Container-Images basieren auf Schichten (Layers), die übereinander gestapelt werden, um die fertige Umgebung für die Ausführung der Anwendung zu bilden.

Aufbau von Docker-Images: Das Layering-Prinzip

Docker-Images bestehen aus mehreren Schichten, die das sogenannte Union-Filesystem verwenden. Jede Schicht enthält eine Änderung gegenüber der vorherigen. Dieser schichtbasierte Ansatz bringt mehrere Vorteile mit sich, insbesondere die Wiederverwendbarkeit und Effizienz bei der Speicherung.

1. Schichten (Layers)

Jede Schicht eines Docker-Images stellt eine Änderung oder ein Update am Dateisystem dar. Zum Beispiel:

  • Die erste Schicht kann ein Basis-Betriebssystem wie Ubuntu oder Alpine Linux enthalten.
  • Eine zweite Schicht könnte zusätzliche Bibliotheken oder Tools installieren (z. B. apt-get install in einem Dockerfile).
  • Eine dritte Schicht könnte den Anwendungscode selbst hinzufügen.

Docker speichert jede Schicht als Delta, d. h. nur die Unterschiede oder Änderungen im Vergleich zur vorherigen Schicht. Dadurch können identische Schichten von verschiedenen Images wiederverwendet werden, was Speicherplatz spart.

2. Union-Filesystem

Das Union-Filesystem ist eine besondere Art von Dateisystem, das Docker verwendet, um mehrere Schichten zu einem einheitlichen Dateisystem zusammenzuführen. Wenn ein Container gestartet wird, erscheinen die verschiedenen Schichten so, als ob sie ein einzelnes Dateisystem bilden würden.

Das Union-Filesystem ermöglicht:

  • Effizienz: Änderungen werden nicht dupliziert, sondern in separaten Schichten gespeichert.
  • Schnelligkeit: Das System greift direkt auf die vorhandenen Schichten zu, was die Startzeit von Containern beschleunigt.
  • Schreibgeschützte Schichten: Die unteren Schichten eines Docker-Images sind schreibgeschützt. Das bedeutet, dass beim Starten eines Containers nur die oberste Schicht beschreibbar ist. So werden die Basisimages nicht verändert, sondern Container arbeiten auf einer separaten „Overlay“-Schicht.

Speicherung von Docker-Images

Docker-Images werden in einem Image-Cache auf dem Host-System gespeichert. Dieser Cache befindet sich standardmäßig im Verzeichnis /var/lib/docker. Jedes Image besteht aus seinen verschiedenen Schichten, die zusammen das gesamte Image darstellen.

1. Layer Caching

Docker verwendet ein ausgeklügeltes Caching-System, um Speicherplatz und Netzwerklast zu reduzieren. Wenn mehrere Container auf demselben Image basieren, lädt Docker das Basisimage nur einmal herunter und verwendet es für alle Container.

  • Gemeinsame Schichten: Da viele Images auf denselben Basisimages (wie z. B. alpine, ubuntu oder nginx) aufbauen, muss Docker diese Basis-Schichten nicht mehrfach speichern, sondern kann sie für verschiedene Images wiederverwenden.
  • Layer-Wiederverwendung beim Bauen: Auch beim Erstellen neuer Images werden vorhandene Schichten wiederverwendet. Wenn sich der Inhalt einer Schicht nicht ändert, wird sie aus dem Cache übernommen, was die Image-Build-Zeit stark verkürzt.

2. Image-Kompression und Distribution

Wenn Docker-Images gespeichert und über ein Netzwerk (z. B. Docker Hub oder private Registries) verteilt werden, komprimiert Docker die Images automatisch. Dies reduziert die Größe der Dateien und beschleunigt den Transfer.

Docker-Images werden als TAR-Archive gespeichert und über Registries verteilt. Eine Docker-Registry wie Docker Hub dient als zentrales Repository, in dem Entwickler ihre Images hochladen und mit anderen teilen können.

Erstellen eines Docker-Images: Ein Beispiel

Um ein besseres Verständnis für den Aufbau eines Docker-Images zu erhalten, schauen wir uns ein einfaches Beispiel an, bei dem ein Dockerfile verwendet wird, um ein neues Image zu erstellen.

# Basisimage verwenden
FROM ubuntu:20.04

# Umgebungsvariable setzen
ENV DEBIAN_FRONTEND=noninteractive

# Updates und notwendige Pakete installieren
RUN apt-get update && apt-get install -y \
    curl \
    vim

# Anwendungscode hinzufügen
COPY ./app /usr/src/app

# Startbefehl definieren
CMD ["bash"]

Erklärung des Dockerfiles:

  1. Basisimage: Das FROM-Kommando lädt ein Basisimage von Ubuntu 20.04 herunter.
  2. Umgebungsvariablen: Mit ENV wird eine Umgebungsvariable festgelegt.
  3. Schicht für Pakete: Der RUN-Befehl führt eine Paketinstallation durch, was eine neue Schicht im Image erzeugt.
  4. Anwendungscode: COPY fügt Dateien von Ihrem lokalen Verzeichnis in das Image ein, was ebenfalls eine neue Schicht erzeugt.
  5. Startbefehl: Der CMD-Befehl definiert, welches Kommando beim Starten des Containers ausgeführt werden soll.

Jede dieser Anweisungen erzeugt eine neue Schicht im Docker-Image. Wenn Sie das Dockerfile ändern und erneut ein Image erstellen, wird nur die geänderte Schicht neu gebaut. Die vorherigen Schichten bleiben im Cache.

Speicherung und Optimierung von Docker-Images

Docker-Images können mit der Zeit groß werden, insbesondere wenn sie viele Abhängigkeiten und Schichten enthalten. Es gibt verschiedene Strategien, um die Größe der Images zu optimieren:

  1. Alpine Linux verwenden: Alpine ist ein sehr minimalistisches Linux-Basisimage, das viel Speicherplatz spart. Anstatt ein großes Image wie ubuntu zu verwenden, können Sie alpine als Basis nehmen, was die Image-Größe stark reduziert.
  2. Mehrere Befehle in einem RUN-Kommando zusammenfassen: Jeder RUN-Befehl erzeugt eine neue Schicht. Durch das Zusammenfassen von Befehlen in einem einzigen RUN-Kommando (mit &&) wird die Anzahl der Schichten reduziert. Beispiel: DockerfileKopierenBearbeiten
RUN apt-get update && apt-get install -y \
    curl \
    vim \
    && apt-get clean
  1. Unnötige Dateien entfernen: Vermeiden Sie es, temporäre Dateien oder Build-Artefakte im Image zu speichern. Nutzen Sie --no-install-recommends, um nur die nötigsten Pakete zu installieren.
  2. Multistage-Builds: Mit Multistage-Builds können Sie Entwicklungs- und Produktionsumgebungen trennen, um am Ende nur das Nötigste im finalen Image zu haben.

Fazit

Docker-Images basieren auf einem schichtweisen Aufbau, der durch das Union-Filesystem ermöglicht wird. Jede Schicht enthält Änderungen gegenüber der vorherigen, was eine effiziente Speicherung und Verteilung von Images erlaubt. Das Caching und die Wiederverwendung von Schichten sorgen dafür, dass Docker-Images sowohl platzsparend als auch schnell erstellt werden können.

Durch die Verwendung von Basis-Images, Optimierungen im Dockerfile und effektive Speicherstrategien können Entwickler die Vorteile von Docker voll ausschöpfen und gleichzeitig die Größe und Komplexität der Images minimieren.


Hoffentlich hat Ihnen dieser Beitrag geholfen, die Funktionsweise von Docker-Images und deren Speicherung besser zu verstehen! Wenn Sie Fragen oder eigene Optimierungstipps haben, teilen Sie sie gerne in den Kommentaren.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert