Moritz Marquardt IT-Projekte zwischen Embedded & Web

Alles über .service-Dateien für systemd

Inzwischen hat in vielen Linux-Distributionen systemd das alte sysvinit abgelöst, und damit ändert sich auch die Art und Weise wie man Dienste definiert, die im Hintergrund laufen sollen. Das ist nämlich meiner Meinung nach deutlich einfacher und flexibler geworden: es gibt nun statt den Skripten in /etc/init.d die neuen .service-Dateien, die in verschiedenen Orten vorkommen können:

  • /etc/systemd/system: Systemdienste, sozusagen das neue /etc/init.d
  • /etc/systemd/user: Nutzerdienste für alle Nutzer
  • ~/.config/systemd/user: Nutzerdienste für einen speziellen Nutzer

Zusätzlich existieren unter Umständen noch weitere Verzeichnisse ({/usr,}/lib/systemd/{system,user}), um die kümmern sich aber schon apt, yum oder andere Paketverwalter.

Was ebenfalls neu ist, sind Nutzerdienste. Mit systemctl --user start <service> kann jeder Benutzer einen solchen Dienst ausführen - beispielsweise eine Backup-Software für die persönlichen Dateien.

Wie sieht so eine .service-Datei aus?

Es gibt ziemlich viel was geht mit .service-Dateien, aber bleiben wir mal bei den Dingen, die man für einen normalen Dienst so braucht:

[Unit]
Description=Beispieldienst
After=syslog.target network.target
Wants=wasanderes.service

[Service]
Type=simple
ExecStart=/PFAD/ZUM/BEFEHL/befehl

Restart=on-failure
RemainAfterExit=false

User=moritz
Group=moritz

WorkingDirectory=/WO/AUCH/IMMER

EnvironmentFile=/PFAD/ZUM/BEFEHL/beispiel.env
Environment=HOME=/home/moritz
Environment=ENVIRONMENT=production

[Install]
WantedBy=multi-user.target

Also, was tun wir hier? In der [Unit]-Sektion stehen allgemeine Informationen, die systemd dabei helfen unser Dienst in die Startreihenfolge einzuordnen - Abhängigkeiten, Beschreibung und Dokumentation, und so weiter.

  • Mit Description geben wir unserem Dienst einen Namen, in diesem Fall "Beispieldienst". So wird das Programm später in den Logs genannt.
  • Systemd kann mehrere Dienste gleichzeitig starten. Mit After wird angegeben, dass beim Systemstart erst Logging und Netzwerk initialisiert werden sollen, bevor unser Programm gestartet wird.
  • Mit Wants können wir Abhängigkeiten hinzufügen. Wenn die hier angegebenen Dienste nicht laufen, werden sie gestartet bevor unser Programm gestartet wird.

In [Section] geben wir nun an, wie genau das Programm gestartet werden soll.

  • Der Type beschreibt, um was für eine Art Programm es sich handelt:
    • simple sind Programme, die gestartet werden, dann laufen und ihre Daten auf die Standardausgabe schreiben, und mit einem SIGTERM wieder beendet werden können. Systemd kümmert sich hierbei um alles.
    • forking sind Programme, die gestartet werden und sich dann in den Hintergrund verdrücken (forken). Es sind also Dienste gemeint, die sich selbst verwalten (also starten, stoppen und neu laden) können. Mit ExecStop, ExecReload und gegebenenfalls ExecRestart können die Befehle für die jeweiligen Optionen eingestellt werden.
    • oneshot sind z.B. Skripte, die komplett durchlaufen müssen, bevor systemd davon abhängige Dienste starten kann, ansonsten aber wie simple.
  • ExecStart beschreibt jetzt den Befehl zum starten, im Fall von simple oder oneshot also die eigentliche Programmdatei mit Argumenten. Wichtig ist, dass dieser Befehl mit absolutem Pfad angegeben wird. Weiß man diesen nicht, kann man ihn ganz einfach mit where <programmname> herausfinden.
  • Restart gibt an, wann der Dienst neugestartet werden soll wenn er sich selbst beendet. In der Dokumentation gibt es dazu eine schöne Tabelle, die die möglichen Werte beschreibt (no, always, on-failure, on-abnormal, on-abort und on-watchdog).
  • RemainAfterExit ist besonders sinnvoll für oneshot-Dienste und lässt nach Beenden des Programms den Dienst weiterlaufen (bzw. tut so, als würde er weiterlaufen), wodurch man hier auch mit ExecStop arbeiten kann. Normalerweise kann diese Einstellung aber weggelassen werden (oder wie hier auf `false).
  • User und Group bestimmen den ausführenden Benutzer bzw. die Gruppe. Da Programme am besten nie als root laufen sollten, ist es meistens sinnvoll, diese Optionen zu setzen.
  • WorkingDirectory macht genau was es sagt: es setzt das Arbeitsverzeichnis, funktioniert also wie cd /WO/AUCH/IMMER && befehl.
  • Environment kann Umbgebungsvariablen setzen. Gibt es viele davon, lohnt es sich eventuell, sie in eine EnvironmentFile auszulagern, von wo aus sie dann geladen werden.

Zu guter letzt gibt es noch [Install]. Hiermit können wir beeinflussen, wann der Dienst automatisch gestartet wird.

  • WantedBy tut genau das: es gibt ein Ziel an, das zum Start des Programms führt. Für systemweite Dienste ist dies meistens multi-user.target, Benutzerdienste brauchen hier ein default.target.

Viele weitere Optionen sind in der Dokumentation aufgelistet, wobei auch das Kapitel systemd.exec sehr hilfreich ist.

Wie aktiviere und steuere ich einen Dienst?

Die Dateiendung (.service) in den folgenden Befehlen ist komplett optional (außer es gibt andere Unit-Typen mit dem gleichen Namen), ein Dateipfad muss ebenfalls nicht angegeben werden, da systemd seine Verzeichnisse automatisch durchsucht. Praktisch, nicht?

Ist die .service-Datei erstellt, kann man sie mit folgendem Befehl aktivieren, und damit zu den unter [Install] angegebenen Bedingungen automatisch starten lassen:

$ systemctl enable beispieldienst.service
Oder für Nutzerdienste:
$ systemctl --user enable beispieldienst.service

Verändert man die Service-Datei, muss man das systemd wiefolgt mitteilen:

$ systemctl [--user] daemon-reload

Um den Dienst zu starten und zu stoppen, kann man folgende Befehle verwenden:

$ systemctl [--user] start|stop|restart|reload beispieldienst.service

Möchte man sehen, was ein Dienst gerade tut (Status und die letzten paar Zeilen aus dem Log), gibt es diesen Befehl:

$ systemctl [--user] status beispieldienst.service

Für ausführlichere Protokolle gibt es journalctl:

$ journalctl [--user] -exfu beispieldienst.service

Wichtig dabei ist übrigens, dass der Nutzer dafür in der Gruppe systemd-journal sein muss um Logs von Systemdiensten einzusehen.

Nutzerdienste auf Servern automatisch starten

Da Nutzerdienste mit WantedBy=default.target normalerweise erst gestartet werden, wenn der entsprechende Nutzer sich tatsächlich anmeldet, ist das auf Servern erst einmal nicht allzu sinnvoll. Man kann Nutzerservices eines Nutzers jedoch auch mit dem System starten, und zwar mit dem folgenden Befehl (siehe Dokumentation):

$ loginctl enable-linger moritz

Nächster Beitrag Vorheriger Beitrag