Schlagwort-Archive: systemd.service

Podman kann auch Systemd-Service-Units generieren

Mit „Kanboard im Container…“ hat mein Ausflug ins Containerland begonnen. Mittlerweile läuft mein Pod bereits eine Weile und ich nutze die Anwendung regelmäßig. Jedoch musste ich nach jedem Neustart des Hosts den Pod kanboardpod manuell starten. Ich hatte mir daher vorgenommen, hierfür eine Systemd-Service-Unit zu erstellen, welche diesen Task automatisiert. Doch habe ich mit Freude festgestellt, dass podman-generate-systemd(1) mir diese Arbeit abnehmen kann.

Also starte ich einen ersten Versuch und erzeuge entsprechende Service-Unit-Dateien in meinem HOME-Verzeichnis. Die Option „--name“ sorgt dafür, dass in den Service-Unit-Dateien die Namen des Pods bzw. der Container verwendet werden, anstatt der UUIDs. Die Option „--files“ sorgt dafür, dass die Ausgabe in Dateien und nicht auf die Standard-Ausgabe geschrieben wird.

$ podman generate systemd --name --files kanboardpod
/home/alice/pod-kanboardpod.service
/home/alice/container-kanboard.service
/home/alice/container-pgsql_db.service

$ cat pod-kanboardpod.service
# pod-kanboardpod.service
# autogenerated by Podman 2.0.5
# Mon Jan 25 13:19:51 CET 2021

[Unit]
Description=Podman pod-kanboardpod.service
Documentation=man:podman-generate-systemd(1)
Wants=network.target
After=network-online.target
Requires=container-kanboard.service container-pgsql_db.service
Before=container-kanboard.service container-pgsql_db.service

[Service]
Environment=PODMAN_SYSTEMD_UNIT=%n
Restart=on-failure
ExecStart=/usr/bin/podman start 62cdd29105a4-infra
ExecStop=/usr/bin/podman stop -t 10 62cdd29105a4-infra
ExecStopPost=/usr/bin/podman stop -t 10 62cdd29105a4-infra
PIDFile=/run/user/1000/containers/overlay-containers/b3c9bd9754cdc999108d0f4aad7d808c007cc34eee34faefd41ee39c3e1ca18b/userdata/conmon.pid       
KillMode=none
Type=forking

[Install]
WantedBy=multi-user.target default.target

$ cat container-kanboard.service 
# container-kanboard.service
# autogenerated by Podman 2.0.5
# Mon Jan 25 13:19:51 CET 2021

[Unit]
Description=Podman container-kanboard.service
Documentation=man:podman-generate-systemd(1)
Wants=network.target
After=network-online.target
BindsTo=pod-kanboardpod.service
After=pod-kanboardpod.service

[Service]
Environment=PODMAN_SYSTEMD_UNIT=%n
Restart=on-failure
ExecStart=/usr/bin/podman start kanboard
ExecStop=/usr/bin/podman stop -t 10 kanboard
ExecStopPost=/usr/bin/podman stop -t 10 kanboard
PIDFile=/run/user/1000/containers/overlay-containers/99d386a42b186efb3347d909cea265b990469dc33e1889a3006425a71956699b/userdata/conmon.pid
KillMode=none
Type=forking

[Install]
WantedBy=multi-user.target default.target

$ cat container-pgsql_db.service 
# container-pgsql_db.service
# autogenerated by Podman 2.0.5
# Mon Jan 25 13:19:51 CET 2021

[Unit]
Description=Podman container-pgsql_db.service
Documentation=man:podman-generate-systemd(1)
Wants=network.target
After=network-online.target
BindsTo=pod-kanboardpod.service
After=pod-kanboardpod.service

[Service]
Environment=PODMAN_SYSTEMD_UNIT=%n
Restart=on-failure
ExecStart=/usr/bin/podman start pgsql_db
ExecStop=/usr/bin/podman stop -t 10 pgsql_db
ExecStopPost=/usr/bin/podman stop -t 10 pgsql_db
PIDFile=/run/user/1000/containers/overlay-containers/fe757283f0662220fee23a563053ea7f30dbdf6d9fbb492c010a01dd7598fc9b/userdata/conmon.pid
KillMode=none
Type=forking

[Install]
WantedBy=multi-user.target default.target

Um die generierten Service-Units zu installieren und zukünftig als derjenige User auszuführen, welcher den Pod und die Container erzeugt hat, verschiebe ich sie in das zu erstellende Verzeichnis ~/.config/systemd/user. Anschließend werden die neuen Units in die Systemd-Konfiguration eingelesen und aktiviert.

$ mkdir -p .config/systemd/user
$ mv *.service .config/systemd/user/
$ systemctl --user enable pod-kanboardpod.service
$ podman pod stop kanboardpod
$ systemctl --user start pod-kanboardpod.service

Nachdem ich die Service-Units an die richtige Stelle verschoben und aktiviert hatte, habe ich meine laufende Pod-Instanz gestoppt und über die entsprechende Service-Unit gestartet.

Ich wähnte mich nun bereits am Ziel. Doch nach einem Neustart des Hosts war die Anwendung wieder nicht verfügbar. Die Service-Unit war nicht gestartet worden. Podman kann hier nichts dafür, denn es ist systemd, welcher dafür sorgt, dass im User-Kontext laufende Services beendet werden, wenn sich der entsprechende User ausloggt und diese erst startet, wenn der User sich einloggt.

Valentin Rothberg von Red Hat gab mir den entscheidenden Tipp, um dieses Problem zu lösen. Die Lösung versteckt sich in der Manpage zu podman-generate-systemd(1):

The systemd user instance is killed after the last session for the user is closed. The systemd user instance can be kept running ever after the user logs out by enabling lingering using

$ loginctl enable-linger <username>

Manual page podman-generate-systemd(1)

@Valentin: Thanks a lot, that solved my issue!

Fazit

Ich bin ehrlich gesagt hoch erfreut, dass die Entwickler hier eine Möglichkeit vorgesehen haben, um aus bestehenden Pods bzw. Container-Instanzen Systemd-Service-Units generieren zu können. Dies ermöglicht es, Container-Instanzen und Pods mit den gewohnten Werkzeugen zu starten, zu stoppen und deren Status zu kontrollieren.

So besteht die Möglichkeit, die rootless-Podman-Container auch als unprivilegierte Dienste laufen zu lassen. Das gefällt mir.

tdps.service — Service-Unit für den TeamDrive Personal Server

Kaum eine Software verwende ich so lange wie TeamDrive. Die ersten Artikel dazu in diesem Blog stammen bereits aus dem Jahr 2011 [0], [1].

Nachdem ich den TeamDrive Personal Server schon auf einem NAS installiert und als Dienst konfiguriert habe, dokumentiere ich in diesem Artikel die Erstellung einer Systemd-Service-Unit. Dazu sei gesagt, dass ich kein Experte für Systemd-Units bin. Tatsächlich kenne ich mich nichtmal besonders gut damit aus. Folgende Lösung funktioniert auf meinem Server. Anregungen zur Verbesserung der Service-Unit nehme ich gern in den Kommentaren entgegen.

Installation des TeamDrive Personal Server

Zuerst muss der TeamDrive Personal Server heruntergeladen und installiert werden. Dabei hilft das dazugehörige Handbuch. Auch wenn dieses schon einige Jahre auf dem Buckel hat, sind die enthaltenen Informationen weiterhin aktuell.

Da die Installation hier im Blog bereits einmal beschrieben wurde, gehe ich hier im Detail nicht weiter darauf ein. Im vorliegenden Fall wurde die Software im Verzeichnis /opt/tdps installiert. Darüber hinaus wurde ein Benutzer tdps erstellt, welchem das Verzeichnis /opt/tdps und die darin enthaltenen Dateien gehören.

Systemd-Service-Unit tdps.service

Ich habe den TeamDrive Personal Server (TDPS) auf einem Debian 10 System installiert. Um diesen wie alle übrigen Dienste mit dem Programm systemctl verwalten zu können, habe ich die Datei /etc/systemd/system/tdps.service mit folgendem Inhalt erstellt:

[Unit]
Description="TeamDrive Personal Server"
After=network.target

[Service]
User=tdps
PIDFile=/opt/tdps/tdpsd.pid
ExecStart=/opt/tdps/tdpsd -c /opt/tdps/tdps.config -m /opt/tdps/mime.types -w /opt/tdps
ExecStop=/opt/tdps/stop-tdps -p /opt/tdps/tdpsd.pid
KillMode=process
PrivateTmp=yes
ProtectHome=yes
ProtectSystem=full

[Install]
WantedBy=multi-user.target

Im Abschnitt [Unit] findet sich eine kurze Beschreibung der Service-Unit. Die Option After=network.target gibt an, dass diese Unit erst gestartet werden soll, wenn das Netzwerk zur Verfügung steht. Dies ist Voraussetzung, um den Dienst über das Netzwerk nutzen zu können.

Die Zeile im letzten Abschnitt [Install] definiert, dass diese Unit Bestandteil des multi-user.target ist und mit diesem geladen wird.

Der Abschnitt [Service] definiert die für den Dienst relevanten Parameter. Die gewählten Optionen werde ich im Folgenden erläutern.

User= gibt den Namen des Benutzers an, mit dessen Rechten der Dienst ausgeführt werden soll. In diesem Fall wird der extra für diesen Zweck erstellte User tdps verwendet.

PIDFile= gibt den Pfad an, wo der Dienst seine PID speichert. Der Service-Manager liest die PID des Hauptprozess aus dieser Datei, nachdem der Service gestartet wurde.

ExecStart= gibt das Kommando mit allen notwendigen Argumenten an, um den Dienst zu starten. Ich habe hier ein wenig mit verschiedenen Aufrufen experimentiert. Dabei habe ich verschiedene Möglichkeiten gefunden, den Dienst erfolgreich zu starten. Worin die Unterschiede im Aufruf genau bestehen, kann ich leider nicht sagen. Am Ende habe ich mich für obige Kommandozeile entschieden, die ich im TeamDrive-Forum gefunden habe. Aus diesem Foren-Beitrag habe ich auch Stumpf die Option KillMode=process übernommen. Zur Option selbst kann ich (noch) nicht viel sagen. Dies änderst sich vielleicht noch, da ich mich noch etwas damit beschäftigen möchte.

Mit ExecStop= gibt man entsprechend an, wie der Dienst zu stoppen ist. Auch hier stammt die Kommandozeile aus oben verlinktem Foren-Thread.

Die nächsten drei Optionen PrivateTmp=, ProtectHome= und ProtectSystem= dienen dazu den Dienst zu härten. Durch PrivateTmp=yes erhält der gestartete Prozess einen eigenen Dateisystem-Namespace für /tmp und /var/tmp. So kann der Prozess nicht auf die Dateien anderer Prozesse und Benutzer in den normalen Verzeichnissen /tmp und /var/tmp zugreifen. Mit ProtectHome=yeswird der Zugriff des Dienstes auf die Verzeichnisse /home, /root und /run/user verhindert. ProtectSystem=full sorgt dafür, dass die Verzeichnisse /boot, /etc und /usr im Nur-Lese-Modus für den Prozess zur Verfügung stehen. Damit ist sichergestellt, dass der von dieser Unit gestartete Prozess keine Dateien innerhalb dieser Verzeichnisse verändern kann.

Die drei im vorhergehenden Abschnitt genannten Optionen stellen einen zusätzlichen Schutz dar. Zwar darf der Benutzer tdps schon aufgrund der Datei- und Verzeichnisberechtigungen nicht in den genannten Verzeichnissen schreiben und Dateien verändern bzw. diese überhaupt lesen, doch bieten diese Optionen einen zusätzlichen Schutz und greifen auch noch, wenn jemand Schindluder mit den Berechtigungen getrieben hat. Daher halte ich es für sinnvoll diese Optionen wenn möglich in allen Service-Units zu nutzen.

Neben den drei hier vorgestellten Optionen gibt es noch einige weitere, welche im englischsprachigen Artikel „Mastering systemd: Securing and sandboxing applications and services“ nachgelesen werden können. Ich finde diese so sinnvoll, dass ich mich ehrlich gesagt frage, warum die genannten Einstellungen nicht der Standard sind und man diese editieren muss, wenn man entsprechende Zugriffe explizit erlauben möchte.

Damit ist alles getan und dokumentiert. Der TDPS lässt sich mit oben beschriebener Service-Unit starten und stoppen. Den Status abfragen kann man selbstverständlich auch.

Es bleibt ein kleiner Schönheitsfehler. Wird der Dienst mittels sudo systemctl stop tdps.service gestoppt, endet die Unit im Status „failed“. Warum das so ist, habe ich noch nicht herausgefunden. Ich vermute, es hängt damit zusammen, dass der Dienst mittels SIGTERM beendet wird. Bin mir an dieser Stelle jedoch nicht sicher. Falls von euch jemand eine Idee dazu hat, freue ich mich über euren Kommentar.

Wie schreibt ihr eure Service-Units? Welche Optionen sollten eurer Meinung nach in keiner Unit fehlen? Wenn ihr mögt, lasst uns gerne in den Kommentaren darüber diskutieren.

Ältere Artikel zu TeamDrive in diesem Blog