Das Mysterium Container-Image demystified

  1. Grundlagen
  2. Dive into Image
  3. Proof it

 „Jede hinreichend fortschrittliche Technologie ist von Magie nicht zu unterscheiden.“
 – Arthur C. Clarke

Nicht erst seit Docker und Kubernetes gibt es Container. Aber sicherlich wurden sie durch diese Anwendungen einer breiten Öffentlichkeit bekannt und auch deutlich zugänglicher. Wie man ein Image für einen Container baut und auch wie er zur Laufzeit funktioniert wurde indes oft erörtert; doch wie steht es um den Zustand dazwischen? Um Container-Images in unserer täglichen Arbeit bestmöglich nutzen und optimieren zu können, kommen wir nicht umhin zu verstehen, wie sie aufgebaut sind. So beleuchtet der folgende Artikel – garniert mit einem eigens handgefertigten Image – das Konzept der OCI-Images.

1. Grundlagen

Grundsätzlich kann man sagen, dass Container Sandboxing-Fähigkeiten des (Linux)-Kernels zugänglich machen. Unter der Haube bedienen sie sich unter Linux dafür der „control groups“, kurz cgroups. Sie dienen dazu, Prozesse [nach Containern] zu gruppieren und so Ressourcen-Verbrauch einteilen, überwachen und beschränken zu können. Zur Isolation der Container kommen Linux-Namespaces zum Einsatz. Diese trennen die Prozesse in eigene Namensräume, sodass sich die Prozesse nicht gegenseitig sehen oder direkt miteinander interagieren können. Einem Namespace ist so beispielsweise nicht einmal bekannt, dass er in einem Namespace ist und dass es evtl. noch weitere Prozesse außerhalb gibt.

Da wir ITler unsere Zeit an der Kaffeemaschine optimieren wollen, ist uns der händische Einsatz von cgroups und Namespaces zu aufwändig. So kommt uns als Lösung als Abstraktionsschicht eine Laufzeitumgebung, wie containerd (docker) oder cri-o (podman) zum Einsatz. Mehr über Docker Alternativen finden Sie in unserem gleichnamigen Artikel.

Container-Images bündeln für uns alle wichtigen Informationen für unsere Container, wie Dateisystem, user und Start-Kommando. Zum Start des Containers, stellt das Image so der Laufzeitumgebung die notwendigen Informationen bereit. Diese wiederum erstellt, dann cgroups und namespaces und erleichtert uns so das Management deutlich.

2. Dive into Image

Container bieten uns also die Möglichkeit unsere Software isoliert zu betreiben. Und ein Image bietet die Blaupause mit allen notwendigen Informationen. Aber wie kann man sich diese Infos denn anschauen? Immerhin leben die Images für die meisten Anwender hinter dem Befehl `docker images`. Mit `docker save -o out.tgz <img>` lässt sich das gewünschte Image aus dem Docker-internen Storage ins Dateisystem speichern.
Offensichtlich handelt es sich bei einem Image tatsächlich einfach um einen Tarball, in dem Dateien abgelegt sind. Nicht mehr, nicht weniger (und bestimmt keine Magie).

In diesem Tarball finden sich verschiedene Dateien mit verschiedenen Aufgaben. Die grundlegenden Informationen finden sich in der Datei `manifest.json`. Sie teilt uns mit, wo im Tarball wir die Konfiguration finden, wo und in welcher Reihenfolgen die Layer für unser Dateisystem liegen und wie diese zu schichten sind. Im Fall von Docker findet man hier auch das Repository Tag. Die im Manifest angegebene Config-Datei beinhaltet Informationen wie den User, mit dem der Container betrieben werden soll, die Environment-Variablen, das Start-Kommando oder für welches Betriebssystem und welche Prozessor-Architektur das Image gebaut ist.

Während sich alle Konfigurations-Informationen in einer Datei befinden, ist unser Dateisystem im Container meist in mehrere Schichten aufgeteilt. Diese Schichten sind zur Laufzeit nicht relevant, sondern dienen primär dazu, bei der Speicherung mehrerer Container mit einem ähnlichen Bauprozess gleiche Informationen nur einmal speichern zu müssen. So können sich mehrere Images in einer Registry denselben Layer „teilen“.
Unmittelbar vor dem Start des Containers werden die Image-Layer in der Reihenfolge, in der sie im Manifest aufgeführt sind auf das Dateisystem des neuen Containers geschrieben. Somit gilt, dass bei gleichen Dateien spätere Layer die Dateien früherer Layer überschreiben. Wer sich tiefergehend mit dem Dateisystem eines Images befassen möchte, dem sei das Tool dive empfohlen.

3. Proof it

Das heißt, nun sollte man sich eigentlich auch ganz ohne Build-System einen Container zusammenschustern können.

Für diesen Versuch benötigt man erst einmal einen Arbeitsordner, `tiny-atix`.

Dieser bekommt eine config.json, manifest.json und repositories file und einen ./base/layer pfad.

tiny-atix                       < folder
├── base                   < folder
│   └── layer              < folder
│   └── hello-atix.txt  < file
├── config.json         < file
└── manifest.json     < file 

In unsere Dateien schreiben wir folgendes:

# manifest.json
[{
"Config": "config.json",
"RepoTags": ["atix:tiny"],
"Layers": ["base/layer.tar"]
}]

# config.json
{
"architecture": "amd64",
"os": "linux",
"rootfs": {
"type": "layers",
"diff_ids": ["sha256:5962285ecca4a05d873887b24651922e65d9d32f4902ed0c54ada32c26559e72"]
}
}

# hello-atix.txt
hello atix

Nun nur noch die Layer packen und wir haben unseren Container.

# layer packen
$ tar -C tiny-atix/base/layer -czvf tiny-atix/base/layer.tar .
# image packen
$ tar -C tiny-atix -czf atix.tar .

Der Spannende Moment ist das Laden des Images. Im Falle diesen Images ist das Starten nicht spannend, denn wir haben kein Start-Kommando angegeben und auch keine Binary, die ausgeführt werden kann.

# image laden $ docker load -i atix.tar # image anschauen $ docker images REPOSITORY      TAG      IMAGE ID      CREATED      SIZE atix      tiny      a308f37e9ac7      N/A      10B # image starten $ docker run atix:tiny docker: Error response from daemon: No command specified.

Selbstverständlich lässt sich nun weiter forschen, um Images und die gegebenen Möglichkeiten noch besser zu verstehen.
Der nächste Schritt wäre ein Start-Kommando und eine Binary, die uns unsere `hello-atix.txt` Datei ausgibt.
Am Besten kommt die über einen zweiten Layer in das Image. 😉

Doch für diesen Artikel soll es das nun gewesen sein.

Wer die Forschungsexpedition fortsetzen will, dem seien folgende Quellen und Tools ans Herz gelegt:
oci spezifikationen
dive

The following two tabs change content below.

Lukas Paluch

Neueste Artikel von Lukas Paluch (alle ansehen)

This post is also available in: English