Managed Service Podman
Podman ist eine "daemonless" Container Engine, um OCI Container auf Ihrem Linux System entwickeln, administrieren und betreiben zu können. Die Container laufen im Kontext des Systembenutzers www-data und sind daher komplett unabhängig vom Root Benutzer.
Der Syntax von Podman ist grösstenteils identisch zu Docker, daher kann bei Bedarf der folgende Bash-Alias genutzt werden, um Docker-Befehle via podman zu starten:
alias docker=podman
Bitte beachten Sie dabei: In Zukunft kann sich die Syntax von Podman verändern und wird eventuell nicht mehr mit der von Docker identisch sein.
Die komplette Dokumentation zu Podman findet sich hier:
https://podman.readthedocs.io/en/latest/index.html
Cleanup-Jobs
Standardmässig erstellen wir bei der Installation von Podman zwei Cleanup-Jobs. Einer läuft jeweils in der Nacht von Sonntag auf Montag und löscht alle unbenutzten Images. Der Zweite läuft immer in der Nacht zum 1. des Monats und löscht alle unbenutzten Volumes.
Dies sind Sicherheitsmassnahmen, um den Platzbedarf von Podman so gering wie möglich zu halten und das Risiko zu vermindern, dass Ihre Festplatte vollläuft. Wenn Sie diese beiden Cleanup-Jobs anpassen möchten, können Sie uns gerne ein Ticket schreiben und wir werden diese gemäss Ihren Bedürfnissen konfigurieren.
Images und Registries
Suchen nach bestehenden Images auf den gängigen Container Registries:
podman search <search_term>
Filtern nach offiziellen Images:
podman search ghost --filter=is-official
Ein Image lokal verfügbar machen:
podman pull docker.io/library/ghost
Lokal vorhandene Images anzeigen:
podman images
Podman sucht in unterschiedlichen Registries. Damit sicherlich das richtige Image verwendet wird, sollte jeweils der vollständige Image-Name angegeben werden (z.B.: docker.io/library/ghost
anstatt ghost
).
Container starten
In diesem Beispiel starten wir einen Ghost Container, welcher ein simples CMS zur Verfügung stellt.
podman run -dt -p 8080:2368/tcp docker.io/library/ghost
Der Container wird im detached mode
gestartet, nach dem Start des Containers wird uns die Container ID am Prompt ausgegeben. Die Option -t
bewirkt, dass ein Pseudo-TTY kreiert wird, welches uns erlaubt, auf eine interaktive Shell zu verbinden.
Port Forwarding/Publishing wird mit der Option -p 8080:2368/tcp
eingerichtet, damit das CMS Ghost über den TCP Port 8080 auf dem Host System erreichbar ist.
Container Übersicht
Die Übersicht über laufende Container wird mit dem Befehl podman ps -a
angezeigt.
$ podman ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
5728ad900bc4 docker.io/library/ghost:latest node current/inde... 4 hours ago Up 4 hours ago 0.0.0.0:8080->2368/tcp gifted_edison
Container verbinden
Wir können uns mit dem Container über die Container ID verbinden. Die Container ID ist einsehbar über die Ausgabe von podman ps
.
$ podman attach b3376ff455a0
[2020-06-10 09:17:15] INFO "GET /" 200 512ms
Container Funktionstest
Der Ghost CMS Container ist nun erreichbar über den TCP Port 8080 auf dem Hostsystem.
Mittels eines einfachen curl
Aufrufs kann geprüft werden, ob das CMS im Container läuft:
$ curl localhost:8080| grep "og:site"
<meta property="og:site_name" content="Ghost" />
Container mit nine-manage-vhosts veröffentlichen
Die Managed Services Nginx und Apache2 ermöglichen es uns, die Container-Applikation mittels nine-manage-vhosts
im Internet zu veröffentlichen.
Zudem kann damit auch eine Verschlüsselung mittels Let's Encrypt-Zertifikaten vorgenommen werden.
Neuen vHost erstellen:
sudo nine-manage-vhosts virtual-host create testdomain.org --template=proxy --template-variable=PROXYPORT=8080
Let's Encrypt-Registrierung vornehmen:
sudo nine-manage-vhosts certificate register-client --contact-email=contact@yourcompany.org
Neues Let's Encrypt-Zertifikat erstellen und auf dem vHost anwenden:
sudo nine-manage-vhosts certificate create --virtual-host=testdomain.org
sudo nine-manage-vhosts virtual-host update testdomain.org --template=proxy_letsencrypt_https --template-variable=PROXYPORT=8080
Nur in Kombination mit dem Apache Webserver notwendig: Um einen automatischen Redirect von http nach https zu erzeugen, können Sie das Template proxy_letsencrypt_https_redirect
verwenden.
docker-compose
Ab Ubuntu 22.04 unterstützt Podman docker-compose
nativ und Ihre Installation beinhaltet docker-compose
v1.
Installieren Sie die neueste v2 Version von docker-compose
.
mkdir -p ~/bin
curl -L $(curl -s https://api.github.com/repos/docker/compose/releases/latest | jq -r '.assets[] | select(.name == "docker-compose-linux-x86_64") | .browser_download_url') -o ~/bin/docker-compose
chmod +x ~/bin/docker-compose
Wenn Sie Podman unter Ubuntu 20.04 verwenden, können Sie das native docker-compose
nicht nutzen. Sie können jedoch podman-compose
verwenden, dass dessen Verhalten nachbildet:
podman-compose
mkdir -p ~/bin
curl -o ~/bin/podman-compose https://raw.githubusercontent.com/containers/podman-compose/main/podman_compose.py
chmod +x ~/bin/podman-compose
ln -s ~/bin/podman-compose ~/bin/docker-compose # compatibility for the docker-compose command
Beispiel-Konfiguration für docker-compose
In diesem Beispiel lassen wir unser Ghost CMS mittels einer compose-Datei im Netzwerkmodus slirp4netns
laufen.
slirp4netns
ist Podmans Netzwerkstandard und muss nicht explizit angegeben werden.
Für die Datenbankanbindung greifen wir auf eine bereits existierende, lokal betriebene MySQL-Datenbank mit dem Namen
nmd_ghost
zu.
Der Inhalt der docker-compose.yaml
-Datei:
# Standardmässig nutzt das Ghost Image eine SQLite DB und benötigt daher keinen separaten Datenbank-Container.
# Die Verwendung der MySQL-Datenbank dient hier lediglich als Beispiel.
services:
ghost:
image: ghost:3-alpine
restart: always
ports:
- 8080:2368
environment:
# see https://docs.ghost.org/docs/config#section-running-ghost-with-config-env-variables
database__client: mysql
database__connection__host: localhost
database__connection__user: nmd_ghost
database__connection__password: EeNae5xaoapoh5RoDah1muwu
database__connection__database: nmd_ghost
# this url value is just an example, and is likely wrong for your environment!
url: http://testdomain.org
Diese docker-compose.yaml
-Datei wird nun mit dem docker-compose
-Befehl verwendet:
docker-compose -f docker-compose.yml up
Container mit dem System automatisch starten
Wir empfehlen, Systemd User Services zu erstellen, damit Ihre Container automatisch nach einem System-Neustart gestartet werden. Abhängig von Ihrer OS-Version gibt es zwei unterschiedliche Wege, den User-Service zu erstellen.
Obwohl neuere Podman-Versionen weiterhin die Erstellung einer Systemd Unit mittels podman generate systemd
unterstützen, empfehlen wir, von Ubuntu Noble an stattdessen Quadlets zu verwenden, da diese deutlich übersichtlichere und besser kontrollierbare Systemd Units generieren.
Alter Weg mittels podman generate (bis Ubuntu Jammy)
So generieren Sie Unit Files des Pods mit dem Namen examplepod
:
podman generate systemd --new --files --name examplepod
Mittels podman ps
und podman pod ps
können Sie die Namen Ihrer Pods und Container einsehen.
Als Nächstes kopieren Sie die vorher generierten Service-Dateien in Ihr systemd-user-Verzeichnis:
cp -pv /home/www-data/pod-examplepod.service /home/www-data/container-examplepod_ghost_1.service ~/.config/systemd/user/
Zum Abschluss aktivieren Sie so die entsprechenden Services:
systemctl --user enable container-examplepod_ghost_1.service
systemctl --user enable pod-examplepod.services
Neuer Weg mit Quadlets und Podlet (ab Ubuntu Noble)
Quadlet ist ein neues Format für die Erstellung von Systemd Services, das seit Version 4.4 in Podman verfügbar ist. Eine Quadlet-Datei endet auf .container
(oder .image
, .volume
, .network
, .kube
) und wird im Verzeichnis ~/.config/containers/systemd/
erstellt. Sie ähnelt dabei einer Systemd Unit-Datei: Alle Optionen und Sektionen, die in Standard-Systemd Units verfügbar sind (wie [Service]
oder [Install]
) werden unterstützt und an die daraus generierte Systemd Unit weitergegeben.
Zusätzlich gibt es eine weitere Sektion (z. B. [Container]
für eine .container
-Datei) mit einer Vielzahl von containerspezifischen Optionen, die hauptsächlich als Parameter für das generierte podman run
-Kommando verwendet werden. Eine komplette Dokumentation finden Sie auf der Ubuntu manpage.
Um Quadlet-Dateien nicht selbst erstellen zu müssen, gibt es das Tool Podlet, das fertige Quadlet-Dateien aus einem existierenden Objekt erstellt. Ab Ubuntu Noble ist Podlet auf allen Servern mit Managed Podman Service verfügbar.
Im folgenden Beispiel wird eine Quadlet-Datei auf Basis eines existierenden Containers mit dem Namen examplecontainer
erstellt:
podlet --unit-directory --install --overwrite generate container examplecontainer
Wrote to file: /home/www-data/.config/containers/systemd/ghost.container
Mit podman ps
und podman pod ps
können Sie die Namen Ihrer Pods und Container einsehen.
Die Quadlet-Datei wird in ~/.config/containers/systemd/
erstellt:
cat ~/.config/containers/systemd/
[Container]
Environment=NODE_ENV=development database__connection__filename=/var/lib/ghost/content/data/ghost.db
Image=docker.io/library/ghost
PodmanArgs=--tty
PublishPort=8080:2368/tcp
Volume=some-ghost-data:/home/www-data/ghost
[Install]
WantedBy=default.target
Falls Sie weitere Konfigurationsoptionen hinzufügen möchten, können Sie die Datei entsprechend bearbeiten. Anschliessend erstellen Sie eine Systemd-Unit aus der Quadlet-Datei:
systemctl --user daemon-reload
systemctl --user status ghost.service
Das daemon-reload
erstellt KEINE separate Systemd Unit-Datei. Stattdessen wird ein systemd generator verwendet, um die Unit zur Laufzeit aus dem Quadlet zu generieren. Daher kann auch kein systemctl --user enable/disable
auf dem Service verwendet werden. Trotzdem wird die Unit aufgrund der [Install]
-Sektion nach einem Neustart des Servers automatisch gestartet.
Den Systemd Service starten Sie so:
systemctl --user start ghost.service
systemctl --user status ghost.service
● ghost.service
Loaded: loaded (/home/www-data/.config/containers/systemd/ghost.container; generated)
Active: active (running) since Wed 2024-12-04 13:39:10 CET; 1s ago
Main PID: 540254 (conmon)
Tasks: 25 (limit: 3337)
Memory: 80.8M (peak: 80.8M)
CPU: 1.100s
CGroup: /user.slice/user-33.slice/user@33.service/app.slice/ghost.service
├─libpod-payload-805ff2ecbcaa737f9b969dd21323b0115fb3c46c7800ab88563663a1e41341b8
│ └─540256 node current/index.js
└─runtime
├─540239 /usr/bin/slirp4netns --disable-host-loopback --mtu=65520 --enable-sandbox --enable-seccomp --en>
├─540241 rootlessport
├─540246 rootlessport-child
└─540254 /usr/bin/conmon --api-version 1 -c 805ff2ecbcaa737f9b969dd21323b0115fb3c46c7800ab88563663a1e413>
Podlet kann Quadlets auch aus Podman-Kommandos oder compose-Dateien erstellen.
Eine vollständige Dokumentation zu Podlet finden Sie hier.
Netzwerk
Modus (Host, Bridged)
Im standardmässig aktivierten Bridged
-Modus teilen alle Container im selben Pod den gleichen Netzwerkbereich.
Deshalb besitzen alle Container dieselbe IP, MAC-Adresse und Port-Mappings.
Die Netzwerkkommunikation zwischen den Containern in einem Pod kann hergestellt werden über den localhost
.
Es gibt einen zweiten Modus, den sogenannten Host
-Modus. Dieser kann spezifiziert werden mit dem Parameter network=host
.
Im Host
-Modus ist der Netzwerkbereich nicht isoliert vom Container-Hostsystem.
Der startende Container bekommt keine eigene IP-Adresse zugewiesen.
Es ist daher möglich, auf jegliche lokalen Dienste wie z.B. einen MySQL Server auf Ihrem Managed Server zu verbinden. Sämtliche lokal laufenden Dienste sind somit aus dem Container heraus erreichbar.
Rootless
Alle unsere Container starten wir rootless
, das Netzwerk wird somit automatisch konfiguriert.
Port Zuweisung
Nur sogenannte "höhere Ports" können einem rootless Container zugewiesen werden. Alle Ports unter 1024 sind privilegiert und können für die Zuweisung nicht verwendet werden.
Statt die Applikation im Container auf Port 80 zu starten, verwenden wir einen höheren Port. In diesem Beispiel, ein Ghost Containernder auf den Port 2368 hört, veröffentlichen wir ihn mit dem Port 8080 auf dem localhost.
$ podman run -dt -p 8080:2368/tcp docker.io/library/ghost
Mit dem Parameter podman -P
kann die Port-Zuweisung automatisch durch Podman getriggert werden.
Überprüfung der verwendeten Ports:
$ podman port -a
c0194f22266c 2368/tcp -> 0.0.0.0:8080
Kommunikation von Container zu Container
Die Kommunikation zwischen zwei rootless Containern kann über mehrere Wege stattfinden. Am einfachsten ist es aber, die zugewiesenen Ports via dem Hostsystem dafür zu nutzen.
Laufende Container auflisten:
podman ps
Die verwendeten Ports und die eigene Host IP heraussuchen:
podman port <container_id>
ip a
Einen neuen Container laufen lassen, der mittels curl
auf die Host IP mit dem veröffentlichten Port zugreift:
podman run -it --rm fedora curl <HOST_IP_ADDRESS>:8080
Volumes: Speicher in Container mounten
Um Daten zu persistieren, müssen diese entweder in einem externen System wie einer Datenbank gespeichert werden oder es wird lokaler Speicher mit der -v
volumes Flag gemountet. Dafür können Sie einen der beiden verfügbaren Mount-Typen nutzen:
- Bind Mounts
- Named Volumes
Bind Mounts
Bind Mounts mounten einen Ordner oder eine Datei in den Container.
Bind Mounts, die Dateien und Ordner enthalten, die einer subuid oder subgid gehören und mit dem normalen Benutzer nicht mehr gelöscht werden können, können mit dem folgenden Befehl gelöscht werden:
podman unshare rm -r ${bind_mount_dir}
Named Volumes
Named Volumes werden von Podman gemanaged und können über CLI angepasst werden. Die Volumes werden in einem eigenen Ordner in ~/.local/share/containers/storage/volumes/
abgelegt.
Backup
Alle Daten, auch die von Volumes, werden automatisch von uns gesichert. Ein separates Backup kann aus folgenden Gründen dennoch nötig sein:
- Es können keine spezifischen Files oder Ordner aus Volumes wiederhergestellt werden.
- Die Daten sind nicht persistent gespeichert (z.B. bei einer Datenbank).
Darum empfehlen wir ihnen separate Dumps / Exports der Daten anzulegen. Diese werden dann von unserem Backup gesichert.