systemd-Service erstellen: Unit, Sicherheit, Logs und Neustart prüfen
EdwardMoon
Um ein manuell installiertes Programm beim Systemstart automatisch auszuführen, es bei Fehlern neu zu starten und den ausführenden Benutzer sowie die Protokollierung einheitlich zu verwalten, registriert man einen systemd-Service. Dieser Artikel bezieht sich auf Systemdienste unter RHEL und Rocky Linux 8/9. Die Anwendung muss im Vordergrund aktiv bleiben und darf nicht in den Hintergrund abzweigen (forken).

1. Ausführbare Datei und Konfiguration vorbereiten
Ersetzen Sie /usr/local/bin/myapp und /etc/myapp/config.yml im Beispiel durch die Pfade Ihrer tatsächlichen Anwendung. Die folgenden Schritte installieren das Programm nicht selbst und erstellen auch keine Konfigurationsdateien. Prüfen Sie vorab den korrekten Startbefehl, das Arbeitsverzeichnis, benötigte Dateien und Ports sowie die Methode zur sauberen Beendigung des Programms. Wenn Sie Änderungen an einem bestehenden Dienst vornehmen, sichern Sie die zu ändernde Unit und die Konfigurationsdateien in einem separaten Pfad.
command -v systemctl
systemd --version
sudo test -x /usr/local/bin/myapp
sudo test -f /etc/myapp/config.yml
Sollte die Prüfung fehlschlagen, fahren Sie nicht mit dem nächsten Schritt fort. Falls bereits ein vom Paket bereitgestellter Dienst existiert, prüfen Sie diesen vor dem Erstellen einer neuen Unit mit systemctl cat dienstname. Die Verwendung von Drop-in-Dateien für die bereitgestellte Unit ist für die Wartung meist vorteilhafter.
2. Dediziertes Benutzerkonto und beschreibbares Verzeichnis erstellen
getent passwd myapp
getent group myapp
# Nur einmal ausführen, wenn das Benutzerkonto nicht existiert.
sudo useradd --system --user-group --home-dir /var/lib/myapp --shell /sbin/nologin myapp
sudo install -d -o myapp -g myapp -m 0750 /var/lib/myapp
sudo chown root:myapp /etc/myapp/config.yml
sudo chmod 0640 /etc/myapp/config.yml
sudo -u myapp test -x /usr/local/bin/myapp
sudo -u myapp test -r /etc/myapp/config.yml
Falls das Benutzerkonto myapp bereits existiert, prüfen Sie dessen Verwendungszweck sowie die primäre Gruppe und überspringen Sie den Befehl zur Benutzererstellung. Da auch für die übergeordneten Verzeichnisse Zugriffsberechtigungen erforderlich sind, prüfen Sie den gesamten Pfad mit namei -l /etc/myapp/config.yml. Verhindern Sie, dass das Dienstkonto die ausführbare Datei oder die Konfiguration ändern kann. Weisen Sie dem Konto nur das Datenverzeichnis zu, in das die Anwendung schreibt. In Umgebungen mit aktivem SELinux sollten Sie zudem die AVC-Protokolle und Dateikontexte prüfen.
3. Service-Unit erstellen
Speichern Sie den folgenden Inhalt mittels sudoedit /etc/systemd/system/myapp.service.
[Unit]
Description=My application
After=network-online.target
Wants=network-online.target
StartLimitIntervalSec=60
StartLimitBurst=5
[Service]
Type=simple
User=myapp
Group=myapp
WorkingDirectory=/var/lib/myapp
ExecStart=/usr/local/bin/myapp --config /etc/myapp/config.yml
Restart=on-failure
RestartSec=5s
TimeoutStopSec=30s
NoNewPrivileges=yes
PrivateTmp=yes
StandardOutput=journal
StandardError=journal
[Install]
WantedBy=multi-user.target
Type=simpleverfolgt den gestarteten Prozess als Hauptprozess des Dienstes. Da dies nicht garantiert, dass die Anwendung vollständig bereit ist, ist eine separate Funktionsprüfung erforderlich.ExecStartist keine Shell-Befehlszeile. Verwenden Sie dort keine Zeichen wie&, Pipes oder Umleitungen direkt.Afterdefiniert die Reihenfolge, währendWantsdie Abhängigkeiten zu anderen Units festlegt, die mitgestartet werden sollen. Der Status „Netzwerk online“ garantiert nicht, dass entfernte Datenbanken oder APIs bereits fehlerfrei antworten.- Verwenden Sie
Restart=on-failurein Kombination mit Startbegrenzungen, um schnelle, wiederholte Fehlschläge zu stoppen. Ob der Dienst auch nach einer regulären Beendigung neu gestartet werden soll, hängt von der Art des Programms ab. PrivateTmperstellt einen dedizierten temporären Bereich. Prüfen Sie die Kompatibilität bei Programmen, die /tmp-Dateien mit anderen Diensten teilen müssen.
Hinterlegen Sie Passwörter und Token nicht direkt in der Unit unter Environment=. Nutzen Sie stattdessen von der Anwendung unterstützte zugriffsgeschützte Konfigurationsdateien oder die von der jeweiligen systemd-Version bereitgestellte Funktion zur Übergabe von Zugangsdaten (Credentials).
4. Syntax prüfen, Dienst starten und Funktion testen
sudo systemd-analyze verify /etc/systemd/system/myapp.service
# Fortfahren, wenn die obige Prüfung fehlerfrei war.
sudo systemctl daemon-reload
sudo systemctl enable --now myapp.service
systemctl is-enabled myapp.service
systemctl is-active myapp.service
sudo systemctl status myapp.service --no-pager --full
sudo journalctl -u myapp.service -b -n 100 --no-pager
daemon-reload liest die Units neu ein, enable konfiguriert den automatischen Start beim Systemstart und --now startet den Dienst sofort. Wenn Sie die Konfiguration eines bereits laufenden Dienstes geändert haben, ist ein vom Programm unterstützter Reload oder ein geplanter Neustart erforderlich.
Geben Sie sich nicht mit dem Status active zufrieden. Senden Sie bei Webdiensten eine Anfrage an die Status-URL (Health Check) der Anwendung. Bei einem Task-Runner prüfen Sie das Verarbeitungsergebnis eines Testjobs. Testen Sie während eines Wartungsfensters auch das Verhalten nach einem Systemneustart sowie die ordnungsgemäße Beendigung. Die im Beispiel definierten 30 Sekunden Wartezeit für das Beenden sollten basierend auf der Zeit angepasst werden, die für ein Beenden ohne Datenverlust benötigt wird.
5. Häufige Fehlerursachen identifizieren
| Symptom | Zu prüfende Punkte |
|---|---|
| 203/EXEC | Pfad zur ausführbaren Datei, Ausführungsrechte, Skript-Interpreter und SELinux-Verweigerungen |
| 200/CHDIR | Existenz von WorkingDirectory und Zugriffsberechtigungen des Dienstkontos auf das Verzeichnis |
| 217/USER | Existenz der Konten für User und Group |
| Beendigung direkt nach dem Start | Fehler bei Argumenten oder Konfiguration, Prüfung, ob das Programm in den Daemon-Modus forkt |
| start-limit-hit | Behebung der wiederholten Fehlerursache anhand der Logs, anschließend Zurücksetzen des Fehlerzählers und Neustart |
sudo journalctl -u myapp.service --since '-10 minutes' --no-pager
# Nach Behebung der Ursache ausführen.
sudo systemctl reset-failed myapp.service
sudo systemctl restart myapp.service
Änderungen rückgängig machen
Handelt es sich um einen neu erstellten Dienst, deaktivieren und stoppen Sie diesen mit sudo systemctl disable --now myapp.service. Bei Änderungen an einem bestehenden Dienst stellen Sie die gesicherte Unit und Konfiguration wieder her, führen ein daemon-reload aus und führen anschließend einen geplanten Neustart sowie eine Funktionsprüfung durch. Löschen Sie das Dienstkonto oder das Datenverzeichnis nicht, bevor Sie die Abhängigkeiten und die Notwendigkeit der Aufbewahrung geprüft haben.