Docker-Einführung, Teil 3 Container als Linux-Kernel-Feature
Um die Popularität und Bedeutung von Docker zu verstehen, sollte man sich mit den Prinzipien von Linux-Containern auseinandersetzen. Dies erleichtert das Verständnis der Funktionsweise von Docker.
Anbieter zum Thema

In Kürze wollen wir uns der Installation und Inbetriebnahme von Docker auf verschiedenen Plattformen sowie den verfügbaren Clients und wichtigsten Befehlen zuwenden. Doch vorher wollen wir zunächst noch einmal darauf eingehen, dass Linux-Container kein von Docker implementiertes Feature sind.
Linux Container gibt es wie im ersten Teil dieser Serie erläutert schon weitaus länger. Eine der ältesten Realisierungen von Linux Containern sind BSDs Jails (Gefängnisse). Auch hier ging es primär darum, einzelne Prozesse in „andere“ root.-Verzeichnisse zu verschieben. So wurde beispielsweise verhindert, dass bei einem gekaperten Apache-Webserver nicht das Betriebssystem kompromittiert wird, sondern lediglich ein relativer Ordner.
Vorlage für die Idee ist das Unix-Kommando „change root“. Damit das zuverlässig funktioniert, müssen in solch einem relativen root-Folder selbstverständlich auch entsprechende Teilanwendungen und Abhängigkeiten bereitgestellt werden. Sie werden also sinngemäß mit in das Gefängnis eingesperrt.
Eine der ersten wirklich brauchbaren Implementierungen von Containern waren und sind Solaris-Zonen (Zones). Mit diesem Ansatz entschied sich der ehemalige Hersteller Sun (jetzt Oracle) seinerzeit bewusst gegen Virtualisierung auf Hardware-Ebene.
LXC und Docker
Im Gegensatz zu Docker lassen sich BSD Jails und Solaris Zones allerdings verhältnismäßig schwierig konfigurieren. Ferner mangelt es diesen Konzepten an Reproduzierbarkeit und entsprechenden Schnittstellen, wie LXC (Linux Containers) eine war und ist. Auch Docker nutzte bis zur Version 0.9 noch LXC als unabhängige Linux-Kernel-Schnittstelle, um die Kernel-Features von Linux-Container einzurichten.
Insofern war Docker ursprünglich eine Abstraktion über einer Schnittstelle zu speziellen Linux-Kernel-Features. Später hat Docker die Schnittstelle LXC durch eine eigene Implementierung namens „libcontainer“ ausgetauscht. Sie gilt heute als De-facto-Standard für diese Funktionalität, da sie auch von anderen Firmen wie Google oder Red Hat unterstützt wird.
Das libcontainer-Interface umfasst diverse Funktionalitäten, die von Docker wahrgenommen werden können. Docker selbst ist „nur“ ein Systemdienst, der auf höchster Ebene als root-Nutzer im Betriebssystem läuft und u. a über eine REST-API z. B. aus Webanwendungen heraus oder vom mitgelieferten Kommandozeile-Client bzw. bei Bedarf vom graphischen Client „Kitematic“ aus angesprochen wird.
In der vorangestellten Abbildung erzeugen und starten wir einen neuen Busybox-Container auf einem Windows-10-basierten Container-Hosts im Kitematic-Client im interaktiven Modus, weshalb automatisch eine Powershell geöffnet wird.
Klickt man auf „New“, landet man wieder in der Ansicht für Images und Repositories und kann weitere Container instanziieren bzw. besser „initialisieren“. Mehr zu Clients, Container-Hosts, Berechtigungen, Ports, Netzwerk und Storage sowie den Ausführungsmodi und vor allem zum automatisierten Bauen anhand von Dockerfiles in den kommenden Teilen.
Werfen wir zunächst noch einen tieferen Blick auf die Architektur, zunächst auf die Docker-Engine. Der Docker-Dienst greift auf die libcontainer-Bibliothek zurück und richtet je nach Konfiguration die entsprechenden Linux-Kernel-Features für die auszuführende Anwendung ein. Diese sind:
Control Groups: Unter Control Groups (kurz CGroups) versteht man unter Linux die Fähigkeit des Kernels, so genannte Ressource Limits zu definieren. Diese erlauben es, verschiedene Anwendungen zu limitieren und zwar in Form von Rechenzeit, die auf der CPU des Container-Hosts konsumiert werden darf. Man steuert so also indirekt der „Menge“ nutzbarer CPUs sowie der Speichermengen, die im Container genutzt werden können. Control Groups können aber auch viele andere I/O-affine Aspekte wie z. B. die Netzwerk-Nutzung steuern.
Namespaces: Der zweite wichtige Aspekt sind die so genannten Namespaces. Sie ermöglichen das eigentliche „Simulieren“ in sich geschlossener Umgebungen gegenüber der jeweiligen ausgeführten Anwendung. Somit wird der Anwendung nur derjenige Ausschnitt einer Umgebung „präsentiert“, der sie selbst betrifft.
Für die Anwendung sieht es so aus, als gäbe es tatsächlich eigene Mountpunkte, einen eigenen Hostnamen oder eigene virtuelle Netzwerkkarten und andere Geräte. Sichtbar sind dann immer nur genau jene Prozesse, die dem jeweiligen Container gehören“. Namespaces ermöglichen auch die Existenz eigene Nutzer, die unabhängig von Host-Betriebssystem sichtbar sind.
Capabilities: Ein weiteres wichtiges Linux-Kernel-Feature im Zusammenhang mit Container-Technik. Prinzipiell läuft jeder Linux-Container als Root-Nutzer. Mittels Capabilities lassen sich aber entsprechende „Fähigkeiten“ von den gestarteten Anwendungen „entfernen“ und nur on demand zuweisen, sodass eine Container-Anwendung, auch wenn sie mit Root-Rechten läuft, z. B. keine administrativen Tasks im Netzwerk ausführen oder den Container-Host herunterfahren kann.
Je nach verwendeter Linux-Distribution gibt es zusätzlich noch die Mandatory Access Control in Form von SELinux (Red Hat, CentOS) oder AppArmor (Suse, Ubuntu), die z. B. von Docker ebenfalls mit eingerichtet werden. Hierbei handelt es sich um zusätzliche Sicherheitsmechanismen im Linux-Kernel, die es ermöglichen die Anwendungen davon abzuhalten, unerwünschte Änderungen vorzunehmen.
Ferner nutzen Container-Lösungen zwecks Ermöglichung einer effizienten Dateisystem-Nutzung hierarchische Dateisysteme mit besonderen Fähigkeiten, wie z. B. AUFS oder ext4 mit devmapper auf Red Hat bzw. btrfs, einer anderen Art „versionierender“ Dateisysteme (Copy-On-Write).
Das letzte Kernel-Feature für Container, welches schon von Free BSD Jails verwendet wurde, ist „change root“, also die Fähigkeit des Linux-Kernels, den Mountpunkt des Root-Ordners zu verschieben, wodurch erst eine geschlossene Betriebssystemumgebung simuliert werden kann.
Gegenüber allen älteren beschriebenen Ansätzen zur Container-Virtualisierung kommt es den Machern von Docker zu Gute, die zum Teil komplexe Konfiguration all dieser Funktionalitäten extrem vereinfacht und in ein leicht handhabbares Framework eingebettet zu haben.
(ID:45458390)