Fehlerbehebung bei Systemd-Dienstausfällen: Eine Schritt-für-Schritt-Anleitung

Diagnostizieren Sie Systemd-Dienstausfälle mit Statusprüfungen, Journal-Logs, Unit-Datei-Überprüfung, Abhängigkeitskorrekturen und Umgebungs-Debugging.

Fehlerbehebung bei Systemd-Dienstausfällen: Eine Schritt-für-Schritt-Anleitung

Systemd-Dienstausfälle sind einfacher zu debuggen, wenn Sie langsamer vorgehen und den Beweisen folgen. Eine fehlgeschlagene Unit hinterlässt normalerweise drei nützliche Hinweise: den von systemd aufgezeichneten Status, den Befehl, den es auszuführen versuchte, und die Logs, die entweder von systemd oder der Anwendung geschrieben wurden. Wenn Sie diese in der richtigen Reihenfolge lesen, vermeiden Sie die häufige Falle, eine Unit-Datei zu bearbeiten, bevor Sie wissen, ob das Problem die Unit, die Anwendung, eine Abhängigkeit oder der Host ist.

Die folgenden Beispiele verwenden einen fiktiven mywebapp.service, aber der gleiche Arbeitsablauf gilt für Datenbank-Helfer, Queue-Consumer, Backup-Jobs, Exporteure und interne Dienste.

Die erste Verteidigungslinie: systemctl status

Wenn ein Dienst nicht startet, sollten Sie als allererstes systemctl status <Dienstname> ausführen. Dieser Befehl liefert eine Momentaufnahme des aktuellen Zustands des Dienstes, einschließlich ob er aktiv ist, geladen ist und, entscheidend, einen Ausschnitt seiner letzten Logs. Dies liefert oft genug Informationen, um das Problem schnell zu identifizieren.

Angenommen, Ihr Webanwendungsdienst mywebapp.service startet nicht:

systemctl status mywebapp.service

Beispiel für die Ausgabeinterpretation:

● mywebapp.service - Meine Webanwendung
     Geladen: geladen (/etc/systemd/system/mywebapp.service; aktiviert; vendor preset: deaktiviert)
     Aktiv: fehlgeschlagen (Ergebnis: exit-code) seit Mo 2023-10-26 10:30:05 UTC; vor 10s
    Prozess: 12345 ExecStart=/usr/local/bin/mywebapp-start.sh (code=exited, status=1/FAILURE)
   Haupt-PID: 12345 (code=exited, status=1/FAILURE)
        CPU: 10ms

Oct 26 10:30:05 hostname systemd[1]: Gestartet: Meine Webanwendung.
Oct 26 10:30:05 hostname mywebapp-start.sh[12345]: Fehler: Port 8080 bereits belegt
Oct 26 10:30:05 hostname systemd[1]: mywebapp.service: Hauptprozess beendet, code=exited, status=1/FAILURE
Oct 26 10:30:05 hostname systemd[1]: mywebapp.service: Fehlgeschlagen mit Ergebnis 'exit-code'.

Aus dieser Ausgabe können wir sofort sehen:

  • Der Dienst mywebapp.service ist fehlgeschlagen.
  • Er ist mit Ergebnis: exit-code fehlgeschlagen, was bedeutet, dass der ExecStart-Befehl mit einem Nicht-Null-Status beendet wurde.
  • Die Prozess-Zeile zeigt, dass der Befehl mywebapp-start.sh mit status=1/FAILURE fehlgeschlagen ist.
  • Entscheidend: Die Logzeilen zeigen: Fehler: Port 8080 bereits belegt. Dies ist ein klarer Hinweis auf das Problem.

Dieser Befehl ist Ihr erstes Diagnosewerkzeug, das oft direkt auf die Ursache hinweist oder eingrenzt, wo als nächstes gesucht werden muss.

Tief eintauchen mit journalctl

Während systemctl status eine schnelle Zusammenfassung liefert, ist journalctl Ihr bevorzugter Befehl für detaillierte Logs. Es fragt das systemd-Journal ab, das Logs von allen Teilen des Systems sammelt, einschließlich Diensten.

Grundlegende Log-Überprüfung

Um alle Logs für einen bestimmten Dienst anzuzeigen, einschließlich historischer Einträge:

journalctl -u mywebapp.service

Dies zeigt alle Logeinträge, die mit mywebapp.service verbunden sind. Wenn der Dienst wiederholt fehlschlägt, sehen Sie Einträge von jedem fehlgeschlagenen Versuch.

Filtern und zeitbasierte Abfragen

Um die Ergebnisse einzugrenzen, insbesondere nach einem kürzlichen Fehler, können Sie Flags wie --since und --priority verwenden:

  • Logs seit einer bestimmten Zeit anzeigen:
    journalctl -u mywebapp.service --since "vor 10 Minuten"
    journalctl -u mywebapp.service --since "2023-10-26 10:00:00"
    
  • Nur Fehlermeldungen oder höher anzeigen:
    journalctl -u mywebapp.service -p err
    
  • Kombinieren mit -xe für erweiterte Erklärung und ausführliche Ausgabe:
    journalctl -u mywebapp.service -xe --since "vor 5 Minuten"
    
    -x kann erklärenden Text für einige systemd-Nachrichten hinzufügen. Behandeln Sie diese Erklärungen als Hinweise, nicht als Ersatz für die unitspezifischen Logs.

Log-Nachrichten verstehen

Suchen Sie nach Schlüsselwörtern wie Fehler, Fehlgeschlagen, Warnung oder anwendungsspezifischen Nachrichten, die darauf hinweisen, was schiefgelaufen ist. Achten Sie auf Zeitstempel, um die Abfolge der Ereignisse zu verstehen, die zum Fehler geführt haben.

Tipp: Wenn das ExecStart-Skript Ihres Dienstes auf die Standardausgabe oder Standardfehlerausgabe schreibt, werden diese Nachrichten normalerweise von journalctl erfasst. Stellen Sie sicher, dass Ihre Skripte beschreibende Fehlermeldungen protokollieren.

Inspizieren der Unit-Datei: Der Bauplan Ihres Dienstes

Jeder systemd-Dienst wird durch eine Unit-Datei (z.B. mywebapp.service) definiert. Fehlkonfigurationen in dieser Datei sind eine häufige Ursache für Startfehler. Sie müssen verstehen, was der Dienst zu tun versucht.

Abrufen der Unit-Datei

Um die aktive Unit-Datei für Ihren Dienst anzuzeigen:

systemctl cat mywebapp.service

Dieser Befehl zeigt die genaue Unit-Datei, die systemd verwendet, einschließlich aller Überschreibungen.

Wichtige Direktiven zum Überprüfen

Konzentrieren Sie sich auf den Abschnitt [Service] für ausführungsbezogene Probleme und [Unit] für Abhängigkeiten.

  • ExecStart: Dies ist der Befehl, den systemd ausführt, um Ihren Dienst zu starten. Überprüfen Sie, ob der Pfad korrekt ist und der Befehl selbst ausführbar ist und erfolgreich ausgeführt wird, wenn er manuell aufgerufen wird (z.B. als angegebener User).
    ExecStart=/usr/local/bin/mywebapp-start.sh
    
  • Type: Definiert den Starttyp des Prozesses. Häufige Typen sind:
    • simple (Standard): ExecStart ist der Hauptprozess.
    • forking: ExecStart erzeugt einen Kindprozess und der Elternprozess beendet sich. Systemd wartet auf das Ende des Elternprozesses.
    • oneshot: ExecStart läuft und beendet sich; systemd betrachtet den Dienst als aktiv, solange der Befehl läuft.
    • notify: Dienst sendet eine Benachrichtigung an systemd, wenn er bereit ist.
    • Ein falscher Type kann dazu führen, dass systemd denkt, ein Dienst sei fehlgeschlagen, obwohl er tatsächlich gestartet ist, oder umgekehrt.
  • User / Group: Der Benutzer und die Gruppe, unter denen der Dienst ausgeführt wird. Berechtigungsprobleme entstehen oft, wenn der Dienst versucht, auf Dateien oder Ressourcen zuzugreifen, für die er unter diesem Benutzer keine Rechte hat.
    User=mywebappuser
    Group=mywebappgroup
    
  • WorkingDirectory: Das Verzeichnis, von dem aus der Dienst ausgeführt wird. Relative Pfade in ExecStart oder anderen Befehlen hängen davon ab.
  • Restart: Definiert, wann der Dienst neu gestartet werden soll. Wenn auf on-failure oder always gesetzt, kann ein fehlschlagender Dienst ständig neu starten, was es schwieriger macht, den anfänglichen Fehler zu erfassen.
  • TimeoutStartSec / TimeoutStopSec: Wie lange systemd auf den Start oder Stopp des Dienstes wartet. Wenn ein Dienst länger für die Initialisierung benötigt als TimeoutStartSec, wird systemd ihn beenden und einen Fehler melden.

Häufige Probleme mit Unit-Dateien

  • Falsche Pfade: Tippfehler in ExecStart oder anderen Dateipfaden.
  • Fehlende Environment-Variablen: Dienste benötigen oft spezifische Umgebungsvariablen (z.B. PATH), die in systemds sauberer Umgebung möglicherweise nicht vorhanden sind (siehe unten).
  • Berechtigungen: Der angegebene User hat keine Ausführungsberechtigung für das Skript oder Lese-/Schreibberechtigung für erforderliche Datendateien.
  • Syntaxfehler: Einfache Tippfehler in der Unit-Datei selbst.

So testen Sie ExecStart manuell:

Wechseln Sie zum Benutzer des Dienstes und versuchen Sie, den Befehl direkt auszuführen:

sudo -u mywebappuser /usr/local/bin/mywebapp-start.sh

Dies reproduziert oft den Fehler, der in journalctl zu sehen ist, direkt in Ihrem Terminal, was das Debuggen erleichtert.

Abhängigkeitsmanagement: Wenn Dienste nicht alleine starten können

Dienste sind oft auf andere Dienste oder Systemkomponenten angewiesen, die aktiv sein müssen, bevor sie selbst starten können. Systemd verwendet die Direktiven Wants, Requires, After und Before, um diese Abhängigkeiten zu verwalten.

Identifizieren von Abhängigkeiten

Verwenden Sie systemctl list-dependencies <Dienstname>, um zu sehen, was ein Dienst explizit benötigt oder wünscht, um zu laufen.

systemctl list-dependencies mywebapp.service

Häufige Direktiven im Abschnitt [Unit]:

  • After=: Gibt an, dass dieser Dienst nach den aufgelisteten Units starten soll. Wenn die aufgelistete Unit fehlschlägt, wird dieser Dienst dennoch versuchen zu starten (es sei denn, Requires= wird ebenfalls verwendet).
  • Requires=: Gibt an, dass dieser Dienst die aufgelisteten Units benötigt. Wenn eine der benötigten Units nicht startet, wird dieser Dienst nicht starten.
  • Wants=: Eine schwächere Form von Requires=. Wenn eine gewünschte Unit fehlschlägt, wird dieser Dienst dennoch versuchen zu starten.

Beispiel:

[Unit]
Description=Meine Webanwendung
After=network.target mysql.service
Requires=mysql.service

Hier ist mywebapp.service nach network.target und mysql.service geordnet und benötigt, dass mysql.service erfolgreich gestartet wird. Wenn mysql.service fehlschlägt, wird mywebapp.service nicht starten.

Beheben von Abhängigkeitskonflikten

Wenn ein Dienst aufgrund eines Abhängigkeitsproblems fehlschlägt, wird journalctl normalerweise anzeigen, welche Abhängigkeit nicht erfüllt werden konnte. Zum Beispiel könnte es Abhängigkeit fehlgeschlagen für Meine Webanwendung gefolgt von Details zum Fehler von mysql.service angeben.

Schritte zur Behebung:

  1. Überprüfen Sie den abhängigen Dienst: Führen Sie systemctl status <abhängiger_Dienst> (z.B. systemctl status mysql.service) und journalctl -u <abhängiger_Dienst> aus, um zuerst dessen Fehler zu beheben.
  2. Überprüfen Sie die After=- und Requires=-Direktiven: Stellen Sie sicher, dass sie die gewünschte Startreihenfolge und Strenge korrekt widerspiegeln. Manchmal muss ein Dienst darauf warten, dass ein bestimmter Port geöffnet ist, nicht nur darauf, dass der Startjob einer anderen Unit abgeschlossen ist. Für enge Prüfungen kann ExecStartPre= helfen. Bei Netzwerkdämonen ist Socket-Aktivierung oder anwendungseigene Wiederholungslogik oft zuverlässiger.

Umgebungsvariablen und Pfade: Die versteckten Fallstricke

Systemd-Dienste laufen in einer sehr sauberen und minimalen Umgebung. Dies führt oft zu Problemen, bei denen Befehle, die in einer Benutzershell einwandfrei funktionieren, bei systemd fehlschlagen, weil wichtige Umgebungsvariablen (wie PATH) fehlen.

Systemds saubere Umgebung

Wenn systemd einen Dienst startet, erbt es nicht die vollständige Umgebung des Benutzers, der systemctl start ausgeführt hat. Die Variable PATH wird beispielsweise oft reduziert, was bedeutet, dass Befehle wie python oder node möglicherweise nicht gefunden werden, wenn sie sich nicht in Standardverzeichnissen wie /usr/bin oder /bin befinden.

Symptom: ExecStart=/usr/local/bin/myscript.sh schlägt fehl mit python: command not found, node: command not found, einem Fehler wegen fehlender Bibliothek oder einer Anwendungsmeldung, dass eine erforderliche Einstellung leer ist.

Behebung: Machen Sie die Dienstumgebung explizit.

[Service]
WorkingDirectory=/opt/mywebapp
Environment="APP_ENV=production"
Environment="PATH=/opt/mywebapp/venv/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin"
ExecStart=/opt/mywebapp/venv/bin/gunicorn app:app

Für viele Variablen verwenden Sie eine Umgebungsdatei:

[Service]
EnvironmentFile=/etc/mywebapp/mywebapp.env
ExecStart=/opt/mywebapp/bin/server

Halten Sie diese Datei einfach. EnvironmentFile= ist kein Bash-Skript. Verwenden Sie KEY=value-Zeilen, nicht export KEY=value, Befehlssubstitution oder Shell-Bedingungen. Setzen Sie auch restriktive Berechtigungen, wenn die Datei Geheimnisse enthält:

sudo chown root:mywebapp /etc/mywebapp/mywebapp.env
sudo chmod 0640 /etc/mywebapp/mywebapp.env

Berechtigungen: Reproduzieren Sie den Fehler als Dienstbenutzer

Berechtigungsprobleme sind häufig, da manuelle Tests oft als root oder als Ihr Anmeldebenutzer durchgeführt werden, während die Unit als dediziertes Dienstkonto läuft.

Überprüfen Sie den konfigurierten Benutzer:

systemctl show mywebapp.service -p User -p Group

Führen Sie dann denselben Befehl als diesen Benutzer aus:

sudo -u mywebappuser /usr/local/bin/mywebapp-start.sh

Wenn die App ein Arbeitsverzeichnis benötigt, schließen Sie es ein:

sudo -u mywebappuser bash -lc 'cd /opt/mywebapp && /usr/local/bin/mywebapp-start.sh'

Schauen Sie über die ausführbare Datei hinaus. Der Dienstbenutzer benötigt möglicherweise Lesezugriff auf /etc/mywebapp/config.yml, Schreibzugriff auf /var/lib/mywebapp, Ausführungszugriff auf jedes übergeordnete Verzeichnis oder die Berechtigung, einen Unix-Socket unter /run/mywebapp zu erstellen. Eine schnelle Überprüfung kann viel Rätselraten ersparen:

sudo -u mywebappuser test -r /etc/mywebapp/config.yml
sudo -u mywebappuser test -w /var/lib/mywebapp
namei -l /var/lib/mywebapp/uploads

Wenn der Dienst nur fehlschlägt, wenn er an einen niedrigen Port wie 80 oder 443 bindet, führen Sie ihn nicht sofort als root aus. Ein Reverse-Proxy, Socket-Aktivierung oder eine gezielte Capability können je nach Dienst sicherer sein.

Startlimits und Neustartschleifen

Ein Dienst, der wiederholt abstürzt, kann mit einer Meldung wie start request repeated too quickly stoppen. Das bedeutet, dass die Ratenbegrenzung von systemd eingegriffen hat. Der ursprüngliche Fehler ist früher aufgetreten, konzentrieren Sie sich also nicht nur auf die Ratenbegrenzungsmeldung.

Verwenden Sie:

journalctl -u mywebapp.service --since "vor 30 Minuten"
systemctl show mywebapp.service -p NRestarts -p Restart -p StartLimitBurst -p StartLimitIntervalUSec

Nachdem Sie die Grundursache behoben haben, löschen Sie den fehlgeschlagenen Zustand:

sudo systemctl reset-failed mywebapp.service
sudo systemctl start mywebapp.service

Seien Sie vorsichtig mit Restart=always. Es ist nützlich für robuste Dämonen, aber während des Debuggens kann es das Journal überfluten und den ersten klaren Fehler verbergen. Sie können die Unit vorübergehend stoppen, die Logs überprüfen und sie manuell starten, sobald Sie eine Sache geändert haben.

Validieren Sie die Unit vor dem Neuladen

Bevor Sie einen Dienst nach dem Bearbeiten einer Unit-Datei neu starten, validieren Sie die Datei und laden Sie systemd neu:

sudo systemd-analyze verify /etc/systemd/system/mywebapp.service
sudo systemctl daemon-reload
sudo systemctl restart mywebapp.service

Wenn der Dienst Drop-In-Überschreibungen hat, überprüfen Sie die zusammengeführte Version:

systemctl cat mywebapp.service
systemctl show mywebapp.service -p FragmentPath -p DropInPaths -p ExecStart

Dies fängt die kniffligen Fälle: Sie haben eine Datei unter /usr/lib/systemd/system bearbeitet, aber ein Drop-In unter /etc/systemd/system/mywebapp.service.d/override.conf ändert immer noch ExecStart; oder Sie haben eine kopierte Unit-Datei korrigiert, die nicht die von systemd geladene ist.

Eine praktische Vorgehensweise

Wenn ein Produktionsdienst ausgefallen ist, verwenden Sie eine kurze, wiederholbare Schleife:

  1. Führen Sie systemctl status mywebapp.service --no-pager aus.
  2. Lesen Sie journalctl -u mywebapp.service --since "vor 15 Minuten".
  3. Überprüfen Sie systemctl cat mywebapp.service.
  4. Überprüfen Sie den Befehl, Benutzer, das Arbeitsverzeichnis, die Umgebung und die Abhängigkeiten.
  5. Reproduzieren Sie den Befehl als Dienstbenutzer.
  6. Nehmen Sie eine Änderung vor.
  7. Führen Sie systemctl daemon-reload aus, wenn sich die Unit geändert hat.
  8. Starten Sie neu und überprüfen Sie das Journal erneut.

Diese Reihenfolge hält die Untersuchung fundiert. Wenn das Journal Permission denied sagt, beheben Sie die Berechtigungen. Wenn es No such file or directory sagt, überprüfen Sie die Pfade aus systemds Sicht. Wenn es Dependency failed sagt, debuggen Sie zuerst die Abhängigkeit. Wenn es sagt, der Prozess wurde mit Status 0/SUCCESS beendet, aber der Dienst ist fehlgeschlagen, überprüfen Sie Type= und ob die Anwendung daemonisiert oder sofort beendet wird.

Das Ziel ist nicht, jede systemd-Direktive auswendig zu lernen. Es geht darum, die Fehlermeldung konsequent mit der Ebene abzugleichen, die sie produziert hat.