Nach unseren Beiträgen zu Podman und Buildah widmen wir uns heute dem dritten Tool aus der Sammlung von RedHat-Container Tools: Skopeo.
Bei Skopeo handelt es sich um ein vergleichsweise kleines Tool, das hauptsächlich Anwendung in der Container-Erstellung und -Nutzung findet und damit prädestiniert für folgende Aufgaben und Bereiche ist:
- Kopieren von Images zwischen verschiedenen Registries
- Synchronisieren von Images zwischen verschiedenen Stellen
- Inspizieren von Images in einer Registry ohne vorherigen Download
- Löschen von Images
Hierbei ist noch zu erwähnen, dass Skopeo auch einen lokalen Docker-Daemon wie eine Registry behandelt und dies als Quelle genutzt werden kann. Auch wird, wenn nicht anders angegeben, immer auch für die Images von der aktuell für Skopeo genutzten Architektur ausgegangen. Dies lässt sich jedoch durch Parameter ändern bzw. erweitern.
Entwicklung
Die Skopeo Version 1.0 wurde am 18.05.2020 veröffentlicht und wird aktuell, wie auch die anderen RHEL-Container-Tools, unter der Apache 2.0 Lizenz angeboten. Es wird ebenfalls in Golang entwickelt, hat keinen festen Release-Zyklus und wird von der containers-group betreut. Der Code kann auf Github eingesehen werden.
Installation
Die Installation von Skopeo ist mittlerweile auf fast allen Linux-Distributionen recht trivial über eine einfache Paketinstallation erledigt, da alle gängigen Distributionen das Paket mittlerweile in den Repositories hinterlegt haben. Sollte eine bestimmte Version benötigt werden, oder es nicht möglich sein Pakete zu installieren, so wird Skopeo auch als dedizierter Container zur Verfügung gestellt.
Auch ist hier wie üblich zu beachten, dass nicht jede Distribution auch die aktuellste Version bereit stellt und damit ggf. Dritt-Repositories hinzugefügt werden müssen.
Eine detaillierte Dokumentation dazu findet sich auf Github.
Konfiguration
Skopeo nutzt hier, ebenso wie podman und buildah, folgende Dateien als Standard:
- /etc/containers/policy.json – Für, falls benötigt, Image-Policies. Kann auch per Parameter
--policy
übergeben werden. - /etc/containers/registries.d – Standardverzeichnis für die eventuell spezielle Konfiguration von Image-Registries. Als prominentes Beispiel wäre hier z.B. das SSL-Verifiy für eigene Entwicklungsregistries. Kann auch per Parameter
--registries.d
überschrieben werden.
Arbeiten mit Skopeo
In den hier verwendeten Beispielen nutzen wir die aktuell unter CentOS 8 Streams verfügbare Version 1.5.0.
Inspizieren eines Images
Mittels skopeo inspect
können die Details zu einem Image bzw. dem Repository eingesehen werden ohne sich die Daten vorher herunterladen zu müssen. Es werden unter anderem alle verfügbaren Tags des Repositorys sowie verschiedene Details zum angefragten Image beziehungsweise Image-Tag.
Es werden hierbei noch verschiedene Parameter angeboten um bestimmte Details anzufragen oder die Ausgabe anzupassen.
Folgend ein paar Beispiele:
--config
– Liefert mehr Details zum eigentlichen Image das angefragt wird. So werden zum Beispiel angelegte Volumes, Ports usw. direkt ersichtlich.--creds
– Übergabe von Logindaten (username:password) der Zielregistry--tls-verify
– Gibt an ob das SSL Zertifikat der Registry validiert werden soll oder nicht (Boolean)--no-tags
– Unterbindet die Ausgabe aller Tags des angefragten Images
Alle Optionen für inspect
sowie diverse Beispiele finden sich in der Dokumentation.
Hierzu ein Beispiel für ein generisches skopeo inspect
:
$ skopeo inspect docker://docker.io/library/postgres:latest { "Name": "docker.io/library/postgres", "Digest": "sha256:f91f537eb66b6f80217bb6921cd3dd4035b81a5bd1291e02cfa17ed55b7b9d28", "RepoTags": [ "10", ... "bullseye", "buster", "latest" ], "Created": "2022-01-04T01:19:59.244463885Z", "DockerVersion": "20.10.7", "Labels": null, "Architecture": "amd64", "Os": "linux", "Layers": [ "sha256:a2abf6c4d29d43a4bf9fbb769f524d0fb36a2edab49819c1bf3e76f409f953ea", ... ], "Env": [ "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/lib/postgresql/14/bin", "GOSU_VERSION=1.14", "LANG=en_US.utf8", "PG_MAJOR=14", "PG_VERSION=14.1-1.pgdg110+1", "PGDATA=/var/lib/postgresql/data" ] }
Und ein Bespiel für ein skopeo inspect --config
:
$ skopeo inspect --config docker://docker.io/library/postgres:latest { "created": "2022-01-04T01:19:59.244463885Z", "architecture": "amd64", "os": "linux", "config": { "ExposedPorts": { "5432/tcp": {} }, "Env": [ "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/lib/postgresql/14/bin", "GOSU_VERSION=1.14", "LANG=en_US.utf8", "PG_MAJOR=14", "PG_VERSION=14.1-1.pgdg110+1", "PGDATA=/var/lib/postgresql/data" ], "Entrypoint": [ "docker-entrypoint.sh" ], "Cmd": [ "postgres" ], "Volumes": { "/var/lib/postgresql/data": {} }, "StopSignal": "SIGINT" }, "rootfs": { "type": "layers", "diff_ids": [ "sha256:2edcec3590a4ec7f40cf0743c15d78fb39d8326bc029073b41ef9727da6c851f", ... ] }, "history": [ { "created": "2021-12-21T01:22:43.418913408Z", "created_by": "/bin/sh -c #(nop) ADD file:09675d11695f65c55efdc393ff0cd32f30194cd7d0fbef4631eebfed4414ac97 in / " }, ... { "created": "2022-01-04T01:19:59.244463885Z", "created_by": "/bin/sh -c #(nop) CMD [\"postgres\"]", "empty_layer": true } ] }
Um eine dedizierte Liste aller im Repository verfügbaren Image-Tags zu bekommen eignet sich der Befehl skopeo list-tags
. Skopeo braucht nur die URL zum Repository, zum Beispiel docker://docker.io/library/postgres
, und kann so die ganze Liste aller Image-Tags auslesen und verfügbar machen.
Die Dokumentation ist selbst-erklärend und fällt entsprechend kompakt aus.
Kopieren von Images
Mittels skopeo copy
können Images entweder zwischen verschiedenen Registries kopiert werden, oder auch zum Beispiel von einer Registry in ein lokales Verzeichnis.
Hierbei stehen vielfältige Parameter zur Verfügung, wobei hier wieder nur ein paar Beispiele genannt werden sollen:
--additional-tag
– Versieht das Image in der Ziel-Registry mit zusätzlichen Tags--all
– Kopiert alle Architektur-Versionen des Images. Nicht nur das zur aktuellen Architektur passende.--sign-by
– Signiert das Image in der Ziel-Registry mit einem angegebenen Schlüssel--dest-compress-format
– Bestimmt die Kompressionsmethode für das Zielimage. Auch das Level ist als Parameter verfügbar--encryption-key
– Gibt Protokoll und Key für eine Verschlüsselung der Layer in der Ziel-Registry an
Hierbei kann es natürlich nötig sein Login-Informationen anzugeben. Details dazu werden weiter unten beschrieben.
Generelle Dokumentation zu den Copy-Möglichkeiten findet sich in der Dokumentation.
Löschen von Images
Mit Skopeo ist es ebenfalls möglich Images einer Registry remote als “gelöscht” zu markieren. Hierbei ist zu beachten, dass das Image erst dann tatsächlich im Dateisystem entfernt wird, wenn die Löschroutine / Garbage-Collection des Registry ihren Lauf hatte. Neben der Authentifizierung sind hier keine weiteren Optionen möglich.
Alle Informationen finden sich inkl. Beispielen in der offiziellen Dokumentation.
Synchronisieren von Images
Skopeo bietet die Möglichkeit alle Images eines Repositories entweder mit einem anderen Repository oder einem lokalen Ordner zu synchronisieren. Hierbei werden alle Images des Quellpfades in den Zielpfad übernommen.
Anwendung findet diese Synchronisierungsmöglichkeit zum Beispiel bei lokalen Entwicklungsumgebung deren Images in einem Repository liegen und dort automatisch synchronisiert werden können. Auch der umgekehrte Weg, die Synchronisierung aller Images aus einem internen Repository auf eine lokale Umgebung ist hiermit einfach umzusetzen.
Es ist sogar möglich als Quelle eine yaml-Datei anzugeben in welcher als Liste verschiedene Images aus verschiedenen Repositories hinterlegt sind und diese dann einmal auf einen bestimmten Host oder eine andere Registry zu kopieren. Hierbei ist zu beachten, dass in der YAML-Version keine lokalen Pfade als Quelle erlaubt sind. Natürlich können in der yaml-Datei auch verschiedene zusätzliche Parameter wie zum Beispiel Logindaten hinterlegt werden.
Eine volle Übersicht der Parameter und verschiedene Beispiele finden sich wie gewohnt in der Dokumentation.
Authentifizierung
In fast allen Fällen ist es möglich die Parameter für die Authentifizierung direkt im skopeo-Aufruf mit anzugeben.
Bei Befehlen welche Quelle und Ziel beinhalten gibt es die Möglichkeit für --src-creds
sowie --dest-creds
. Beim Arbeiten an einer einzelnen Registry wird meist nur --creds
genutzt. Gleichermaßen gibt es auch diverse Einzelparameter für username
und password
.
Alternativ kann ein Login auch mittels skopeo login
manuell gestartet werden. Dieser Aufruf erfragt die Logindaten interaktiv und erstellt ein Auth-file (Im Standard ${XDG_RUNTIME_DIR}/containers/auth.json
), welches dann ebenfalls als Parameter an die einzelnen Skopeo-Aufrufe weitergereicht werden kann. So müssen Authentifizierungsdaten nicht zwangsläufig im Code selbst hinterlegt werden sondern können, angepasst an die Vorliebe des Nutzers, interaktiv erfragt werden. Diese Auth-Datei teilt sich Skopeo auch mit Podman und Buildah und reiht sich damit nahtlos in die Sammlung von RedHat-Container Tools ein.
Um Datensätze wieder zu entfernen kann skopeo logout
genutzt werden.
Fazit
Skopeo ist eine sinnvolle Ergänzung zu den beiden anderen Container-Tools und bietet einige Features bei denen sich der Autor schon länger fragt warum diese nicht auch an andere Stelle zur Verfügung stehen.
Besonders die inspect
und copy
Funktionen von Skopeo können viele verschiedene andere Schritte und auch eine Menge Zeit einsparen wenn man viel mit verschiedenen Repositories arbeitet. Allein sich die verschiedenen Tags eines Images und deren Details anzuschauen ist durchaus eine große Hilfe in der alltäglichen Arbeit und erspart diverse Suchanfragen und Recherchen über UIs.
Auch das einfache Kopieren eines Images von einer Registry (z.B. Docker-Hub) in eine interne Registry ist gerade nach den Limitierung von Dockerhub eine willkommene Funktion, auch wenn die nach und nach Einzug haltende Funktion des Cache-Proxies bei einigen Registries wie zum Beispiel Harbor dies auch ablösen kann.
Wir unterstützen Sie gerne
Mit über 22+ Jahren an Entwicklungs- und Dienstleistungserfahrung im Open Source Bereich, kann die credativ GmbH Sie mit einem beispiellosen und individuell konfigurierbaren Support professionell Begleiten und Sie in allen Fragen bei Ihrer Open Source Infrastruktur voll und ganz unterstützen.
Die credativ GmbH ist ein herstellerunabhängiges Beratungs- und Dienstleistungs- unternehmen mit Standort in Mönchengladbach. Seit dem erfolgreichen Merger mit Instaclustr 2021 ist die credativ GmbH das europäische Hauptquartier der Instaclustr Gruppe.
Die Instaclustr Gruppe hilft Unternehmen bei der Realisierung eigener Applikationen im großen Umfang dank Managed-Plattform-Solutions für Open Source Technologien wie zum Beispiel Apache Cassandra®, Apache Kafka®, Apache Spark™, Redis™, OpenSearch™, Apache ZooKeeper™, und PostgreSQL®.
Instaclustr kombiniert eine komplette Dateninfrastruktur-Umgebung mit praktischer Expertise, Support und Consulting um eine kontinuierliche Leistung und Optimierung zu gewährleisten. Durch Beseitigung der Konplexität der Infrastruktur, wird es Unternehmen ermöglicht, ihre internen Entwicklungs- und Betriebsressourcen auf die Entwicklung innovativer kundenorientierter Anwendungen zu geringeren Kosten zu konzentrieren. Zu den Kunden von Instaclustr gehören einige der größten und innovativsten Fortune-500-Unternehmen.
In unserem vorhergehenden Artikel über Buildah haben wir erläutert wie man Container auch ohne Docker und root-Berechtigungen erstellt.
In diesem Artikel soll es darum gehen wie man eben jene Container auch ohne erweiterte Rechte nutzen kann.
Podman gehört neben dem bereits erwähnten Buildah und Skopeo zu den RedHat Container Tools und ist, kurz Zusammengefasst, eine daemonlose Laufzeitumgebung für Container. Es ist dabei, wie der Docker-Daemon, für den Betrieb eines einzelnen Hosts ausgelegt und bietet auch keine Clusterfunktionalität.
Entwicklung
Podman wurde in der Version 1.0 am 11.01.2019 released und steht ebenfalls unter der Apache 2.0 Lizenz.
Die Implementierung erfolgt in Golang und wird von der „containers organisation“ in erster Instanz vorangetrieben. Diese beinhaltet sowohl RedHat-Mitarbeiter als auch externe Entwickler.
Der Code kann auf Github eingesehen werden. Die Entwicklung erfolgt dabei in keinem festen Releasezyklus. So können sowohl Monate wie auch Wochen zwischen den Versionen liegen, je nachdem wenn Entschieden wird, dass genügend neue Features für einen Release implementiert wurden.
Podman selbst baut komplett auf die libpod auf, bzw. könnte man auch sagen, dass es das Tool zur libpod ist. Daher ist der Name des Repositorys auch libpod und nicht podman.
Container ohne root-Berechtigungen
Eine zentrale Komponente von Buildah und auch Podman ist hier die libpod welche es ermöglicht Container nur mit Nutzerberechtigungen zu starten und Images zu erstellen.
Diese setzt auf slirp4netns, fuse-overlayfs und die /etc/sub(u|g)id.
Dieser Bereich wurde bereits ausführlich im Artikel zu Buildah behandelt, weswegen hier nur auf diesen verwiesen wird, um Wiederholungen zu vermeiden.
Installation
Podman ist bei den gängigen RedHat-Distributionen direkt in den Repositories verfügbar.
Diese können dort je nach Version via dnf install podman
oder yum install podman
installiert werden.
Zu beachten ist, dass die Pakete in den CentOS-Distributionen nicht unbedingt aktuell sind. Daher bietet es sich hier an ebenfalls auf Kubic auszuweichen.
Für Debian und Derivate sowie Suse stehen analog zu Buildah Pakete in Kubic bereit.
Weitere Informationen dazu finden sich in der Dokumentation
[podmanager@buildah ~]$ podman -v podman version 1.8.2
Konfiguration
Die Konfigurationsdatei für Podman ist analog zu Builder unter /etc/containers/libpod.conf
für die globale und unter ~/.config/containers/libpod.conf
für die benutzerspezifische Konfiguration zu finden.
Die Vorlage mit den Default-Werten findet sich unter /usr/share/containers/libpod.conf
. Diese sollte jedoch nicht direkt, sondern durch die beiden Alternativen angepasst werden.
In der Datei lassen sich verschiedene Parameter für Podman konfigurieren; welches CNI-Plugin genutzt werden soll, welche Container-Runtime oder auch wo die Volumes der Container zu finden sind.
Ein Online-Beispiel findet sich auf Github
Für einen ersten Testbetrieb sind hier jedoch keine Änderungen erforderlich, es dient lediglich dazu ggf. podman an die eigenen Wünsche anzupassen.
Arbeiten mit Podman
Podman wurde als Drop-In-Replacement für Docker entworfen und daher funktionieren die meisten Befehle wie ps
, rm
, inspect
, logs
oder exec
analog zu docker und sollen hier wenn dann nur kurz erwähnt werden. Die Funktionalität ist dabei nicht nur auf den Betrieb von Container beschränkt, es ist im begrenzten Maße auch möglich Container zu erstellen. Im Hintergrund setzt Podman auf die Funktionalität von Buildah, kann jedoch Container nur aus einem Containerfile heraus erstellen.
Details dazu finden sich in der Dokumentation
Auch ein podman top $ContainerID
funktioniert ebenso wie das Erstellen, Migrieren und Restore eines Checkpoints.
[user@system1 ~]$ podman container checkpoint <container_id> -e /tmp/checkpoint.tar.gz [user@system1 ~]$ scp /tmp/checkpoint.tar.gz <destination_system>:/tmp [user@system2 ~]$ podman container restore -i /tmp/checkpoint.tar.gz
In den folgenden Bereichen soll daher in erster Linie auf die Unterschiede in der Containerhandhabe zwischen Docker und Podman eingegangen werden.
Einen Container starten
Um einen Container der Wahl (hier postgres) zu starten, pullen wir das Image und starten dies anschließend.
[podmanager@buildah ~]$ podman pull postgres ... Copying config 73119b8892 done Writing manifest to image destination Storing signatures 73119b8892f9cda38bb0f125b1638c7c0e71f4abe9a5cded9129c3f28a6d35c3 [podmanager@buildah ~]$ podman inspect postgres | grep "PG_VERSION=" "PG_VERSION=12.2-2.pgdg100+1", "created_by": "/bin/sh -c #(nop) ENV PG_VERSION=12.2-2.pgdg100+1", [podmanager@buildah ~]$ podman run -d -e POSTGRES_PASSWORD=SuperDB --name=postgres_dev postgres c8b9732b6ad253710ae6e75f934a74e8469e61bc5b5d88c2fa92c7257d00d2e7 [podmanager@buildah ~]$ podman ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES c8b9732b6ad2 docker.io/library/postgres:latest postgres 5 seconds ago Up 4 seconds ago postgres_dev
Es läuft nun ein PostgreSQL®-Container mit dem Namen „postgres_dev“. Dies unterscheidet sich soweit nicht von Docker.
Die Besonderheit von podman ist erst in der Prozessliste ersichtlich:
podmana+ 2209 1 0 13:11 ? Ssl 0:00 /usr/bin/conmon --api-version 1 -c c8b9732b6ad253710ae6e75f934a74e8469e61bc5b5d88c2fa92c7257d00d2e7 -u c8b9732b6ad253710ae6e75f934a74e8469e61bc5b5d88c2fa92c7257d00d2e7 -r /usr/bin/runc -b 232070 2219 2209 0 13:11 ? Ss 0:00 \_ postgres
Der PostgreSQL®-Prozess läuft nicht als Kind eines Daemon-Prozesses, sondern der Komponente „conmon“.
Diese überwacht den Zustand des Containers nach dem Start. Außerdem stellt es den Socket für die Kommunikation bereit und den Stream für den Output, welche
in das von podman konfigurierte Log geschrieben werden.
Weiterführende Informationen zu conmon finden sich auf Github
Starten wir jetzt einen zweiten Container (postgres_prod) via podman wird ein weiterer conmon-Prozess gestartet:
[podmanager@buildah ~]$ podman run -d -e POSTGRES_PASSWORD=SuperDB --name=postgres_prod postgres 6581a25c82620c725fe1cfb6546479edac856228ecb3c11ad63ab95a453c1b64 [podmanager@buildah ~]$ podman ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 6581a25c8262 docker.io/library/postgres:latest postgres 15 seconds ago Up 15 seconds ago postgres_prod c8b9732b6ad2 docker.io/library/postgres:latest postgres 7 minutes ago Up 7 minutes ago postgres_dev
podmana+ 2209 1 0 13:11 ? Ssl 0:00 /usr/bin/conmon --api-version 1 -c c8b9732b6ad253710ae6e75f934a74e8469e61bc5b5d88c2fa92c7257d00d2e7 -u c8b9732b6ad253710ae6e75f934a74e8469e61bc5b5d88c2fa92c7257d00d2e7 -r /usr/bin/runc -b 232070 2219 2209 0 13:11 ? Ss 0:00 \_ postgres ... podmana+ 2337 1 0 13:19 ? Ssl 0:00 /usr/bin/conmon --api-version 1 -c 6581a25c82620c725fe1cfb6546479edac856228ecb3c11ad63ab95a453c1b64 -u 6581a25c82620c725fe1cfb6546479edac856228ecb3c11ad63ab95a453c1b64 -r /usr/bin/runc -b 232070 2348 2337 0 13:19 ? Ss 0:00 \_ postgres ...
Hier finden sich im Prozess jeweils die UUIDs der Container wieder.
Der cmdline des Prozesses ist natürlich sehr viel länger als hier dargestellt. Folgend noch ein komplettes Beispiel manuell formatiert:
[podmanager@buildah ~]$ ps -ef f | grep conmon ... podmana+ 2209 1 0 13:11 ? Ssl 0:00 /usr/bin/conmon --api-version 1 -c c8b9732b6ad253710ae6e75f934a74e8469e61bc5b5d88c2fa92c7257d00d2e7 -u c8b9732b6ad253710ae6e75f934a74e8469e61bc5b5d88c2fa92c7257d00d2e7 -r /usr/bin/runc -b /home/podmanager/.local/share/containers/storage/overlay-containers/c8b9732b6ad253710ae6e75f934a74e8469e61bc5b5d88c2fa92c7257d00d2e7/userdata -p /var/tmp/run-1002/containers/overlay-containers/c8b9732b6ad253710ae6e75f934a74e8469e61bc5b5d88c2fa92c7257d00d2e7/userdata/pidfile -l k8s-file:/home/podmanager/.local/share/containers/storage/overlay-containers/c8b9732b6ad253710ae6e75f934a74e8469e61bc5b5d88c2fa92c7257d00d2e7/userdata/ctr.log --exit-dir /var/tmp/run-1002/libpod/tmp/exits --socket-dir-path /var/tmp/run-1002/libpod/tmp/socket --log-level error --runtime-arg --log-format=json --runtime-arg --log --runtime-arg=/var/tmp/run-1002/containers/overlay-containers/c8b9732b6ad253710ae6e75f934a74e8469e61bc5b5d88c2fa92c7257d00d2e7/userdata/oci-log --conmon-pidfile /var/tmp/run-1002/containers/overlay-containers/c8b9732b6ad253710ae6e75f934a74e8469e61bc5b5d88c2fa92c7257d00d2e7/userdata/conmon.pid --exit-command /usr/bin/podman --exit-command-arg --root --exit-command-arg /home/podmanager/.local/share/containers/storage --exit-command-arg --runroot --exit-command-arg /var/tmp/run-1002/containers --exit-command-arg --log-level --exit-command-arg error --exit-command-arg --cgroup-manager --exit-command-arg cgroupfs --exit-command-arg --tmpdir --exit-command-arg /var/tmp/run-1002/libpod/tmp --exit-command-arg --runtime --exit-command-arg runc --exit-command-arg --storage-driver --exit-command-arg overlay --exit-command-arg --storage-opt --exit-command-arg overlay.mount_program=/usr/bin/fuse-overlayfs --exit-command-arg --storage-opt --exit-command-arg overlay.mount_program=/usr/bin/fuse-overlayfs --exit-command-arg --events-backend --exit-command-arg file --exit-command-arg container --exit-command-arg cleanup --exit-command-arg c8b9732b6ad253710ae6e75f934a74e8469e61bc5b5d88c2fa92c7257d00d2e7 ...
Durch die Formatierung ist sehr schön zu sehen, wie die Übergabe von Parametern zwischen podman und conmon über die *args funktioniert.
Zusätzlich zu conmon wird natürlich auch jeweils eine Instanz von slirp4netns und fuse-overlayfs pro container gestartet um Netzwerk und Storage ohne root-Berechtigungen bereitzustellen.
podmana+ 2201 1 0 13:11 ? Ss 0:00 /usr/bin/fuse-overlayfs -o lowerdir=/home/podmanager/.local/share/containers/storage/overlay/l/FX4RZGGJ5HSNVMGVFG6K3I7PIL:/home/podmanager/.local/share/containers/storage/overlay/l/AIHUOS podmana+ 2206 1 0 13:11 pts/0 S 0:00 /usr/bin/slirp4netns --disable-host-loopback --mtu 65520 --enable-sandbox -c -e 3 -r 4 --netns-type=path /tmp/run-1002/netns/cni-18902a12-5b1b-15d3-0c31-138efe1d66ba tap0 podmana+ 2209 1 0 13:11 ? Ssl 0:00 /usr/bin/conmon --api-version 1 -c c8b9732b6ad253710ae6e75f934a74e8469e61bc5b5d88c2fa92c7257d00d2e7 -u c8b9732b6ad253710ae6e75f934a74e8469e61bc5b5d88c2fa92c7257d00d2e7 -r /usr/bin/runc -b 232070 2219 2209 0 13:11 ? Ss 0:00 \_ postgres
Erstellen eines Systemd-service-files
Da die Container ohne Daemon laufen und einzeln angestartet werden können, bietet es sich natürlich auch an diese nicht via Docker, sondern via Systemd zu steuern.
Das Schreiben von Service-files ist jedoch im allgemeinen mühsam, weswegen podman dafür direkt eine Funktion eingebaut hat.
Folgend ein Beispiel für unsere postgres_dev
[podmanager@buildah ~]$ podman generate systemd postgres_dev # container-c8b9732b6ad253710ae6e75f934a74e8469e61bc5b5d88c2fa92c7257d00d2e7.service # autogenerated by Podman 1.8.2 # Tue Mar 24 13:47:11 CET 2020 [Unit] Description=Podman container-c8b9732b6ad253710ae6e75f934a74e8469e61bc5b5d88c2fa92c7257d00d2e7.service Documentation=man:podman-generate-systemd(1) Wants=network.target After=network-online.target [Service] Environment=PODMAN_SYSTEMD_UNIT=%n Restart=on-failure ExecStart=/usr/bin/podman start c8b9732b6ad253710ae6e75f934a74e8469e61bc5b5d88c2fa92c7257d00d2e7 ExecStop=/usr/bin/podman stop -t 10 c8b9732b6ad253710ae6e75f934a74e8469e61bc5b5d88c2fa92c7257d00d2e7 PIDFile=/var/tmp/run-1002/containers/overlay-containers/c8b9732b6ad253710ae6e75f934a74e8469e61bc5b5d88c2fa92c7257d00d2e7/userdata/conmon.pid KillMode=none Type=forking [Install] WantedBy=multi-user.target default.target
Einen Fehler gibt es hier jedoch noch. Es muss im Servicefile der User ergänzt werden unter dem der Container gestartet werden soll, insofern dies nicht als root passieren soll. Dazu muss in der Sektion [Service]
nur User=podmanager
ergänzt werden (bzw. der Username auf ihrem System).
Um den Container als Service zu hinterlegen wären unter CentOS 8 folgende Schritte durchzuführen:
[podmanager@buildah ~]$ podman generate systemd --files --name postgres_dev /home/podmanager/container-postgres_dev.service # User= im servicefile ergänzen [podmanager@buildah ~]$ sudo cp /home/podmanager/container-postgres_dev.service /etc/systemd/system/ [podmanager@buildah ~]$ sudo systemctl daemon-reload [podmanager@buildah ~]$ sudo systemctl start container-postgres_dev.service [podmanager@buildah ~]$ systemctl status container-postgres_dev.service ● container-postgres_dev.service - Podman container-postgres_dev.service Loaded: loaded (/etc/systemd/system/container-postgres_dev.service; disabled; vendor preset: disabled) Active: active (running) since Tue 2020-03-24 14:04:14 CET; 1s ago Docs: man:podman-generate-systemd(1) Process: 7691 ExecStart=/usr/bin/podman start postgres_dev (code=exited, status=0/SUCCESS) Main PID: 7717 (conmon) Tasks: 11 (limit: 25028) Memory: 46.7M CGroup: /system.slice/container-postgres_dev.service ├─7710 /usr/bin/fuse-overlayfs -o lowerdir=/home/podmanager/.local/share/containers/storage/overlay/l/FX4RZGGJ5HSNVMGVFG6K3I7PIL:/home/podmanager/.local/share/containers/storage/overlay/l/AIHUOSIVGT5DN5GCUR7PRELVKK:/home/podma> ├─7714 /usr/bin/slirp4netns --disable-host-loopback --mtu 65520 --enable-sandbox -c -e 3 -r 4 --netns-type=path /tmp/run-1002/netns/cni-a0ee9d78-2f8c-a563-1947-92d0766a43b7 tap0 ├─7717 /usr/bin/conmon --api-version 1 -c c8b9732b6ad253710ae6e75f934a74e8469e61bc5b5d88c2fa92c7257d00d2e7 -u c8b9732b6ad253710ae6e75f934a74e8469e61bc5b5d88c2fa92c7257d00d2e7 -r /usr/bin/runc -b /home/podmanager/.local/share/c> ├─7727 postgres ├─7758 postgres: checkpointer ├─7759 postgres: background writer ├─7760 postgres: walwriter ├─7761 postgres: autovacuum launcher ├─7762 postgres: stats collector └─7763 postgres: logical replication launcher Mär 24 14:04:13 buildah.localdomain systemd[1]: Starting Podman container-postgres_dev.service... Mär 24 14:04:14 buildah.localdomain podman[7691]: postgres_dev Mär 24 14:04:14 buildah.localdomain systemd[1]: Started Podman container-postgres_dev.service.
Wichtig zu erwähnen ist noch, dass man den über Systemd gestarteten Container natürlich auch via podman administrieren / erreichen kann, zumindest das Starten und Stoppen aber dem Service überlassen sollte.
Erstellen eines Pods
Wie der Name vermuten lässt ist es mit Podman nicht nur möglich Container zu erstellen, sondern diese auch in Pods zu organisieren.
Ein Pod stellt dabei analog zu Kubernetes einen organisatorischen Verbund von Containern dar die sich auch bestimmte Namespaces wie pids, network o.ä. teilen können.
Diese werden dann via podman pod $cmd
administriert.
Im Folgenden laden wir das Image des postgres-exporters für prometheus und erstellen daraus einen Pod mit der Bezeichnung postgres-prod-pod.
[podmanager@buildah ~]$ podman pull docker.io/wrouesnel/postgres_exporter [podmanager@buildah ~]$ podman pod create --name postgres-prod-pod 727e7544515e0e683525e555934e02a341a42009a9c49fb2fd53094187a1e97c [podmanager@buildah ~]$ podman run -d --pod postgres-prod-pod -e POSTGRES_PASSWORD=SuperDB postgres:latest 8f313260973ef6eb6fa84d2893875213cee89b48c93d08de7642b0a8b03c4a88 [podmanager@buildah ~]$ podman run -d --pod postgres-prod-pod -e DATA_SOURCE_NAME="postgresql://postgres:password@localhost:5432/postgres?sslmode=disable" postgres_exporter fee22f24ff9b2ace599831fa022fb1261ef836846e0ba938c7b469d8dfb8a48a [podmanager@buildah ~]$ podman pod ps POD ID NAME STATUS CREATED # OF CONTAINERS INFRA ID 727e7544515e postgres-prod-pod Running 48 seconds ago 3 6edc862441f1 [podmanager@buildah ~]$ podman pod inspect postgres-prod-pod { "Config": { "id": "727e7544515e0e683525e555934e02a341a42009a9c49fb2fd53094187a1e97c", "name": "postgres-prod-pod", "hostname": "postgres-prod-pod", "labels": { }, "cgroupParent": "/libpod_parent", "sharesCgroup": true, "sharesIpc": true, "sharesNet": true, "sharesUts": true, "infraConfig": { "makeInfraContainer": true, "infraPortBindings": null }, "created": "2020-03-24T14:44:27.01249721+01:00", "lockID": 0 }, "State": { "cgroupPath": "/libpod_parent/727e7544515e0e683525e555934e02a341a42009a9c49fb2fd53094187a1e97c", "infraContainerID": "6edc862441f18234f0c61693f11d946f601973a71b85fa9d777273feed68ed3c", "status": "Running" }, "Containers": [ { "id": "6edc862441f18234f0c61693f11d946f601973a71b85fa9d777273feed68ed3c", "state": "running" }, { "id": "8f313260973ef6eb6fa84d2893875213cee89b48c93d08de7642b0a8b03c4a88", "state": "running" }, { "id": "fee22f24ff9b2ace599831fa022fb1261ef836846e0ba938c7b469d8dfb8a48a", "state": "running" } ] }
Hier ist nun zu sehen, dass der Pod die beiden Container postgres und postgres-exporter beinhaltet (Vergleich UUIDs).
Der dritte Container ist der Infra-Container für das Bootstrapping und soll hier nicht weiter Thema sein.
In der Prozessliste zeigt sich das Ganze dann wie folgt:
podmana+ 6279 1 0 14:44 ? Ss 0:00 /usr/bin/fuse-overlayfs -o lowerdir=/home/podmanager/.local/share/containers/storage/overlay/l/ podmana+ 6281 1 0 14:44 pts/2 S 0:00 /usr/bin/slirp4netns --disable-host-loopback --mtu 65520 --enable-sandbox -c -e 3 -r 4 --netns-type=path /tmp/run-1002/netns/cni-5fe4356e-77c0-8127-f72b-7335c2ed05c4 tap0 podmana+ 6284 1 0 14:44 ? Ssl 0:00 /usr/bin/conmon --api-version 1 -c 6edc862441f18234f0c61693f11d946f601973a71b85fa9d777273feed68ed3c -u 6edc862441f18234f0c61693f11d946f601973a71b85fa9d777273feed68ed3c -r /usr/bin/runc -b podmana+ 6296 6284 0 14:44 ? Ss 0:00 \_ /pause podmana+ 6308 1 0 14:44 ? Ss 0:00 /usr/bin/fuse-overlayfs -o lowerdir=/home/podmanager/.local/share/containers/storage/overlay/l/FX4RZGGJ5HSNVMGVFG6K3I7PIL:/home/podmanager/.local/share/containers/storage/overlay/l/AIHUOS podmana+ 6312 1 0 14:44 ? Ssl 0:00 /usr/bin/conmon --api-version 1 -c 8f313260973ef6eb6fa84d2893875213cee89b48c93d08de7642b0a8b03c4a88 -u 8f313260973ef6eb6fa84d2893875213cee89b48c93d08de7642b0a8b03c4a88 -r /usr/bin/runc -b 232070 6322 6312 0 14:44 ? Ss 0:00 \_ postgres ... 232070 6549 6322 0 14:44 ? Ss 0:00 \_ postgres: postgres postgres ::1(51290) idle podmana+ 6520 1 0 14:44 ? Ss 0:00 /usr/bin/fuse-overlayfs -o lowerdir=/home/podmanager/.local/share/containers/storage/overlay/l/P5NJW4TB6JUFOBBIN2MOHW7272:/home/podmanager/.local/share/containers/storage/overlay/l/KVC54Z podmana+ 6523 1 0 14:44 ? Ssl 0:00 /usr/bin/conmon --api-version 1 -c fee22f24ff9b2ace599831fa022fb1261ef836846e0ba938c7b469d8dfb8a48a -u fee22f24ff9b2ace599831fa022fb1261ef836846e0ba938c7b469d8dfb8a48a -r /usr/bin/runc -b 251072 6534 6523 0 14:44 ? Ssl 0:00 \_ /postgres_exporter
Hier ist zu sehen, dass es zwar mehrere conmon und overlayfs-Prozesse für die Container gibt, jedoch nur einen slirp4netns, da die Container sich diesen teilen und darüber auch via localhost kommunizieren können.
Auch ist zu sehen dass die PostgreSQL®-Datenbank eine Verbindung von localhost hat (PID 6549) wobei es sich um den Exporter handelt.
Im Normalfall werden bei der Erstellung eines Pods via podman pod create
folgende Namespaces für die Container zusammengefasst: net,ipc,uts und user.
Somit hat auch jeder Container trotz der Zusammenfassung im Pod noch seinen eigenen PID-namespace.
Sollte dies jedoch gewünscht sein, so kann mittels dem Parameter --share
bei der Erstellung angegeben werden was alles geteilt werden soll.
So sieht z.B. die Prozessliste des Pods ohne gemeinsamen pid-namespace aus. Jeder Container hat seine eigene Prozesstruktur.
[podmanager@buildah ~]$ podman pod top postgres-prod-pod USER PID PPID %CPU ELAPSED TTY TIME COMMAND 0 1 0 0.000 11m45.845291911s ? 0s /pause postgres 1 0 0.000 11m45.854330176s ? 0s postgres postgres 25 1 0.000 11m45.854387876s ? 0s postgres: checkpointer postgres 26 1 0.000 11m45.854441615s ? 0s postgres: background writer postgres 27 1 0.000 11m45.854483844s ? 0s postgres: walwriter postgres 28 1 0.000 11m45.854525645s ? 0s postgres: autovacuum launcher postgres 29 1 0.000 11m45.854567082s ? 0s postgres: stats collector postgres 30 1 0.000 11m45.854613262s ? 0s postgres: logical replication launcher postgres 31 1 0.000 11m45.854653703s ? 0s postgres: postgres postgres ::1(51292) idle postgres_exporter 1 0 0.000 11m45.859505449s ? 0s /postgres_exporter
Als Alternative die Ausgabe bei der Erstellung des Pods mittels podman pod create --name postgres-prod-pod --share=pid,net,ipc,uts,user
[podmanager@buildah ~]$ podman pod top postgres-prod-pod USER PID PPID %CPU ELAPSED TTY TIME COMMAND root 1 0 0.000 20.396867487s ? 0s /pause postgres 6 0 0.000 20.396936905s pts/0 0s postgres postgres 60 6 0.000 18.396997034s ? 0s postgres: checkpointer postgres 61 6 0.000 18.397086198s ? 0s postgres: background writer postgres 62 6 0.000 18.39713465s ? 0s postgres: walwriter postgres 63 6 0.000 18.39718056s ? 0s postgres: autovacuum launcher postgres 64 6 0.000 18.397229737s ? 0s postgres: stats collector postgres 65 6 0.000 18.397279102s ? 0s postgres: logical replication launcher 20001 66 0 0.000 16.397325377s pts/0 0s /postgres_exporter
Zur Auswahl stehen bei --share
ipc, net, pid, user und uts.
Der gesamte pod kann nun auch via podman pod stop/start
gestartet und gestoppt werden.
Ebenso können für Pods wie Container systemd-service-files generiert werden.
Podman und Kubernetes
Podman bietet einige Unterstützung im Bereich Kubernetes-YAML.
So ist es z.B. möglich aus den Pods, welche man mit podman erstellt hat, kubernes-Pod YAML zu generieren.
[podmanager@buildah ~]$ podman generate kube postgres-prod-pod -f postgres-prod-pod.yaml [podmanager@buildah ~]$ cat postgres-prod-pod.yaml # Generation of Kubernetes YAML is still under development! # # Save the output of this file and use kubectl create -f to import # it into Kubernetes. # # Created with podman-1.8.2 apiVersion: v1 kind: Pod metadata: creationTimestamp: "2020-03-24T14:08:14Z" labels: app: postgres-prod-pod name: postgres-prod-pod spec: containers: - command: - postgres env: ... image: docker.io/library/postgres:latest name: reverentptolemy resources: {} securityContext: allowPrivilegeEscalation: true capabilities: {} privileged: false readOnlyRootFilesystem: false seLinuxOptions: {} workingDir: / - env: ... image: docker.io/wrouesnel/postgres_exporter:latest name: friendlyshirley resources: {} securityContext: allowPrivilegeEscalation: true capabilities: {} privileged: false readOnlyRootFilesystem: false runAsGroup: 20001 runAsUser: 20001 seLinuxOptions: {} workingDir: / status: {}
Mit der Option -s
wird sogar noch ein Service mit ggf. published-Ports generiert:
--- apiVersion: v1 kind: Service metadata: creationTimestamp: "2020-03-24T14:10:42Z" labels: app: postgres-prod-pod name: postgres-prod-pod spec: selector: app: postgres-prod-pod type: NodePort status: loadBalancer: {}
Die generate-yaml Funktionalität greift dabei für Pods wie auch für Services und auch in beide Richtungen.
Mit podman play
ist es möglich Pod und Container-Definitionen in podman zu testen.
Fazit
Dies war ein grober Einblick in die Möglichkeiten Container ohne Daemon und root-Berechtigungen auf einem Host zu betreiben.
Natürlich ist mit podman noch viel mehr möglich, jedoch würde die Erläuterung jeder Option hier den Rahmen sprengen.
Zu erwähnen ist vielleicht noch, dass Podman mit aktiviertem cgroupsV2 auch z.B. Resourcennutzung in Pods auswerten kann.
Dies ist jedoch aktuell nur unter Fedora31 standardmäßig aktiviert.
Lange Zeit war Docker das Standard-Tool zum Erstellen und Betreiben von Containern.
Durch die starke Verbreitung der Technologie gibt es mittlerweile deutlich mehr Tools, die einzelne Teilbereiche des Gespannes aus Docker-Daemon und Docker-CLI übernehmen. Auch im Hinblick auf den Aspekt der Sicherheit ist der root-Rechte benötigende Docker-Daemon für viele Nutzer nicht unbedingt die erste Wahl.
Buildah ist neben Podman und Skopeo Teil der RedHat-Containertools und übernimmt den Abschnitt für Erstellung und Modifizierung von Containern und Images.
Die Tools wurden dabei nach dem bekannten Unix-Prinzip entwickelt, so dass es kleinere Programme beinhaltet, die jedoch gut zusammenarbeiten und jeweils einen eigenen Teilbereich abdecken.
Entwicklung
Buildah wurde in der Version 1.0 am 07.05.2018 veröffentlicht und ist unter Apache 2.0 lizenziert. Die Implementierung erfolgt in Golang und wird in erster Instanz von der „containers organisation“ vorangetrieben. Diese beinhaltet sowohl RedHat-Mitarbeiter, wie auch externe Entwickler.
Der Code kann auf Github eingesehen werden. Die Entwicklung erfolgt dabei in keinem festen Releasezyklus. So können sowohl Monate wie auch Wochen zwischen den Versionen liegen, je nachdem ob genügend neue Features für einen Release implementiert wurden.
Container im Userspace
Eine zentrale Komponente von Buildah und auch Podman ist die libpod, die es ermöglicht Container im Userspace zu starten und Images zu erstellen. Diese setzt auf slirp4netns, fuse-overlayfs und die /etc/sub(u|g)id auf.
slirp4netns
Seit Kernelversion 3.8 sind network- bzw. usernamespaces nutzbar. Jedoch werden für die Erstellung eines Interfaces, das diese nutzen kann, immer noch root-Rechte benötigt.
slirp4netns umgeht diese Problematik, in dem es ein TAP-Device innerhalb des neuen Namespaces mit dem usermode TCP-IP-Stack verbindet. Details dazu sind im Projekt auf Github zu finden.
fuse-overlayfs
fuse-overlayfs stellt eine Implementierung von overlay+shiftfs in FUSE (Filesystem in Userspace) bereit. Dieses ermöglicht Nutzern Container-Dateisysteme auch ohne root-Rechte zu erstellen und anzupassen. Details dazu finden sich auf dem Projekt-Github.
/etc/sub(u|g)id
Sowohl /etc/subuid als auch /etc/subgid sind Teil der shadow-utils auf aktuellen Systemen und definieren welche uids und gids ein Nutzer des Systems beanspruchen darf.
Als Standard bekommt jeder neu angelegte Nutzer eines Systems 65536 ids zugeteilt (sowohl uid, wie auch gid). Es sind jedoch auch größere oder mehrere Ranges möglich. Diese müssen allerdings manuell angepasst werden und es ist darauf zu achten, dass es keine Überschneidungen gibt. Auch ist es möglich, dass beiden Dateien auf älteren Systemen manuell angelegt / angepasst werden müssen. Mindestens seit Fedora 30 sind diese Teil der Standardinstallation.
Diese IDs werden beim Betrieb von libpod für die uid/gid innerhalb von Containern genutzt.
Folgend ein Beispiel:
[builder@buildah ~]$ cat /etc/subuid builder:100000:65536 wracker:165536:65536 [builder@buildah ~]$ cat /etc/subgid builder:100000:65536 wracker:165536:65536
Hier ist zu sehen, dass der Nutzer builder
uids/gids im Bereich von 100000 bis 165535 nutzen darf. Neue Nutzer bekommen (ohne manuelle Anpassungen) den entsprechend nächsten Block zugeteilt.
Installation
Buildah ist auf den gängigen Distributionen entweder direkt paketiert (z.B. Fedora, Archlinux, Suse, Gentoo) oder aber per Dritt-Repo verfügbar (Debian und Derivate).
Unter Fedora ist es zum Zeitpunkt der Erstellung dieses Artikel noch notwendig zusätzlich das Paket seccomp
zu installieren, da es hier in der letzten Version Probleme mit den Abhängigkeiten gibt. So wäre der Aufruf sudo dnf install buildah libseccomp
unter Fedora und sudo pacman -S buildah
unter Archlinux.
Für die Debian-Derivate gibt es das Kubic-Repository, in dem aktuelle Pakete verfügbar sind.
Debian selbst enthält das Paket zunächst nur in den unstable-Repositories. Für Ubuntu oder Debian stable muss Kubic genutzt werden.
Selbstredend ist eine manuelle Installation über die Quellen ebenfalls möglich. Eine Komplette Liste der Installationsmöglichkeiten findet sich auf Github.
Nach der Installation von Buildah sollte der Aufruf in etwa die folgende Ausgabe liefern:
[builder@buildah ~]$ buildah A tool that facilitates building OCI images Usage: buildah [flags] buildah [command] # ...
Konfiguration
Die Konfiguration findet in einzelnen Dateien im Ordner /etc/containers
für die globale Konfiguration statt, oder in $HOME/.config/containers
für die Konfiguration der einzelnen Nutzer. Die Nutzerkonfiguration überschreibt dabei wie immer die globale.
Folgende Dateien sind dabei in den Ordnern, bzw. mindestens global, zu finden:
- storage.conf – Optionen und Konfiguration für Storage
- mounts.conf – automatischer Mount in einen Container bei der Erstellung
- registries.conf – Registrykonfiguration V1
- registries.d – Registrykonfiguration V2
storage.conf
Hier wird definiert mit welchem Treiber und in welchen Ordner die Images bzw. der interne Storage der Container gespeichert werden soll.
Beispiel der globalen Konfiguration:
[root@buildah containers]# grep -v "^#\|^$" /etc/containers/storage.conf [storage] driver = "overlay" runroot = "/var/run/containers/storage" graphroot = "/var/lib/containers/storage" # ...
Beispiel einer Konfiguration eines Nutzers:
[builder@buildah containers]$ cat ~/.config/containers/storage.conf [storage] driver = "overlay" runroot = "/var/tmp/1000" graphroot = "/home/builder/.local/share/containers/storage" # ...
mounts.conf
Hier wird pro Zeile ein Mount definiert, der immer in jeden auf dem System bzw. unter dem Nutzer gestarteten Container eingebunden werden soll.
Diese Möglichkeit wird meist genutzt um häufig genutzt Credentials oder Zertifikate in einen Container zu mounten (z.B: /usr/share/rhel/secrets:/run/secrets
für RHEL-Subscriptions). Hier ist ggf. darauf zu achten, dass die Mount-Wünsche nicht mit bestehenden SELinux-Konfigurationen kollidieren. Im Problemfall wäre hier das /var/log/audit/audit.log
zu prüfen.
registries.conf
Konfiguration der verfügbaren Container-Registries im V1-Format. Hier können analog zu Docker-Konfiguration die genutzten Registries freigegeben oder auch ungewollte Blockiert werden. Bei Mehrfachangaben werden die Registries der Reihe nach angefragt.
Auch wenn es sich hier um „V1“ handelt und „V2“ bereits verfügbar ist, so ist dies die stabile und auch empfohlene Variante seine Repositories zu definieren.
Beispiel einer registries.conf:
[root@buildah containers]# grep -v "^#\|^$" /etc/containers/registries.conf [registries.search] registries = ['registry.access.redhat.com', 'registry.fedoraproject.org', 'registry.centos.org', 'docker.io'] [registries.insecure] registries = [] [registries.block] registries = []
registries.d
Stellt die aktuell noch in der Beta befindlichen Version 2 der Registry-Konfiguration dar. Im Ordner registries.d
finden sich verschiedene Dateien,
welche jeweils Registries konfigurieren.
In dieser Version wird auf das Feature der sigstores
(Signature-Stores) gesetzt, welches jedoch aktuell nur von wenigen Registries überhaupt unterstützt wird.
Es wird dabei zwischen sigstore-staging
(nur schreibend) und sigstore
(lesend und schreibend) unterschieden. So soll das Image z.B. im sigstore-staging
nur erstellt werden um es anschließend im sigstore
zu signieren und zu veröffentlichen.
Folgend eine Beispielkonfiguration:
docker: privateregistry.com: sigstore: http://privateregistry.com/sigstore/ sigstore-staging: /mnt/nfs/privateregistry/sigstore
Da es sich bei dem Feature jedoch um ein Beta-Feature ohne breite Unterstützung handelt soll hier an dieser Stelle nicht weiter darauf eingegangen werden.
Details dazu finden sich auf Github.
Buildah – CLI
Viele Befehle der Buildah-CLI sind mit der Docker-CLI soweit kompatibel und nur um eigene Optionen ergänzt. So z.B. ps
, images
, pull
, push
, rm
, usw.
Im Unterschied zu Docker kann jedoch auch die CLI genutzt werden um neue Container und Images zu erstellen. Dafür stehen dann Optionen wie run,add,copy o.ä. bereit. Dies erlaubt eine sehr gute Automatisierbarkeit bzw. Parametrisierbarkeit der Erstellung, da hier mittels einfacher Shell-Befehle statt eines Dockerfile-Images gebaut werden können.
Es werden bei der Erstellung zwei verschiedene Formate unterstützt, die sich jedoch nur im Manifest unterscheiden: DockerV2 und OCI (standard).
Erstellung eines Images mittels CLI
Der erste Schritt um ein Image zu erstellen lautet z.B. buildah from centos
, welches analog zum Dockerfile FROM das Basisimage definiert. Buildah wird das Image herunterladen und einen entsprechenden Container erstellen.
Um nun Anpassungen am Basisimage vorzunehmen kann z.B. buildah run $newcontainer dnf install wget
ausgeführt werden. Der run
Befehl führt ebenfalls, wieder analog zu Docker, einen Befehl innerhalb des Container aus. Um ENV-Variablen wie einen Proxy o.ä. zu definieren kann buildah config
genutzt werden.
So z.B. buildah config --created-by "DaniloE" $newcontainer
um den Autor zu definieren. Mit config
lassen sich diverse Optionen im Container setzen wie z.B. env, cmd, author, port, user. Details dazu unter man buildah-config
.
Um Dateien in einen Container zu kopieren kann copy/add genutzt werden. z.B. buildah copy/add $newcontainer ./hello /hello
.
copy
kopiert dabei Dateien eines lokalen Pfades in den Container während add
einen Pfad oder eine URL akzeptiert.
Um aus dem angepassten Container ein neues Image zu erstellen wird nach Abschluss der Anpassungen z.B. buildah commit $newcontainer hellocontainer
oder buildah commit $newcontainer docker://localhost:5000/hellocontainer
genutzt. Dieses erstellt ein Image aus 2 Layern (Basis-Image + alle Änderungen).
Sollte es aus z.B. Platzgründen nötig sein dies auf einen Layer zu reduzieren kann der Parameter --squash
genutzt werden. --rm
löscht bei diesem Aufruf auch gleich den Zugrunde liegenden Arbeitscontainer.
Zusammenfassend sähe ein Skript um einen Container zu bauen z.B. aus wie folgt:
newcontainer=$(buildah from centos) buildah run $newcontainer touch /test buildah copy $newcontainer ./hello /hello buildah config --cmd /hello $newcontainer buildah config --created-by "DaniloE" $newcontainer buildah config --author "DaniloE" $newcontainer buildah config --label name=simple-helloworld $newcontainer buildah commit --rm $newcontainer hellocontainer
Erstellung eines Images mittels Dockerfile
Buildah hat natürlich im Zuge der Docker-Kompatibilität eine Möglichkeit Container aus bestehenden Dockerfiles zu erstellen.
Hierzu wird der Aufruf buildah bud
(build-using-dockerfile) genutzt.
Es erstellt ein Image aus einem Containerfile und einer Buildcontext-URL.
Wird kein Buildcontext übergeben ist $(pwd) automatisch gesetzt und hier wird auf das Dockerfile erwartet. Es ist aber auch möglich mit Remote-Sourcen zu arbeiten.
Weitere Möglichkeiten wären z.B. als Context ein Git-Repository zu übergeben. Hier wird der Inhalt des Repositories in ein temporäres Verzeichnis ausgecheckt (default „/var/tmp“) und im Ergebnis das Dockerfile erwartet.
Alternativ funktioniert dies auch mit einer Archiv URL. Hier wird das Archiv (xz,bzip2,gzip) ebenfalls in das temporäre Verzeichnis heruntergeladen, entpackt und als Context behandelt
Mit z.B. -f files/Dockfile
wird der Pfad zum Dockerfile angegeben. Dies kann sowohl ein lokales Verzeichnis sein, oder aber ein Unterverzeichnis im Git oder Archiv.
Wichtig: Bei der Übergabe eines remote-context wird der Parameter immer auf den Inhalt der Remote Sources angewandt, nicht auf einen lokalen Pfad. Es ist also nicht möglich ein lokales Dockerfile und einen Remote-Context zu verbinden.
Weitere Informationen zu Parametern und Optionen sind unter man buildah-bud
ausführlich dokumentiert.
Folgend noch einige Beispielaufrufe zur Veranschaulichung:
buildah bud --iidfile "id.txt" --squash -t "hello:dev" .
- schreibt image-id nach hello.id
- fügt alle Layer am Ende auf einen zusammen
- tagged das Image als „hello:dev“
buildah bud github.com/scollier/purpletest
buildah bud -f dev/Containerfile https://10.10.10.1/docker/context.tar.gz
Erstellung eines Images „from scratch“
Buildah bietet auch die Option, Images komplett neu zu erstellen ohne ein vorangegangenes Basisimage zu nutzen.
Hierfür wird ein neuer Container mit buildah from scratch
erstellt.
Dieser beinhaltet keine Dateien sondern lediglich ein leeres Dateisystem, das man anschließend befüllt.
So können z.B. mit der lokalen Paketverwaltung Pakete installiert oder aber lediglich Binaries in das Dateisystem übertragen werden.
Folgend ein Beispielskript für die Erstellung eines Containers from scratch mit der CLI.
newcontainer=$(buildah from scratch) scratchmnt=$(buildah mount ${newcontainer}) dnf install --installroot ${scratchmnt} --releasever 30 bash coreutils --setopt install_weak_deps=false -y
Es wird ein neuer, leerer Container erstellt, das Dateisystem des Containers in ein temporäres Verzeichnis gemounted und mit Bordmitteln Pakete inkl. Abhängigkeiten installiert.
Für die Nutzung als unprivilegierter User gibt es hier jedoch einen wichtigen Punkt zu beachten.
So bricht das buildah mount
ohne root Rechte ab:
[builder@buildah ~]$ newcontainer=$(buildah from centos) [builder@buildah ~]$ buildah mount $newcontainer cannot mount using driver overlay in rootless mode. You need to run it in a `buildah unshare` session
Um diese Problematik zu umgehen muss die Containererstellung in einem separaten Namespace durchgeführt werden.
Dieser wird mittels buildah unshare
erstellt und auch direkt aktiviert. Da in diesem sowohl uid wie auch gid als root gesetzt sind lässt sich das FS entsprechend mounten.
[builder@buildah ~]$ buildah unshare [root@buildah ~]# newcontainer=$(buildah from centos) [root@buildah ~]# buildah mount $newcontainer /home/builder/.local/share/containers/storage/overlay/... [root@buildah ~]# ls -l /home/builder/.local/share/containers/storage/... insgesamt 16 -rw-r--r--. 1 root root 12076 5. Dez 2018 anaconda-post.log lrwxrwxrwx. 1 root root 7 5. Dez 2018 bin -> usr/bin drwxr-xr-x. 2 root root 6 5. Dez 2018 dev drwxr-xr-x. 47 root root 4096 5. Dez 2018 etc
Dieser Umstand klingt erst einmal problematisch, ist in der Praxis jedoch nicht, sofern man die Container automatisiert erstellt.
Ein Beispielskript findet sich unter man buildah-unshare
.
Vergleich Buildah und andere Lösungen
| Applikation | Punkte |
| ----------- | ---------------------------------------------- |
| Buildkit | * benötigt Daemon + CLI |
| | * schneller bei großen/Komplexen Builds |
| | * kompatibel mit verschiedenen Imagefile |
| | * z.B. Docker-/Mocker-/Gopperfile |
| | * rootless Mode noch experimental |
| Kaniko | * Nur Unterstützung für Dockerfiles |
| | * speziell für Builds in Containern/Kubernetes |
| | * Nur Kaniko-eigenes Image empfohlen |
| | * Empfohlen für Gitlab CI/CD [] |
Unterstützung
Falls Sie Unterstützung beim Einsatz von Docker oder Buildah benötigen, steht Ihnen unser Open Source Support Center zur Verfügung – Falls gewünscht auch 24 Stunden am Tag, an 365 Tagen im Jahr.
Wir freuen uns auf Ihre Kontaktaufnahme.