Systemd-Service-Dateien meistern: Ein umfassender Leitfaden

Lernen Sie, robuste Linux-Dienste mit systemd zu erstellen und zu verwalten. Dieser umfassende Leitfaden behandelt die Syntax der systemd-Service-Unit-Datei, wesentliche Direktiven für die Abschnitte `[Unit]`, `[Service]` und `[Install]` sowie praktische Beispiele. Entdecken Sie Best Practices für Sicherheit, Ressourcenkontrolle und einen detaillierten Vergleich von systemd-Timern und Cron-Jobs. Meistern Sie Fehlerbehebungstechniken, um sicherzustellen, dass Ihre Anwendungen zuverlässig laufen.

27 Aufrufe

Systemd-Service-Dateien meistern: Ein umfassender Leitfaden

Systemd hat sich zum De-facto-Standard für die Verwaltung von Diensten und Systemprozessen auf den meisten modernen Linux-Distributionen entwickelt. Das Verständnis dafür, wie man systemd-Service-Unit-Dateien erstellt und verwaltet, ist für jeden Systemadministrator oder Entwickler, der Anwendungen zuverlässig bereitstellen und warten möchte, von entscheidender Bedeutung. Dieser Leitfaden führt Sie durch die Grundlagen von systemd-Service-Dateien, von der grundlegenden Syntax bis hin zur erweiterten Konfiguration, sodass Sie Ihre Linux-Dienste effektiv verwalten können.

Dieser Artikel konzentriert sich auf die Erstellung und Konfiguration von systemd-Service-Unit-Dateien von Grund auf. Wir werden die grundlegende Syntax behandeln, gängige und wesentliche Direktiven untersuchen und Best Practices für eine robuste Dienstverwaltung diskutieren. Am Ende dieses Leitfadens sind Sie in der Lage, Ihre eigenen systemd-Service-Dateien zu schreiben und sicherzustellen, dass Ihre Anwendungen reibungslos und zuverlässig laufen.

Systemd-Unit-Dateien verstehen

Systemd verwendet Unit-Dateien, um verschiedene Systemressourcen wie Dienste, Sockets, Geräte, Mount-Points und mehr zu beschreiben. Eine Service-Unit-Datei, die typischerweise mit der Erweiterung .service endet, definiert, wie systemd einen bestimmten Daemon oder eine Anwendung verwalten soll.

Diese Dateien sind in Abschnitte gegliedert, wobei jeder Abschnitt Schlüssel-Wert-Paare enthält, die Konfigurationsdirektiven darstellen. Die wichtigsten Abschnitte, auf die wir uns konzentrieren werden, sind [Unit], [Service] und [Install].

Anatomie einer Systemd-Service-Datei

Eine typische systemd-Service-Datei 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 gängigen Direktiven aufschlüsseln:

Der Abschnitt [Unit]

Dieser Abschnitt enthält Metadaten über die Unit und definiert deren Beziehung zu anderen Units. Er wird für Abhängigkeiten und Reihenfolge verwendet.

  • Description=: Ein lesbarer Name für den Dienst. Dies sehen Sie in der Ausgabe von systemctl status.
  • Documentation=: URLs oder Pfade zur Dokumentation für den Dienst.
  • Requires=: Definiert starke Abhängigkeiten. Wenn eine hier aufgeführte Unit beim Start fehlschlägt, schlägt auch diese Unit fehl.
  • Wants=: Definiert schwache Abhängigkeiten. Wenn eine hier aufgeführte Unit beim Start fehlschlägt, wird diese Unit trotzdem versuchen zu starten.
  • Before=: Stellt sicher, dass diese Unit vor den aufgeführten Units startet.
  • After=: Stellt sicher, dass diese Unit nach den aufgeführten Units startet. Dies ist sehr üblich, z. B. stellt After=network.target sicher, dass das Netzwerk hochgefahren ist, bevor Ihr Dienst startet.
  • Conflicts=: Wenn eine hier aufgeführte Unit gestartet wird, wird dieser Dienst gestoppt und umgekehrt.

Der Abschnitt [Service]

Dieser Abschnitt konfiguriert das Verhalten des Dienstes selbst. Hier definieren Sie, wie der Prozess gestartet, gestoppt und verwaltet wird.

  • Type=: Gibt den Starttyp des Prozesses an. Gängige Werte sind:

    • simple (Standard): Der Hauptprozess ist der in ExecStart= angegebene. Systemd geht davon aus, dass der Dienst unmittelbar nach dem Forken des ExecStart=-Prozesses gestartet ist.
    • forking: Der ExecStart=-Prozess forkt einen Kindprozess, und der Elternprozess wird beendet. Systemd betrachtet den Dienst als gestartet, wenn der Elternprozess beendet wird. Bei diesem Typ müssen Sie oft PIDFile= angeben.
    • oneshot: Ähnlich wie simple, aber der Prozess soll beendet werden, nachdem seine Arbeit erledigt ist. 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 ihn unterstützen.
    • dbus: Der Dienst erwirbt einen D-Bus-Namen.
  • ExecStart=: Der Befehl, der zum Starten des Dienstes ausgeführt wird. Dies ist die wichtigste Direktive. Sie können mehrere ExecStart=-Zeilen haben, die nacheinander ausgeführt werden.

  • ExecStop=: Der Befehl, der zum Stoppen des Dienstes ausgeführt wird.
  • ExecReload=: Der Befehl, der zur Neuladung der Konfiguration des Dienstes ohne Neustart ausgeführt wird.
  • Restart=: Definiert, wann der Dienst automatisch neu gestartet werden soll. Gängige 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 wird, durch ein Signal beendet wird oder ein Timeout auftritt.
    • on-abnormal: Neu starten, wenn durch ein Signal beendet oder ein Timeout auftritt.
    • on-abort: Nur neu starten, wenn er durch ein Signal unsauber beendet wird.
    • always: Immer neu starten, unabhängig vom Exit-Status.
  • RestartSec=: Die Wartezeit in Sekunden vor dem Neustart des Dienstes (Standard ist 100 ms).

  • 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 gewechselt werden soll, bevor die Befehle ausgeführt werden.
  • Environment=: Setzt Umgebungsvariablen für den Dienst.
  • EnvironmentFile=: Liest Umgebungsvariablen aus einer Datei.
  • PIDFile=: Pfad zur PID-Datei (wird oft bei Type=forking verwendet).
  • StandardOutput= / StandardError=: Steuert, wohin stdout/stderr geleitet werden (z. B. journal, syslog, null, inherit). journal ist der Standard und wird für die Protokollierung dringend empfohlen.

Der Abschnitt [Install]

Dieser Abschnitt definiert, wie die Unit aktiviert oder deaktiviert werden soll, typischerweise durch das Erstellen symbolischer Links.

  • WantedBy=: Gibt das Ziel an, das diesen Dienst „wünschen“ soll, wenn er aktiviert ist. Gängige Werte:
    • multi-user.target: Für Dienste, die starten sollen, wenn das System einen Multi-User-Kommandozeilenstatus erreicht.
    • graphical.target: Für Dienste, die starten sollen, wenn das System einen grafischen Anmeldestatus erreicht.

Erstellen Ihrer ersten Systemd-Service-Datei

Erstellen wir eine einfache Service-Datei für ein hypothetisches Python-Skript namens my_app.py unter /opt/my_app/my_app.py.

1. Die Service-Datei erstellen:

Service-Dateien für benutzerdefinierte Anwendungen werden typischerweise in /etc/systemd/system/ abgelegt. Nennen wir unsere Datei my_app.service.

# Das Verzeichnis erstellen, falls es nicht existiert
sudo mkdir -p /etc/systemd/system/

# Die Service-Datei mit einem Texteditor erstellen
sudo nano /etc/systemd/system/my_app.service

2. Fügen Sie den folgenden Inhalt in my_app.service ein:

[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

Erläuterung des Beispiels:

  • Description: Identifiziert unsere Anwendung eindeutig.
  • After=network.target: Stellt sicher, dass das Netzwerk verfügbar ist, bevor der Start erfolgt.
  • Type=simple: Geht davon aus, dass my_app.py der Hauptprozess ist und nicht forkt.
  • User=appuser, Group=appgroup: Geben 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 vorhanden sind und über die entsprechenden Berechtigungen verfügen. Möglicherweise müssen Sie sie erstellen:
    bash sudo groupadd appgroup sudo useradd -r -g appgroup appuser sudo chown -R appuser:appgroup /opt/my_app/
  • WorkingDirectory: Legt den Kontext für das Skript fest.
  • ExecStart: Der Befehl zum Ausführen des Python-Skripts. Stellen Sie sicher, dass /usr/bin/python3 der korrekte Pfad zu Ihrem Python-Interpreter ist und dass das Skript ausführbar ist.
  • Restart=on-failure: Wenn das Skript abstürzt, wird systemd versuchen, es neu zu starten.
  • WantedBy=multi-user.target: Dieser Dienst wird beim Systemstart in einer Multi-User-Umgebung automatisch gestartet.

3. Die Konfiguration des Systemd-Managers neu laden:

Nach dem Erstellen oder Ändern einer Service-Datei müssen Sie systemd anweisen, seine Konfiguration neu zu laden.

sudo systemctl daemon-reload

4. Den Dienst aktivieren und starten:

  • Aktivieren: Dadurch wird der Dienst automatisch beim Booten gestartet.
    bash sudo systemctl enable my_app.service
  • Starten: Dadurch wird der Dienst sofort gestartet.
    bash sudo systemctl start my_app.service

5. Den Dienststatus überprüfen:

Um zu überprüfen, ob Ihr Dienst läuft, und um mögliche Fehler anzuzeigen:

sudo systemctl status my_app.service

Bei Problemen zeigt der Befehl status oft Fehlermeldungen oder Protokolle von journald an.

6. Protokolle anzeigen:

Systemd integriert sich zur Protokollierung in journald. Sie können die 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

Weitere 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
  • Automatisches Starten beim Booten deaktivieren: sudo systemctl disable my_app.service

Erweiterte Konfiguration und Best Practices

Sicherheitsaspekte:

  • Dienste als Nicht-Root-Benutzer ausführen: Geben Sie immer User= und Group= an, es sei denn, dies ist absolut notwendig. Dies folgt dem Prinzip der geringsten Privilegien.
  • Dienste isolieren: Erwägen Sie die Verwendung von Sandboxing-Funktionen wie PrivateTmp=true, ProtectSystem=true, NoNewPrivileges=true für erhöhte Sicherheit.
    • PrivateTmp=true: Gibt dem Dienst sein eigenes privates /tmp- und /var/tmp-Verzeichnis.
    • ProtectSystem=true: Macht /usr, /boot, /etc schreibgeschützt.
    • NoNewPrivileges=true: Verhindert, dass der Dienst neue Privilegien erhält.

Umgang mit komplexen Starts:

  • Type=forking mit PIDFile=: Bei älteren Anwendungen, die forken, stellen Sie sicher, dass PIDFile= auf die korrekte Datei zeigt.
  • Type=notify: Wenn Ihre Anwendung dies unterstützt, ist dies der robusteste Weg für systemd zu wissen, wann sie wirklich bereit ist.
  • ExecStartPre= und ExecStartPost=: Befehle, die vor und nach ExecStart= ausgeführt werden. Nützlich für Setup- oder Aufräumarbeiten.

Ressourcenkontrolle:

Systemd ermöglicht es Ihnen, die Ressourcennutzung zu begrenzen:

  • CPUShares=: Relative Zuteilung von CPU-Zeit.
  • MemoryLimit=: Maximaler Speicher, den der Dienst nutzen darf.
  • IOWeight=: Relative E/A-Bandbreite.

Beispiel:

[Service]
# ... andere Direktiven ...
MemoryLimit=512M
CPUShares=512 # Etwa 50% der CPU-Zeit im Vergleich zum Standardwert 1024

Timer vs. Cron

Systemd-Timer bieten eine moderne Alternative zu traditionellen Cron-Jobs. Sie sind flexibler und integrieren sich besser in die Protokollierung und Abhängigkeitsverwaltung von systemd.

  • 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 Uhr morgens auszuführen:

  1. my_script.service: Der auszuführende Dienst.
    ```ini
    [Unit]
    Description=Mein tägliches Skript

    [Service]
    Type=oneshot
    ExecStart=/opt/my_scripts/run_daily.sh
    User=scriptuser
    ```

  2. my_script.timer: Der Timer, der den Dienst plant.
    ```ini
    [Unit]
    Description=Mein tägliches Skript einmal täglich ausführen

    [Timer]

    Täglich um 03:00 Uhr ausführen

    OnCalendar=--* 03:00:00
    Persistent=true # Bei Ausfall sofort ausführen

    [Install]
    WantedBy=timers.target
    ```

Zur Verwendung:

  • Beide Dateien in /etc/systemd/system/ ablegen.
  • sudo systemctl daemon-reload ausführen.
  • Den Timer aktivieren und starten: sudo systemctl enable my_script.timer und sudo systemctl start my_script.timer.

Timer bieten Vorteile wie Persistent=true (führt versäumte Jobs beim Booten aus), Kalenderereignisse (wie hourly, daily, weekly) und eine bessere Integration mit journalctl.

Häufige Probleme beheben

  • Dienst startet nicht: Überprüfen Sie systemctl status <service_name> und journalctl -u <service_name>. Suchen Sie nach Tippfehlern, falschen Pfaden, fehlenden Abhängigkeiten oder Berechtigungsfehlern.
  • Falscher Type=: Wenn ein Dienst sofort fehlschlägt oder hängen bleibt, ist der Type= möglicherweise falsch. Versuchen Sie simple oder forking und stellen Sie sicher, dass PIDFile korrekt ist, wenn Sie forking verwenden.
  • Zugriff verweigert: Stellen Sie sicher, dass der angegebene User= und Group= Lese-/Schreibzugriff auf die erforderlichen Dateien und Verzeichnisse haben.
  • Umgebungsvariablen: Wenn Ihre Anwendung auf bestimmte Umgebungsvariablen angewiesen ist, stellen Sie sicher, dass diese mithilfe von Environment= oder EnvironmentFile= korrekt festgelegt sind.
  • Abhängigkeiten: Überprüfen Sie, ob die Direktiven After= und Requires= korrekt eingestellt sind, um sicherzustellen, dass die Voraussetzungen erfüllt sind, bevor Ihr Dienst startet.

Fazit

Systemd-Service-Dateien sind ein mächtiges Werkzeug zur Verwaltung von Anwendungen unter Linux. Durch das Verständnis der Struktur von Unit-Dateien, des Zwecks wichtiger Direktiven und der Best Practices für die Konfiguration können Sie die Zuverlässigkeit, Sicherheit und Wartbarkeit Ihrer Dienste erheblich verbessern. Ob Sie ein einfaches Skript oder eine komplexe Anwendung bereitstellen, das Beherrschen von systemd-Service-Dateien ist eine wesentliche Fähigkeit für die moderne Linux-Systemadministration.

Denken Sie daran, Ihre Service-Dateien immer gründlich zu testen, systemctl status und journalctl zum Debuggen zu verwenden und die von systemd bereitgestellten Sicherheitsfunktionen zu nutzen.