Podman: Linux-Container einfach gemacht, Teil 1

Das Container-Tool Podman, als Alternative zu Docker angepriesen, ist kürzlich in Version 1.0 erschienen. Gründe genug für einen tiefen Einblick: Von der Installation bis zum Einsatz.

In Pocket speichern vorlesen Druckansicht 11 Kommentare lesen
Podman: Linux-Container einfach gemacht, Teil 1

(Bild: stock.xchng)

Lesezeit: 10 Min.
Von
  • Valentin Rothberg
Inhaltsverzeichnis

Red Hat hat das Container-Werkzeug Podman ursprünglich als Debugging-Tool für die auf Kubernetes spezialisierte Container-Engine CRI-O geplant, um Anwendungsentwicklern und Administratoren von Kubernetes-Clustern gewisse Aufgaben zu vereinfachen. Seitdem hat sich Podman jedoch zu einem umfangreichen Werkzeug für das Container-Management entwickelt. Entwickler können es in Linux-Distributionen, wie Fedora, Arch Linux und openSUSE Tumbleweed ohne weiteres aus den Hauptsoftwarequellen installieren.

Das Podman-Logo

(Bild: Podman)

Die Podman-Entwickler bewerben ihr Werkzeug mit der Kompatibilität zu Docker, was sich auf der Kommandozeile in identischen Befehlen widerspiegelt und sowohl Nutzern als auch Programmen die Migration von Docker zu Podman vereinfachen soll. Unter der Haube unterscheiden sich die beiden Container-Tools jedoch stark.

Mehr Infos

Docker implementiert eine Client-Server-Architektur, wodurch bei der Ausführung eines Befehls der Docker-Client eine Nachricht an den Docker-Daemon sendet, der die Ausführung und das Management der Container wiederum an einen weiteren Server auslagert, nämlich an containerd. Diese Architektur birgt einige Sicherheitsrisiken, besonders wenn der Docker-Daemon als Systemadministrator (also als root) läuft. Um das zu umgehen, verzichtet Podman auf einen Daemon und setzt stattdessen auf ein klassisches Fork-Exec-Modell, das jeden Container zum Kindprozess von Podman macht. Das ermöglicht es zum Beispiel, Nutzeraktivitäten auf einem System nachzuvollziehen. Mit dem Docker-Daemon funktioniert das nicht, da er stets als gleicher Benutzer, in der Regel als root, ausgeführt wird.

Für das Management von Container-Images setzen Podman und die Geschwisterprojekte CRI-O, Buildah und Skopeo auf identische Bibliotheken zum Verwalten und Speichern der Container-Images, sodass sie diese miteinander teilen können. In Anlehnung an Dockers Kommandozeile, können Entwickler Container-Images mit dem pull-Kommando herunterladen. Zum Beispiel die neueste Version von Alpine Linux:

$ podman pull docker.io/library/alpine:latest
Trying to pull docker.io/library/alpine:latest...Getting image source signatures
Copying blob 6c40cc604d8e: 2.63 MiB / 2.63 MiB [============================] 1s
Copying config caf27325b298: 1.48 KiB / 1.48 KiB [==========================] 0s
Writing manifest to image destination
Storing signatures
caf27325b298a6730837023a8a342699c8b7b388b8d878966b064a1320043019

Das heruntergeladene Container-Image können Anwender nun verwenden, um mit podman run Container auszuführen oder um ein neues Image zu bauen. Für das Bauen von Images setzt Podman auf Buildah, das derselben Gruppe von Entwicklern wie Podman, CRI-O und Skopeo entspringt und auf das Bauen von Containern spezialisiert ist. Podman ruft jedoch nicht Buildah direkt auf, sondern verwendet Teile des Buildah-Quellcodes für das podman build-Kommando.

Für das Ausführen der Container rückt das Podman-Team das rootless-Feature besonders in den Vordergrund, also das Ausführen eines Containers ohne root-Rechte auf dem Host. Das dient vor allem der Sicherheit, da ein Container nur die Rechte erhält, die er zur Ausführung tatsächlich benötigt. Das folgende Beispiel illustriert das Vorgehen. Zunächst führt man einen Container mit dem zuvor heruntergeladenen Alpine Linux aus:

$ podman run -d alpine top
5f421b01faa1b81db0da2678adc3537a66897c2f3f97417c6295afecdcd67486

Nun können Anwender überprüfen, welcher Benutzer top im Container ausführt:

$ podman exec 5f421b01faa ps -ef
PID USER TIME COMMAND
1 root 0:00 top
16 root 0:00 ps -ef

Wie man sieht, läuft top als root und besitzt damit root-Rechte innerhalb des Containers. Nun sollte man sichergehen, dass der Container keine root-Rechte auf dem Host hat:

$ ps -ef | grep top
valentin 31332 31321 0 14:03 ? 00:00:00 top

Tatsächlich läuft der Prozess nur innerhalb des Containers als root, auf dem Host jedoch als normaler Nutzer. Podman verwendet dafür einen besonderen Mechanismus des Linux-Kernels, nämlich User Namespaces. Prozesse haben darin andere Rechte und User IDs als außerhalb des Namespace. Dadurch soll rootless-Podman portabel sein, da viele Programme innerhalb eines Containers root-Rechte benötigen und Entwickler sie somit weiterhin ausführen können können, ohne ihnen das Recht auf dem Host zu erteilen.

Das schützt den Host vor Angriffen durch bösartige Container-Images, wie in einem kürzlich veröffentlichten Angriff geschehen. In diesem Angriffsszenario kann es Angreifern durch Ausführen der Container-Images gelingen, aus dem Container auszubrechen und beliebigen Code auf dem Host auszuführen. Der Angriff funktioniert dank User Namespaces jedoch nicht mit rootless Podman.

Der Name Podman ist ein Kürzel für Pod Manager. Die Container-Orchestrierung Kubernetes hat den Begriff Pod geprägt. Er beschreibt eine Gruppe von Containern, die sich bestimmte Ressourcen teilen und nicht völlig isoliert voneinander laufen. Während Docker Pods nur indirekt über Kubernetes unterstützt, sind sie ein integraler Bestandteil von Podman.

Folgende Abbildung beschreibt die Architektur eines Pods, dessen Kern ein sogenannter Infrastruktur-Container oder kurz Infra-Container bildet.

Die Architektur eines Pods in Podman (Abb. 2)

Ein Infra-Container verrichtet keine Arbeit, sorgt aber dafür, dass bestimmte Ressourcen des Pods wie Namespaces, CGroups und Netzwerk-Ports am Leben bleiben. Obwohl Podman dem Fork-Exec-Modell folgt und damit nicht als Server läuft, benötigen wir dennoch einen Prozess, der Container überwacht, um etwa Logs und Exit-Codes zu sichern. Dies ist die Aufgabe von Conmon, ein kleines, in C geschriebenes Monitoring-Werkzeug, dass jedem Container angehängt wird. Neben dem Monitoring hält Conmon das Terminal des Containers offen, um per podman exec zu einem späteren Zeitpunkt das Terminal verwenden zu können.

Der Grafik ist ebenfalls runc zu entnehmen, eine industrieweite Standard-Container-Runtime. Sie entsteht unter dem Schirm der Open Container Initiative (OCI). Runc ist für die Ausführung eines Container-Prozesses zuständig und implementiert die Runtime-Spezifikation der OCI. Runc kann neben OCI Images als kleinster gemeinsamer Nenner des Container-Ökosystems verstanden werden, da es in vielen Container-Tools wie Podman, Docker oder rkt Verwendung findet.

Ein Pod lässt sich über das podman pod create-Kommando erstellen:

$ podman pod create --name heise
fe4462d0819c609a589ca8587cc2db5aa010226b40aeaf6af5d76df1f8c2991c

$ podman ps --pod --all
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES POD
adc0d2498621 k8s.gcr.io/pause:3.1 46 seconds ago Up 46 seconds ago fe4462d0819c-infra fe4462d0819c

Wie im Beispiel zu sehen, wird jedem Pod automatisch ein Infra-Container hinzugefügt. Andere Container können nun dem heise-Pod per podman create und podman run beitreten:

$ podman run --detach --pod=heise alpine:latest top
A85cf439a6df36d558ad7eab989db66df97870b6ba109fad8a3e2a4d34240f59

$ podman ps --all --pod
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES POD
a85cf439a6df docker.io/library/alpine:latest top 8 minutes ago Up 8 minutes ago mystifying_lalande fe4462d0819c
adc0d2498621 k8s.gcr.io/pause:3.1 15 minutes ago Up 15 minutes ago fe4462d0819c-infra fe4462d0819c

Meist setzen Entwickler Pods ein, um eine komplexere Anwendung mit einer Gruppe von Containern zu implementieren. Istio packt zum Beispiel den Sidecar Container mit in den Pod, um seine Funktionalität zu implementieren. Da die Container in einem Pod laufen, teilen sie sich unter anderem den PID und Network Namespace, was für den Betrieb notwendig ist. Dennoch profitieren Anwender davon, dass Teile des Systems in verschiedenen Containern laufen, sodass man Ressourcen wie CPU, Arbeitsspeicher und Netzwerkleistung den Funktionen entsprechend zuteilen und limitieren kann.

Podman ermöglicht es Entwicklern, Pods lokal zu bauen und zu testen, ohne ein komplexes Kubernetes-Cluster aufsetzen zu müssen. Falls Kubernetes jedoch das Einsatzziel sein sollte, können lokal laufende Container und Pods mit podman generate kube in ein Kubernetes-YAML übersetzt und mit kubectl create -f in Kubernetes importiert werden.

Podman bietet neben der Kommandozeile mehrere APIs an, um eine Integration in andere Werkzeuge und Programme zu ermöglichen, ohne einen Umweg über direkte Programmaufrufe gehen zu müssen. Wie im Cloud- und Container-Bereich üblich ist Podman in Go implementiert. Dabei setzt Podman auf die libpod-Bibliothek, die eine umfangreiche und gut dokumentierte API für das Management von Images, Containern und Pods anbietet. Entwickler können mit der API Teile der Podman-Funktionen in andere Programme integrieren, sofern sie in Go geschrieben sind.

Eine weitere API bietet Podman mit Varlink an. Varlink ist eine Schnittstellen-Beschreibungsprache und zugleich ein Protokoll und unterstützt Programmiersprachen wie C, Rust, Python, Java und Go. Unter Verwendung von Varlink kann Podman sowohl lokal als auch auf einem Remote-Server ausgeführt werden, wie es Varlink-Entwickler Harald Hoyer ausführlich im Podman-Blog beschreibt. Auf einem Remote-Server kann man Podman per Systemd-Socket-Aktivierung aufrufen.

Die Varlink-API kommt unter anderem in Podmans Python-Bibliothek zum Einsatz. Die Podman-Entwickler verwenden Varlink darüber hinaus in einem aktuellen Bestreben, um Podman auf Windows und macOS anzubieten. Wie bei Docker setzen sie dabei voraus, dass Podman auf einem Linux-System installiert ist, das Podman per Varlink auf Windows und macOS aufruft.

Podman soll Nutzern die Migration von Docker vereinfachen und setzt auf Kompatibilität auf der Kommandozeile. Teile des Codes von Podman stammen ursprünglich von Docker, insbesondere die Storage-Implementierung. Durch die Unabhängigkeit von Docker kann Podman Wünsche der Nutzer realisieren, was sich manchmal nur in kleinen Details wie einer --all-Option zum Entfernen aller Images oder Container widerspiegelt. An anderen Stelle geht Podman wohl noch weiter – beispielsweise beim rootless-Support, einer nativen Unterstützung von Systemd innerhalb von Containern, dem Konzept der Pods oder der Möglichkeit, das Root-Dateisystem eines Containers direkt auf dem Host zu mounten.

Der nächste Teil der Artikelserie beleuchtet die Sicherheitskonzepte von Podman genauer und zeigt, wie Anwender ihre Container vor potenziellen Angriffen schützen können.

[Update]: In den Codebeispielen auf Seite 3 wurden die sudo-Aufufe entfernt, da sie hier nicht notwendig sind.

Valentin Rothberg
ist Softwareentwickler in Red Hats Container-Runtimes-Team und arbeitet an Container-Tools wie CRI-O, Podman, Buildah und Skopeo und den zugrundeliegenden Techniken und Bibliotheken.
(bbo)