Ursprünglich als Tool für Systemd-Tester gedacht, mausert sich Systemd-nspawn zu einer eigenständigen Containerlösung. Bei Rkt von Core OS ist es bereits als Low-Level-Tool im Einsatz. Rkt-Entwickler Jonathan Boulle stellt Systemd-nspawn vor.
Dank Systemd-nspawn [1], einem schlanken Containertool im Systemd-Ökosystem, laufen einzelne Befehle, umfangreiche Dienste oder gar komplette Betriebssysteme in einer abgekapselten Umgebung unter Linux. Anfangs diente das Tool den Systemd-Entwicklern eigentlich nur dazu, Systemd [2] selbst zu bauen und zu betreiben, ohne damit dem Hostsystem in die Quere zu kommen, auf dem sie arbeiteten.
Seitdem hat es sich jedoch weiterentwickelt und schließt inzwischen eine Reihe von Funktionen ein, die von einer fortgeschrittenen Netzwerkkonfigurationen über eine SE-Linux-Integration bis hin zur Unterstützung für ein natives Overlay-Dateisystem reichen. Das aktuelle Systemd-nspawn ist also ein vielseitiges und funktionsreiches Tool und deckt eine ganze Palette verschiedener Einsatzszenarien für den Betrieb von Anwendungen unter Linux ab.
Um näher herauszufinden, was Systemd-nspawn ausmacht, lohnt es sich, Bekanntschaft mit einigen der mit ihm verwandten Tools zu machen.
Chroot
Chroot [3] bietet einen der ältesten und einfachsten Wege, um einen bestimmten Grad an Prozess-Isolation auf Linux zu erreichen. Der »chroot« -Systemaufruf erlaubt es dem aufrufenden Prozess, in eine isolierte Dateisystem-Umgebung zu wechseln. Ab diesem Punkt betrachtet Linux jede Referenz auf einen Pfad im Dateisystem, den die Anwendung geht, als relativ zu ihrem Chroot-Verzeichnis. Ein Beispiel für dieses Verhalten wäre:
chroot /home/redaktion/images/jessie/ /bin/ls
Der zweite Teil der Zeile versucht dank des ersten, den Befehl »ls« in der Chroot-Umgebung auszuführen (Abbildung 1). Das neue Wurzelverzeichnis befindet sich dann auf dem Host unter »/home/redaktion/images/jessie/« . Nach dem Einsatz des »chroot« -Befehls sieht der Prozess auf dem Host allerdings keine Dateien außerhalb von »/home/redaktion/images/jessie/« mehr.
Letztlich verändert »chroot« lediglich den Mechanismus, den Linux verwendet, um Pfadnamen aufzulösen. Das tut es, sobald ein Prozess versucht auf das Dateisystem zuzugreifen. Damit gewährt Chroot zwar einen rudimentären Level an Isolation auf Dateisystemebene, der aber lässt sich unglücklicherweise recht einfach brechen.
Es gibt mehrere Wege, aus dem Chroot zu entkommen, etwa indem ein Prozess bereits vor seinem Aufruf per File Descriptor auf eine Datei außerhalb des Chroot verweist. Daher lässt sich diese Form der Isolation nicht als sicherer Mechanismus bezeichnen. Des Weiteren fehlen Chroot fortgeschrittene Möglichkeiten der Prozess-Isolation, die für Linux wünschenswert erscheinen, wenn es zum Beispiel um die Speichernutzung oder die Netzwerkschnittstellen geht.
Besser isoliert
Glücklicherweise haben in den letzten fünf Jahren einige ausgefeiltere Containment-Tools die Bühne betreten, zu denen neben Systemd-nspawn auch Docker [4] und das von uns entwickelte Rkt [5] gehören. Sie nutzen spezielle Kernelfeatures, um eine bessere Isolation zwischen den Prozessen des Systems zu erreichen. Während aber Docker und Rkt auf eine Nutzergruppe abzielen, die vorwiegend Anwendungen in Containern laufen lassen möchte, handelt es sich bei Systemd-nspawn eher um ein Low-Level-Tool, das sich an Entwickler richtet.
Rkt
Rkt heißt die von uns entwickelte Laufzeitumgebung für Anwendungs-Container, sie implementiert die App-Container-Spezifikation (Appc, [6]). Diese definiert, aus welchen Komponenten ein Container-Image bestehen sollte und welche Anforderungen erfüllt sein müssen, um Anwendungen in einem Container zu betreiben.
Intern setzt Rkt auf eine abgestufte Architektur und betreibt Container im Drei-Stufen-Modell. Die Rkt-Kommandozeile bildet die erste Stufe (Stage 0). Sie spürt Application Container Images (ACI) im Internet oder in Repositories auf, lädt sie per HTTP oder Bittorrent herunter, verifiziert ihre GPG-Signaturen und verwaltet den lokalen Festplattencache.
In der zweiten Stufe (Stage 1) setzt Rkt die isolierte Umgebung auf, indem es die nötigen Kernelfeatures verwendet, um die Anwendungen vom Host zu isolieren. Die dritte Stufe (Stage 2) betrifft schließlich die vom User spezifizierte Anwendung selbst. Im Fall von Rkt dürfen das eine oder mehrere Anwendungen sein, weil die das Fundament bildende ausführende Einheit, der Pod, per Definition aus einer oder mehreren Anwendungen bestehen darf.
Die von Core OS ausgelieferte Rkt-Version überlässt in Stage 1 standardmäßig Systemd-nspawn den Hauptteil beim Einrichten der Container. Aktuell gibt es noch eine weitere Stage-1-Variante, die Kvmtool [7] für diese Dienste einspannt. Das setzt eine schlanke virtuelle Maschine auf, die den KVM-Treiber des Kernels sowie dessen Hardware-Isolation verwendet. Wer Rkt in diesem Modus betreibt, braucht Systemd-nspawn nicht. Weil die Systemd-nspawn-Variante aber für vorhersehbare Zeit der Standard bleiben soll, arbeiten die Rkt-Entwickler eng mit dem Systemd-Projekt zusammen.
Docker
Auf höchster Ebene referiert der Begriff Docker auf eine Containerplattform, die aus vielen Teilen besteht, die vom Ausführen eines individuellen Containers auf einem Host bis hin zum Orchestrieren von Containern über große Servercluster hinweg reicht. Zu Vergleichszwecken lässt sich Docker aber auf zwei prinzipielle Modi begrenzen, die das zugehörige Kommandozeilentool abdeckt.
Da wäre einmal der Daemon-Modus (die Docker-Engine), der den Großteil der Arbeit beim Betreiben und Verwalten der Container übernimmt. Zum anderen gibt es den Client-Modus, in dem die meisten Anwender mit der Docker-Engine interagieren. Gibt ein User beispielsweise ein simples »docker run« ein, verwandelt Docker dies in einen API-Aufruf und reicht ihn an die lokale Docker-Engine weiter, die dann den vom User gewählten Container konfiguriert und startet.
Die Docker-Engine zeichnet für eine Vielzahl Funktionen verantwortlich. Sie empfängt die Container-Images über das Internet, begleitet die Container auf dem System und bietet dem Docker-Client oder anderen HTTP-Clients das erwähnte REST-HTTP-API an. Die Docker-Engine läuft daher dauerhaft, denn sie verwaltet sämtliche Docker-Container auf einem System. Startet sie neu, betrifft das auch die Container.
Systemd-nspawn
Nun zu Systemd-nspawn selbst. Dies lässt sich treffender mit dem Teil der Docker-Engine vergleichen, der sich um das Einrichten der Container kümmert. Intern macht Systemd-nspawn von zahlreichen Features Gebrauch, die der Linux-Kernel anbietet, um Prozesse und Ressourcen zu isolieren. Das erste und wichtigste sind die Namespaces [9].
Namespaces
Kernel-Namespaces isolieren verschiedene Systemressourcen auf eine von den Prozessen abstrahierte Weise, indem sie den Prozessen zuvorkommen. Befindet sich ein Prozess beispielsweise in seinem eigenen PID-Namespace, sieht er nur andere Prozesse auf dem System, die sich im selben Namespace befinden. Auf diese Weise verhindern die Anwender, dass die Prozesse über verschiedene Achsen miteinander interagieren. Der Linux-Kernel bietet eine Anzahl verschiedener Namespaces an, welche das zurzeit sind, verrät Tabelle 1.
Tabelle 1
Kernel-Namespaces
|
Namespace |
Funktion |
|---|---|
|
IPC |
System-V-Interprozesskommunikation, Posix-Nachrichten-Warteschlangen |
|
Network |
Netzwerkgeräte, Stacks, Ports und so weiter |
|
Mount |
Mountpoints |
|
PID |
Prozess-IDs |
|
User |
Benutzer- und Gruppen-IDs |
|
UTS |
Hostname und NIS-Domainname |
Ein Prozess erzeugt die Namensräume, indem er einen Systemaufruf namens »unshare()« [10] absetzt. Das trennt den aufrufenden Prozess von seinem existierenden Namespace und erzeugt einen neuen. Ein Prozess kann zudem den »setns()« -Aufruf anwenden, um einen auf dem System existierenden Namensraum zu modifizieren.
Systemd-nspawn verwendet Namespaces ziemlich intensiv, was auch der Name des Tools reflektiert: Nspawn verweist auf die Tatsache, dass das Tool neue Namensräume generiert. Standardmäßig betreibt Systemd-nspawn Prozesse allerdings in ihren eigenen IPC-, Mount-, PID- und UTS-Namensräumen. Es bietet auch Optionen an, um einem Container einen unabhängigen Network-Namespace zu spendieren sowie ein Flag, um einen rudimentären User-Namespace zu verwenden. Weitere Informationen zu Namensräumen liefert eine Serie von LWN-Artikeln [11].
Cgroups
Eine weitere Schlüsseltechnologie beim Betrieb von Containern unter Linux sind die Cgroups (Control Groups, [12]). Ist die Rede von Linux-Containern, meinen die Leute in der Regel diese Kombination aus Namespaces und Cgroups. Letztere organisieren Prozesse auf einem Linux-System in hierarchischen Baumstrukturen. Sie weisen einzelnen Bereichen der Hierarchie optional verschiedene Parameter zur Ressourcenverwaltung zu. Damit lassen sich etwa Speicherlimits für einen bestimmten Prozess oder eine Gruppe von Prozessen festlegen, die der Kernel durchsetzt.
Allerdings hat Systemd-nspawn mit Cgroups eher wenig zu tun. Es stellt sicher, dass der Cgroup-Tree in dem Mount-Namespace verfügbar ist, den es einrichtet. Interessant wird es, wo Systemd-nspawn zum Einsatz kommt, um ein Initsystem zu starten, das ebenfalls Cgroup-Management betreibt wie beispielsweise Systemd selbst.
Das tangiert auch Rkt. Führt es Systemd-nspawn in Stage 1 aus, startet es eine Instanz von Systemd als PID 1 innerhalb einer Containerumgebung. Diese Instanz schafft dann eine eigene Baumstruktur von Cgroups, um die Anwendungen zu verwalten, die es startet. Um Systemd-nspawn aufzurufen, verwendet Rkt einen rohen Systemaufruf [13].
Systemd-nspawn in Aktion
Das Tool ist Bestandteil jeder aktuellen Linux-Distribution, die auch Systemd als Initsystem verwendet. In seiner simpelsten Form übergibt ihm der Anwender einfach ein Verzeichnis und fordert es auf, darin ein Binary auszuführen. Mehr Komplexität bieten die über 30 Kommandozeilen-Optionen, die mehrere Aspekte des zu erzeugenden Containers beeinflussen. Die neuesten Versionen von Systemd-nspawn lassen sich zudem mit einer Konfigurationsdatei füttern, die die meisten der verfügbaren Flags in ein wiederverwendbares Format bringt.
Ein einfaches Beispiel in Listing 1 zeigt Systemd-nspawn in Aktion. Es lädt ein Image der Debian-Distribution Jessie [14] herunter und startet es dann in einem Container (Abbildung 2). Die Schritte sind mit Rootrechten auszuführen. Zudem muss der Admin, will er Jessie nutzen, zuvor in der Datei »/home/redaktion/jessie/etc/passwd« das Passwort von Root löschen.
Mit Rkt sieht der Prozess übrigens ganz ähnlich aus (Listing 2). Die dort gezeigten Befehle laden ein ACI von Etcd in Version 2.0.0 herunter und starten es (Abbildung 3). In dem Szenario hat Rkt das notwendige Dateisystem in dem Verzeichnis schon eingerichtet – inklusive einer Kopie von Systemd, das es über »systemd-nspawn […]« aufruft.
Listing 1
Jessie holen und starten
01 apt install debootstrap 02 debootstrap jessie /home/redaktion/jessie 03 systemd-nspawn -bD /home/redaktion/jessie
Listing 2
Rkt in Aktion
01 wget https://github.com/coreos/rkt/releases/download/v0.13.0/rkt-v0.13.0.tar.gz 02 tar xvzf rkt-v0.13.0.tar.gz 03 cd rkt-v0.13.0 04 ./rkt run coreos.com/etcd:v2.0.0
Ausblick
Aber auch wenn Systemd-nspawn bereits viele Features im Gepäck hat, gibt es einige Bereiche, in denen wir auf künftige Verbesserungen hoffen. Zentral sind dabei die User-Namespaces [15], die in ihrer jetzigen Form noch nicht wirklich zu gebrauchen sind. Mit ihrer Hilfe lassen sich Benutzer- und Gruppen-IDs in eigene Namensräume mappen. Im Klartext heißt das: In einem User-Namespace können die Nutzer- und Gruppen-IDs anders lauten als außerhalb dieses Namensraums. Das erlaubt es, einem Prozess innerhalb eines Namespace, etwa in einem Container, Rootrechte zu verleihen, während er außerhalb lediglich über einfache oder gar eingeschränkte Benutzerrechte verfügt.
Abseits solcher Wünsche lässt sich jedoch feststellen, dass Entwickler und Systemadministratoren auch dank »systemd-nspawn« mittlerweile über zahlreiche Optionen verfügen, um Anwendungscontainer zu verwalten.
Reifegrad
Sehr lange enthielt die Manpage von Systemd-nspawn [1] deutliche Warnhinweise gegen den Einsatz in Produktivumgebungen. Das lag unter anderem daran, dass die Entwickler das Tool tatsächlich eher für den Eigenbedarf geschrieben hatten und dass die Realität den Erwartungen an die Fähigkeiten in Sachen Sicherheit und Isolation in den frühen Tagen der Container-Technologie oft hinterherhinkte.
Lennart Poettering entfernte die Warnung auf Anregung von Core OS später, als ihm klar wurde, dass Systemd-nspawn bereits bei vielen Usern produktiv im Einsatz ist und auch die Systemd-Entwickler reichlich Zeit hatten, es auf Herz und Nieren zu prüfen.
Die Systemd-Entwickler lassen im Umgang mit dem Tool jedoch nach wie vor Vorsicht walten. So stellt die Manpage klar, dass Systemd-nspawn noch nicht für ein sicheres Container-Setup tauge, es sei vielmehr für Debugging- und Testzwecke gemacht. Für uns als Rkt-Entwickler spielt das keine Rolle, da die Anwendungsentwickler und Anwender mit der Application Container Runtime als Interface interagieren und Systemd-nspawn lediglich als Low-Level-Tool dient.
Infos
- Systemd-nspawn: http://www.freedesktop.org/software/systemd/man/systemd-nspawn.html
- Systemd: https://wiki.freedesktop.org/www/Software/systemd/
- Chroot: https://www.gnu.org/software/coreutils/coreutils.html
- Docker: https://www.docker.com
- Rkt: https://github.com/coreos/rkt
- Appc: https://github.com/appc
- Kvmtool: https://kernel.googlesource.com/pub/scm/linux/kernel/git/will/kvmtool/+/master/README
- Docker Engine: https://www.docker.com/docker-engine
- Namespaces-Übersicht: http://man7.org/linux/man-pages/man7/namespaces.7.html
- Syscall »unshare()« : http://linux.die.net/man/2/unshare
- Namespace-Serie auf Lwn.net: https://lwn.net/Articles/531114/#series_index
- Cgroups: https://www.kernel.org/doc/Documentation/cgroups/cgroups.txt
- Roher Systemaufruf: http://manpages.ubuntu.com/manpages/saucy/de/man2/clone.2.html
- Debian Jessie: https://www.debian.org/releases/stable/
- User-Namespaces: http://man7.org/linux/man-pages/man7/user_namespaces.7.html









