Systemd-Service-Dateien meistern: Ein umfassender Leitfaden
Erstellen Sie zuverlässige systemd-Dienstdateien mit korrekten Unit-Abschnitten, Neustartverhalten, Protokollen, Sicherheit und Timern.
Systemd-Dienstdateien meistern: Ein umfassender Leitfaden
Systemd-Dienstdateien teilen Linux mit, wie Ihre Anwendung gestartet, gestoppt, neu gestartet und überwacht werden soll. Wenn Ihr Dienst manuell startet, aber beim Booten fehlschlägt, zu aggressiv neu startet oder Protokolle an die falsche Stelle schreibt, müssen Sie normalerweise in der Unit-Datei nachsehen.
Dieser Leitfaden konzentriert sich auf die Erstellung und Konfiguration von systemd-Dienst-Unit-Dateien von Grund auf. Sie sehen die Kernabschnitte, ein funktionierendes Python-Dienstbeispiel, häufige Befehle zur Fehlerbehebung und einige Sicherheits- und Ressourcenkontrollen, die sich in der Produktion lohnen.
Systemd-Unit-Dateien verstehen
Systemd verwendet Unit-Dateien, um verschiedene Systemressourcen wie Dienste, Sockets, Geräte, Mountpoints und mehr zu beschreiben. Eine Dienst-Unit-Datei, die normalerweise mit der Erweiterung .service endet, definiert, wie systemd einen bestimmten Daemon oder eine bestimmte Anwendung verwalten soll.
Diese Dateien sind in Abschnitte organisiert, wobei jeder Abschnitt Schlüssel-Wert-Paare enthält, die Konfigurationsdirektiven darstellen. Die primären Abschnitte, auf die wir uns konzentrieren werden, sind [Unit], [Service] und [Install].
Anatomie einer Systemd-Dienstdatei
Eine typische systemd-Dienstdatei hat die folgende Struktur:
[Unit]
Description=Eine kurze Beschreibung des Dienstes.
After=network.target
[Service]
Type=simple
ExecStart=/usr/local/bin/my_application --config /etc/my_app.conf
Restart=on-failure
User=myuser
Group=mygroup
[Install]
WantedBy=multi-user.target
Lassen Sie uns jeden Abschnitt und seine gebräuchlichen Direktiven aufschlüsseln:
Der [Unit]-Abschnitt
Dieser Abschnitt enthält Metadaten über die Unit und definiert ihre Beziehung zu anderen Units. Er wird für Abhängigkeiten und die Reihenfolge verwendet.
Description=: Ein menschenlesbarer Name für den Dienst. Dies wird in der Ausgabe vonsystemctl statusangezeigt.Documentation=: URLs oder Pfade zur Dokumentation für den Dienst.Requires=: Definiert starke Abhängigkeiten. Wenn eine hier aufgeführte Unit nicht startet, wird auch diese Unit nicht starten.Wants=: Definiert schwache Abhängigkeiten. Wenn eine hier aufgeführte Unit nicht startet, wird diese Unit dennoch versuchen zu starten.Before=: Stellt sicher, dass diese Unit vor den aufgeführten Units startet.After=: Steuert nur die Reihenfolge. Beispielsweise startetAfter=network.targetdiese Unit nach dem grundlegenden Netzwerk-Target, garantiert jedoch keine externe Konnektivität. Netzwerkabhängige Dienste benötigen möglicherweiseAfter=network-online.targetplus den Wait-Online-Dienst der Distribution.Conflicts=: Wenn eine hier aufgeführte Unit gestartet wird, wird diese Unit gestoppt und umgekehrt.
Der [Service]-Abschnitt
Dieser Abschnitt konfiguriert das Verhalten des Dienstes selbst. Hier definieren Sie, wie der Prozess gestartet, gestoppt und verwaltet wird.
Type=: Gibt den Prozessstarttyp an. Häufige Werte sind:simple(Standard): Der Hauptprozess ist der inExecStart=angegebene. Systemd geht davon aus, dass der Dienst sofort nach dem Forken desExecStart=-Prozesses gestartet ist.forking: DerExecStart=-Prozess forkt einen Kindprozess, und der Elternprozess beendet sich. Systemd betrachtet den Dienst als gestartet, wenn der Elternprozess beendet ist. Bei diesem Typ müssen Sie oftPIDFile=angeben.oneshot: Ähnlich wiesimple, aber es wird erwartet, dass der Prozess nach Abschluss seiner Arbeit beendet wird. Nützlich für Setup-Skripte.notify: Der Daemon sendet eine Benachrichtigungsnachricht an systemd, wenn er erfolgreich gestartet wurde. Dies ist der bevorzugte Typ für moderne Daemons, die dies unterstützen.dbus: Der Dienst erwirbt einen D-Bus-Namen.
ExecStart=: Der Befehl, der zum Starten des Dienstes ausgeführt wird. Verwenden Sie für die meisten Diensttypen einenExecStart=-Befehl. MehrereExecStart=-Zeilen sind fürType=oneshotgültig, wo sie nacheinander ausgeführt werden.ExecStop=: Der Befehl, der zum Stoppen des Dienstes ausgeführt wird.ExecReload=: Der Befehl, der zum Neuladen der Konfiguration des Dienstes ohne Neustart ausgeführt wird.Restart=: Definiert, wann der Dienst automatisch neu gestartet werden soll. Häufige Werte:no(Standard): Niemals neu starten.on-success: Nur neu starten, wenn der Dienst sauber beendet wird (Exit-Code 0).on-failure: Neu starten, wenn der Dienst mit einem Exit-Code ungleich Null beendet, durch ein Signal terminiert wird oder eine Zeitüberschreitung auftritt.on-abnormal: Neu starten, wenn er durch ein Signal terminiert wird oder eine Zeitüberschreitung auftritt.on-abort: Nur neu starten, wenn er unsauber durch ein Signal terminiert wird.always: Immer neu starten, unabhängig vom Exit-Status.
RestartSec=: Die Zeit, die vor dem Neustart des Dienstes gewartet werden soll (Standard ist 100ms).User=: Der Benutzer, unter dem der Dienst ausgeführt werden soll.Group=: Die Gruppe, unter der der Dienst ausgeführt werden soll.WorkingDirectory=: Das Verzeichnis, in das vor der Ausführung der Befehle gewechselt werden soll.Environment=: Setzt Umgebungsvariablen für den Dienst.EnvironmentFile=: Liest Umgebungsvariablen aus einer Datei.PIDFile=: Pfad zur PID-Datei (wird oft mitType=forkingverwendet).StandardOutput=/StandardError=: Steuert, wohin stdout und stderr gehen, z. B.journal,nulloderinherit. Auf gängigen systemd-basierten Distributionen landen Dienstausgaben normalerweise im Journal, es sei denn, die Manager-Standardwerte wurden geändert.
Der [Install]-Abschnitt
Dieser Abschnitt definiert, wie die Unit aktiviert oder deaktiviert werden soll, normalerweise durch Erstellen symbolischer Links.
WantedBy=: Gibt das Target an, das diesen Dienst "wollen" soll, wenn er aktiviert ist. Häufige Werte:multi-user.target: Für Dienste, die starten sollen, wenn das System einen Mehrbenutzer-Befehlszeilenstatus erreicht.graphical.target: Für Dienste, die starten sollen, wenn das System einen grafischen Anmeldestatus erreicht.
Erstellen Ihrer ersten Systemd-Dienstdatei
Lassen Sie uns eine einfache Dienstdatei für ein hypothetisches Python-Skript namens my_app.py erstellen, das sich unter /opt/my_app/my_app.py befindet.
1. Erstellen Sie die Dienstdatei:
Dienstdateien für benutzerdefinierte Anwendungen werden normalerweise in /etc/systemd/system/ abgelegt. Nennen wir unsere Datei my_app.service.
# Erstellen Sie das Verzeichnis, falls es nicht existiert
sudo mkdir -p /etc/systemd/system/
# Erstellen Sie die Dienstdatei mit einem Texteditor
sudo nano /etc/systemd/system/my_app.service
2. Fügen Sie den folgenden Inhalt zu my_app.service hinzu:
[Unit]
Description=Meine benutzerdefinierte Python-Anwendung
After=network.target
[Service]
Type=simple
User=appuser
Group=appgroup
WorkingDirectory=/opt/my_app/
ExecStart=/usr/bin/python3 /opt/my_app/my_app.py
Restart=on-failure
[Install]
WantedBy=multi-user.target
Erklärung des Beispiels:
Description: Identifiziert unsere Anwendung klar.After=network.target: Stellt sicher, dass das Netzwerk vor dem Start verfügbar ist.Type=simple: Geht davon aus, dassmy_app.pyder Hauptprozess ist und nicht forkt.User=appuser,Group=appgroup: Gibt den Benutzer und die Gruppe an, unter denen die Anwendung ausgeführt werden soll. Stellen Sie sicher, dass diese Benutzer und Gruppen auf Ihrem System existieren und über entsprechende Berechtigungen verfügen. Möglicherweise müssen Sie sie erstellen:sudo groupadd appgroup sudo useradd -r -g appgroup appuser sudo chown -R appuser:appgroup /opt/my_app/WorkingDirectory: Setzt den Kontext für das Skript.ExecStart: Der Befehl zum Ausführen des Python-Skripts. Stellen Sie sicher, dass/usr/bin/python3der korrekte Pfad zu Ihrem Python-Interpreter ist und dass das Skript ausführbar ist.Restart=on-failure: Wenn das Skript abstürzt, versucht systemd, es neu zu starten.WantedBy=multi-user.target: Dieser Dienst wird automatisch gestartet, wenn das System in eine Mehrbenutzerumgebung bootet.
3. Laden Sie die systemd-Manager-Konfiguration neu:
Nach dem Erstellen oder Ändern einer Dienstdatei müssen Sie systemd mitteilen, seine Konfiguration neu zu laden.
sudo systemctl daemon-reload
4. Aktivieren und Starten Sie den Dienst:
- Aktivieren: Dies bewirkt, dass der Dienst automatisch beim Booten startet.
sudo systemctl enable my_app.service - Starten: Dies startet den Dienst sofort.
sudo systemctl start my_app.service
5. Überprüfen Sie den Dienststatus:
Um zu überprüfen, ob Ihr Dienst läuft, und um mögliche Fehler zu sehen:
sudo systemctl status my_app.service
Bei Problemen zeigt der Befehl status oft Fehlermeldungen oder Protokolle von journald an.
6. Anzeigen von Protokollen:
Systemd integriert journald für die Protokollierung. Sie können Protokolle für Ihren Dienst anzeigen mit:
sudo journalctl -u my_app.service
Sie können Protokolle auch in Echtzeit verfolgen:
sudo journalctl -f -u my_app.service
Andere nützliche Befehle
- Dienst stoppen:
sudo systemctl stop my_app.service - Dienst neu starten:
sudo systemctl restart my_app.service - Konfiguration neu laden (falls von der App unterstützt):
sudo systemctl reload my_app.service - Autostart beim Booten deaktivieren:
sudo systemctl disable my_app.service
Erweiterte Konfiguration und bewährte Methoden
Sicherheitsaspekte
- Dienste als Nicht-Root-Benutzer ausführen: Geben Sie immer
User=undGroup=an, es sei denn, es ist absolut notwendig. Dies folgt dem Prinzip der geringsten Privilegien. - Dienste isolieren: Erwägen Sie Sandboxing-Funktionen wie
PrivateTmp=true,ProtectSystem=strict,ProtectHome=trueundNoNewPrivileges=true. Testen Sie sie mit Ihrer Anwendung, da sie legitime Dateischreibvorgänge blockieren können.PrivateTmp=true: Gibt dem Dienst eigene private/tmp- und/var/tmp-Verzeichnisse.ProtectSystem=strict: Macht den größten Teil des Dateisystems für den Dienst schreibgeschützt. Verwenden SieReadWritePaths=für Verzeichnisse, in die der Dienst schreiben muss.NoNewPrivileges=true: Verhindert, dass der Dienst neue Privilegien erlangt.
Umgang mit komplexen Startvorgängen
Type=forkingmitPIDFile=: Stellen Sie bei älteren Anwendungen, die forken, sicher, dassPIDFile=auf die richtige Datei verweist.Type=notify: Wenn Ihre Anwendung dies unterstützt, ist dies die robusteste Methode für systemd, um zu wissen, wann sie wirklich bereit ist.ExecStartPre=undExecStartPost=: Befehle, die vor und nachExecStart=ausgeführt werden sollen. Nützlich für Setup- oder Bereinigungsaufgaben.
Ressourcenkontrolle
Systemd ermöglicht es Ihnen, die Ressourcennutzung zu begrenzen:
CPUWeight=: Relatives CPU-Gewicht für den Dienst.MemoryMax=: Maximaler Speicher, den der Dienst verwenden kann.IOWeight=: Relatives E/A-Gewicht, wo vom Kernel und Cgroup-Setup unterstützt.
Beispiel:
[Service]
# ... andere Direktiven ...
MemoryMax=512M
CPUWeight=50
Timer vs. Cron
Systemd-Timer bieten eine moderne Alternative zu traditionellen Cron-Jobs. Sie sind flexibler und integrieren sich besser in systemds Protokollierung und Abhängigkeitsverwaltung.
- Cron: Geplante Aufgaben, die in
crontab-Dateien definiert sind. - Systemd-Timer (
.timer-Units): Diese Units planen.service-Units. Sie definieren eine.timer-Datei, die angibt, wann eine entsprechende.service-Datei ausgeführt werden soll.
Beispiel:
Um ein Skript täglich um 3:00 Uhr morgens auszuführen:
my_script.service: Der auszuführende Dienst.[Unit] Description=Mein tägliches Skript [Service] Type=oneshot ExecStart=/opt/my_scripts/run_daily.sh User=scriptusermy_script.timer: Der Timer, der den Dienst plant.[Unit] Description=Führe mein tägliches Skript einmal am Tag aus [Timer] # Ausführen um 03:00 Uhr jeden Tag OnCalendar=*-*-* 03:00:00 # Bald nach dem Booten ausführen, wenn der geplante Zeitpunkt verpasst wurde, während die Maschine ausgeschaltet war. Persistent=true [Install] WantedBy=timers.target
Um dies zu verwenden:
- Legen Sie beide Dateien in
/etc/systemd/system/ab. - Führen Sie
sudo systemctl daemon-reloadaus. - Aktivieren und starten Sie den Timer:
sudo systemctl enable my_script.timerundsudo systemctl start my_script.timer.
Timer bieten Vorteile wie Persistent=true (führt verpasste Jobs beim Booten aus), Kalenderereignisse (wie hourly, daily, weekly) und eine bessere Integration mit journalctl.
Fehlerbehebung bei häufigen Problemen
- Dienst startet nicht: Überprüfen Sie
systemctl status <dienstname>undjournalctl -u <dienstname>. Achten Sie auf Tippfehler, falsche Pfade, fehlende Abhängigkeiten oder Berechtigungsfehler. - Falscher
Type=: Wenn ein Dienst sofort fehlschlägt oder hängt, könnte derType=falsch sein. Versuchen Siesimpleoderforkingund stellen Sie sicher, dassPIDFilekorrekt ist, wenn Sieforkingverwenden. - Zugriff verweigert: Stellen Sie sicher, dass der angegebene
User=undGroup=Lese-/Schreibzugriff auf die erforderlichen Dateien und Verzeichnisse haben. - Umgebungsvariablen: Wenn Ihre Anwendung auf bestimmte Umgebungsvariablen angewiesen ist, stellen Sie sicher, dass diese korrekt mit
Environment=oderEnvironmentFile=gesetzt sind. - Abhängigkeiten: Überprüfen Sie, ob
After=,Wants=undRequires=dem entsprechen, was Sie beabsichtigen.After=ordnet den Start; es zieht keine andere Unit von selbst hinzu.
Bevor Sie eine neue Unit auf einem Produktionshost aktivieren, führen Sie Folgendes aus:
sudo systemd-analyze verify /etc/systemd/system/my_app.service
Dies fängt viele Syntax- und Direktivenfehler, bevor Sie sich beim Booten auf den Dienst verlassen.
Wichtigste Erkenntnis
Schreiben Sie die kleinste Dienstdatei, die Ihre App genau beschreibt, und fügen Sie dann bewusst Neustartrichtlinie, Protokollierung, Sicherheitseinschränkungen und Ressourcengrenzen hinzu. Führen Sie nach jeder Änderung systemctl daemon-reload aus, überprüfen Sie die Unit und kontrollieren Sie systemctl status plus journalctl -u, bevor Sie ihr in der Produktion vertrauen.