Podman: Linux-Container einfach gemacht, Teil 2

Seite 2: Das Root File System (RootFS)

Inhaltsverzeichnis

Das sogenannte Root File System (RootFS) ist das sichtbare Wurzelverzeichnis eines Prozesses und kann über unterschiedliche Systemaufrufe (zum Beispiel pivot_root(2)) verändert werden. Der Wechsel des Wurzelverzeichnisses ist ein altbewährtes Mittel und steht mit dem chroot(2)-Systemaufruf seit den 1970er-Jahren auf Unix-Systemen zur Verfügung. Das ist der wohl offensichtlichste Baustein eines Containers, da man sich durch ein neues RootFS auf einem scheinbar neuen System befindet. Das folgende Beispiel illustriert das genauer.

Zunächst laden Anwender das für Container verwendete RootFS von Alpine Linux 3.9.3 herunter, entpacken es und wechseln mit chroot(1) das Wurzelverzeichnis:

$ wget -q http://dl-cdn.alpinelinux.org/alpine/v3.9/releases/x86_64/alpine-minirootfs-3.9.3-x86_64.tar.gz
$ tar -xzf alpine-minirootfs-3.9.3-x86_64.tar.gz
$ sudo chroot `pwd` /bin/sh
$ cat /etc/os-release | head -n1
NAME="Alpine Linux"
$ ls /
alpine-minirootfs-3.9.3-x86_64.tar.gz media sbin
bin mnt srv
dev opt sys
etc proc tmp
home root usr
lib run var

Das Dateisystem lässt augenscheinlich darauf schließen, dass man sich auf einem Alpine Linux befindet. Nimmt man das System jedoch genauer unter die Lupe, fällt schnell auf, dass ein einfacher Wechsel des RootFS nicht vom Rest des Systems isolieren kann oder gar die Ressourcen beschränkt. Beispielsweise sind alle alten Prozesse nach wie vor sichtbar.

$ whoami
root
$ mount -t proc proc /proc
$ ps -eo user | sort -u
1000
172
193
root

Der Einsatz des RootFS bietet Nutzern also eine gewisse Portabilität, da sie beliebige Container-Images entpacken und deren Programme ausführen können. Um die Ausführung abzusichern, sind jedoch etwas größere Geschütze notwendig.

Namespaces dienen der Isolation von systemweiten Ressourcen. Prozesse innerhalb eines Namespace haben eine andere Sicht und scheinbar eigene Instanzen der Ressourcen. Dabei bietet Linux verschiedene Arten von Namespaces an. Ein beliebtes Anwendungsbeispiel ist der PID Namespace, wodurch über mehrere Namespaces die gleiche Prozess-ID (PID) mehrfach vergeben werden kann. Dadurch hat zum Beispiel jeder Container einen eigenen Init-Prozess mit der PID 1.

Namespaces können Entwickler mit unshare(1) verlassen beziehungsweise erstellen, um zum Beispiel in einen neuen PID Namespace zu wechseln, welcher von Prozessen außerhalb des Namespace isoliert ist und damit keine Sicht auf sie hat.

$ sudo unshare --fork --pid --mount-proc
$ ps
PID TTY TIME CMD
96 pts/27 00:00:00 bash
142 pts/27 00:00:00 ps

Insgesamt gibt es sieben Namespaces, um neben den PIDs auch den Netzwerk-Stack, den Hostnamen, die Mountpoints, Inter-Prozess-Kommunikation (zum Beispiel POSIX Message Queues) und weitere Ressourcen isolieren zu können.

In manchen Anwendungsfällen müssen sich Container bestimmte Ressourcen und damit die zugrunde liegenden Namespaces teilen. Dafür bietet Podman entsprechende Optionen an. Zum Beispiel kann ein Container dem Network Namespace eines anderen Containers mit podman run --network=container:name/id beitreten.