Heimserver & Self-Hosting

systemd Service Watchdogs - Einen lebenden Prozess ohne Fortschritt erkennen

systemd Service Watchdogs - Einen lebenden Prozess ohne Fortschritt erkennen

Ein Prozess kann weiterhin eine PID haben, kaum CPU-Zeit beanspruchen und als active (running) erscheinen, obwohl seine eigentliche Arbeit zum Stillstand gekommen ist. Seine Event Loop kann blockiert sein, ein Worker kann endlos warten oder eine interne Sperre kann jeden Request am Fortschritt hindern. Eine Restart-Regel, die nur auf das Ende des Prozesses reagiert, kann hier nichts tun, denn der Prozess wurde nicht beendet.

Ein systemd Service Watchdog liefert ein enger gefasstes Signal: Eine kooperierende Anwendung verspricht, regelmäßig Keep-alive-Nachrichten zu senden. Bleiben diese Nachrichten aus, behandelt systemd die verpasste Frist als Fehler. Entscheidend ist das Wort kooperierend. Eine einzelne Direktive bringt einem beliebigen Daemon nicht bei, seinen Zustand zu melden, und ein Heartbeat beweist nicht, dass Nutzer korrekte Antworten erhalten.

Die nützliche Frage lautet daher nicht nur: „Wie aktiviere ich WatchdogSec=?“ Sie lautet: Welchen Fortschritt repräsentiert jeder Heartbeat, und was soll geschehen, wenn dieses Indiz ausbleibt?

Laufend, ansprechbar und korrekt sind verschiedene Zustände

Die Prozessüberwachung kann feststellen, ob ein Prozess gestartet oder beendet wurde, ein Signal erhalten oder ein konfiguriertes Timeout überschritten hat. Damit lassen sich bereits viele Fehler erfassen. Sie weiß jedoch nicht automatisch, ob die Hauptschleife weiterläuft oder die Anwendung korrekte Ergebnisse liefert.

Ein Service Watchdog ergänzt ein Signal von der Anwendung an den Manager. Laut der versionierten Debian-Dokumentation systemd.service(5) konfiguriert WatchdogSec= nach abgeschlossenem Start eine Frist. Der Dienst muss systemd regelmäßig mit WATCHDOG=1 benachrichtigen. Ist der Abstand zwischen zwei Benachrichtigungen größer als diese Frist, versetzt systemd die Unit in den Fehlerzustand und beendet sie standardmäßig mit SIGABRT; mit WatchdogSignal= lässt sich ein anderes Signal wählen.

Das ist aussagekräftiger als „die PID existiert“, aber schwächer als eine End-to-End-Prüfung. Ein Programm kann einen Heartbeat senden, während es HTTP-500-Antworten zurückgibt, veraltete Daten liest oder seine Datenbank nicht erreicht. Der Watchdog weiß nur, dass der für die Nachricht zuständige Codepfad vor Ablauf der Frist ausgeführt wurde.

Der Watchdog ist ein zweiseitiger Vertrag

Die Unit-Datei legt die Frist fest. systemd stellt sie dem Dienst anschließend über WATCHDOG_USEC in Mikrosekunden zur Verfügung und kann mit WATCHDOG_PID den erwarteten Prozess angeben. Über die API sd_watchdog_enabled(3) kann ein Daemon diese Werte abfragen, statt ein Intervall fest im Code zu hinterlegen.

In der Gegenrichtung sendet die Anwendung Benachrichtigungen über den in NOTIFY_SOCKET angegebenen Socket. Die Protokolldokumentation sd_notify(3) definiert WATCHDOG=1 als Keep-alive-Nachricht, die den Watchdog-Zeitstempel von systemd aktualisiert. Sie definiert außerdem READY=1, womit ein Dienst des Typs Type=notify seinen abgeschlossenen Start meldet. Readiness ist ein Zustandsübergang; ein Watchdog-Heartbeat ist ein wiederkehrendes Indiz.

Upstream empfiehlt, das Keep-alive nach der Hälfte des Watchdog-Intervalls zu senden. Bei einer Frist von 30 Sekunden bedeutet das, ungefähr alle 15 Sekunden zu senden, statt bis zum letzten Moment zu warten. Diese Reserve hilft bei gewöhnlichen Verzögerungen durch das Scheduling, macht 30 Sekunden aber nicht zu einem universell passenden Wert. Die Frist muss genug Raum für den längsten gesunden Vorgang im jeweiligen Anwendungspfad lassen.

Die Position des Heartbeats bestimmt, was überwacht wird

Man kann zwei Entwürfe vergleichen. Im ersten sendet ein separater Timer-Thread WATCHDOG=1, unabhängig davon, was der Worker tut. Der Prozess kann weiterhin lebendig wirken, obwohl seine Request-Schleife dauerhaft blockiert ist. Im zweiten sendet die Hauptschleife erst dann einen Heartbeat, wenn sie eine sinnvolle Iteration abgeschlossen oder überprüft hat, dass ihre Worker Fortschritte gemacht haben.

Der zweite Entwurf liefert nützlichere Hinweise, doch was „sinnvoll“ bedeutet, hängt von der Anwendung ab. Bei einem Queue Worker könnte die Consumer-Schleife erfolgreich pollen müssen. Ein Webserver könnte prüfen, dass seine Event Loop weiterläuft und die Koordination der Worker reagiert. Ein Heartbeat sollte nicht in jedem Intervall eine teure vollständige Diagnose ausführen, aber ebenso wenig von jeder nützlichen Arbeit entkoppelt sein.

Das ist eine Designinterpretation auf Grundlage der Semantik des Benachrichtigungsprotokolls und keine von systemd gelieferte Garantie. PID 1 kann die Absicht einer Anwendung nicht untersuchen. Die Entwickler des Daemons müssen entscheiden, welcher interne Fortschritt das Senden eines Pings rechtfertigt.

Auch die Zuordnung einer Benachrichtigung ist wichtig. NotifyAccess= steuert, von welchen Prozessen eines Dienstes Nachrichten akzeptiert werden. Wird WatchdogSec= ohne einen expliziten Wert für den Zugriff verwendet, setzt systemd implizit NotifyAccess=main. Die Protokolldokumentation warnt, dass kurzlebige Hilfsprozesse ohne passend entworfenen Zugriff und Synchronisierung schwer zuzuordnen sein können. Noch wichtiger ist, dass ein allgemeiner externer Helfer, der weiter pingt, während die eigentliche Anwendung festhängt, den Zweck des Watchdogs zunichtemacht.

Eine zurückhaltende Unit-Konfiguration

Für Software, die Readiness- und Watchdog-Benachrichtigungen von systemd ausdrücklich unterstützt, könnte der relevante Teil einer Unit so aussehen:

[Service]
Type=notify
ExecStart=/usr/local/bin/example-worker
WatchdogSec=30s
Restart=on-failure
RestartSec=10s

example-worker ist ein Platzhalter und kein auf diesem Server beobachteter Dienst. Diese Konfiguration ist nur geeignet, wenn das Programm nach der Initialisierung READY=1 und anschließend zuordenbare Nachrichten mit WATCHDOG=1 sendet. Ohne READY=1 schließt ein Dienst mit Type=notify seinen normalen Startübergang nicht ab. Ohne wiederkehrende Watchdog-Nachrichten schlägt er nach der konfigurierten Frist fehl.

Restart=on-failure umfasst Watchdog-Fehler. Restart=on-watchdog ist enger gefasst, wenn nur ein verpasster Watchdog einen automatischen Neustart auslösen soll. Keine der beiden Entscheidungen ist grundsätzlich harmlos. Ein Neustart kann Requests unterbrechen, nicht idempotente Arbeit wiederholen oder eine gestörte Abhängigkeit zusätzlich belasten. Automatische Versuche unterliegen außerdem den Start-Ratenbegrenzungen von systemd.

Die bestehende Restart-Policy, das Shutdown-Verhalten, persistenter Zustand und der Recovery-Vertrag müssen weiterhin geprüft werden. Ein Watchdog erkennt eine Fehlerklasse; er entscheidet nicht, ob eine erneute Ausführung des Prozesses sicher ist.

Unterstützung vor einer Änderung der Unit prüfen

Ausgangspunkt sollten die Dokumentation der Anwendung und die mit dem Paket gelieferte Unit sein. Zu suchen ist nach ausdrücklicher Unterstützung für sd_notify, WatchdogSec, WATCHDOG_USEC oder einer dokumentierten Option für systemd-Benachrichtigungen. Der systemd-Integrationsleitfaden daemon(7) behandelt Statusbenachrichtigungen als Verhalten, das ein moderner Daemon implementiert, und nicht als etwas, das der Manager aus der Existenz einer PID ableitet.

systemctl cat example-worker.service
systemctl show example-worker.service \
  --property=Type,NotifyAccess,WatchdogUSec,WatchdogTimestamp,Result

systemctl cat zeigt die Paket-Unit und die von der Festplatte geladenen Drop-ins. systemctl show stellt normalisierte Manager-Eigenschaften dar; Unit-Einstellungen mit der Endung Sec erscheinen dort häufig als Eigenschaften mit USec. Ein Watchdog-Intervall von null bedeutet, dass die Service-Watchdog-Überwachung deaktiviert ist.

Wenn ein Shell-basierter Dienst das Protokoll bewusst implementiert, ist systemd-notify(1) der Kommandozeilen-Wrapper für sd_notify(). Die Watchdog-Nachricht ist eine gewöhnliche Zuweisung:

systemd-notify WATCHDOG=1

Dieser Befehl ist keine Empfehlung für einen Cronjob oder eine externe Schleife. Er muss im Kontext des überwachten Dienstes, mit korrektem Benachrichtigungszugriff und an einer Stelle ausgeführt werden, die tatsächlichen Fortschritt der Anwendung repräsentiert.

Konfiguration validieren und Verhalten anschließend sicher testen

Eine Unit kann vor der Installation geprüft werden:

systemd-analyze verify ./example-worker.service

Laut der Dokumentation systemd-analyze(1) kann verify unter anderem unbekannte Direktiven, fehlende erforderliche Abhängigkeiten und nicht verfügbare Befehle melden. Ein fehlerfreies Ergebnis validiert einen Teil der Konfiguration. Es beweist nicht, dass die Anwendung Pings sendet, dass diese Pings auf sinnvolle Arbeit folgen oder dass ein Neustart Daten bewahrt.

Verhaltenstests sollten mit einer Nicht-Produktionsinstanz oder in einem Wartungsfenster stattfinden. Zuerst ist ein normaler Start einschließlich Readiness und mehrerer Heartbeat-Intervalle zu beobachten. Danach sollte der von der Anwendung unterstützte Testmechanismus genutzt werden, um die überwachte Schleife anzuhalten oder zu blockieren, ohne Zustand zu beschädigen. Zu den erwarteten Indizien gehören ein Watchdog-Fehler im Journal, das konfigurierte Terminierungssignal, der Result der Unit und jeder durch die Policy erlaubte Neustart.

systemctl status example-worker.service
journalctl --unit=example-worker.service --since today
systemctl show example-worker.service \
  --property=ActiveState,SubState,Result,NRestarts,WatchdogTimestamp

Eine unbekannte produktive Datenbank, einen Speicherdienst oder Queue Worker einzufrieren, ist kein sicherer Test. Ein erzwungener Fehler kann Sperren halten, Schreibvorgänge unterbrechen, Client-Retries auslösen oder einen Core Dump erzeugen. Der Testplan benötigt eine ausdrückliche Abbruchbedingung und ein Rollback, das das Drop-in entfernt oder die vorherigen Unit-Einstellungen wiederherstellt.

Was ein Service Watchdog nicht beweisen kann

  • Er kann nicht beweisen, dass eine HTTP-Antwort korrekt oder überhaupt erfolgreich ist.
  • Er kann nicht beweisen, dass eine Datenbank, ein DNS-Resolver, eine Remote API oder ein Speichergerät fehlerfrei arbeitet.
  • Er kann einen logischen Fehler nicht erkennen, solange der Heartbeat-Pfad weiterläuft.
  • Er kann nicht garantieren, dass das Beenden und Neustarten des Prozesses sicher ist.
  • Er kann Monitoring aus Sicht des Netzwerks oder der Nutzer nicht ersetzen.

Ein externer Health Check und ein interner Watchdog beantworten verschiedene Fragen. Die externe Prüfung fragt, ob ein Client ein akzeptables Ergebnis erhalten kann. Der Watchdog fragt, ob ausgewählter Code innerhalb des Dienstes vor Ablauf der Frist Fortschritt gemeldet hat. Einer kann fehlschlagen, während der andere erfolgreich ist. Gemeinsam können sie Unsicherheit reduzieren, doch keiner von beiden sollte als vollständiger Beweis für einen gesunden Dienst beschrieben werden.

Es gibt außerdem Dienste, denen ein Watchdog wenig Nutzen bringt. Ein kurzlebiger Job mit bereits begrenzter Laufzeit, eine Anwendung ohne verlässlichen Fortschrittspunkt oder Software, die sich nicht sicher neu starten lässt, ist mit Logs, Alarmen und einer bewussten Reaktion durch einen Operator möglicherweise besser bedient. Mehr automatische Wiederherstellung bedeutet nicht immer mehr Zuverlässigkeit.

Checkliste für eine vorsichtige Einführung

  1. Prüfen, ob die genaue Anwendungsversion systemd Watchdog-Benachrichtigungen unterstützt.
  2. Festlegen, welcher nützliche Fortschritt vor dem Senden eines Heartbeats erfolgt sein muss.
  3. Eine Frist wählen, die mit Scheduling-Reserve länger als der längste gültige Vorgang ist.
  4. NotifyAccess= prüfen und sicherstellen, dass der erwartete Prozess die Nachricht sendet.
  5. Entscheiden, ob ein Watchdog-Fehler den Dienst neu starten soll und ob dieser Neustart sicher ist.
  6. Start-Ratenbegrenzungen, Logs und einen Alarmweg beibehalten, damit wiederholte Fehler nicht zu unbemerktem Neustart-Churn werden.
  7. Die Unit-Syntax validieren und einen fehlenden Heartbeat außerhalb des Produktionsverkehrs testen.
  8. Eine externe Prüfung für Verhalten beibehalten, das der interne Heartbeat nicht beobachten kann.

Der Heartbeat sollte etwas bedeuten

Ein systemd Service Watchdog schließt eine echte Lücke zwischen „der Prozess wurde beendet“ und „der Prozess existiert noch“. Sein Wert entsteht aus einem kleinen, ausdrücklichen Vertrag: Der Manager setzt eine Frist, und die Anwendung meldet vor ihrem Ablauf fortlaufenden Fortschritt.

Der Mechanismus wird irreführend, wenn der Ping zu einem Ritual wird. Kann ein unbeteiligter Helfer ihn unbegrenzt senden, überlebt das grüne Signal die Arbeit, die es repräsentieren sollte. Ausgangspunkt sollte das Fehlermodell der Anwendung sein; der Heartbeat gehört hinter sinnvollen Fortschritt, und ein Neustart ist als Recovery-Versuch statt als Reparatur zu betrachten. Das Ergebnis ist kein vollkommenes Wissen über den Zustand eines Dienstes, aber ein ehrlicheres Indiz als eine PID allein.

References