
Vom einfachen Health-Check zum echten Funktionstest: Wie wir n8n und Qdrant überwachen
Seit einiger Zeit setzen wir n8n sowohl intern als auch bei unseren Kunden produktiv ein. Hier im Blog hat bereits Christian über unseren Spamschutz mit n8n berichtet. Eifrige Leser werden auch wissen, dass wir unter anderem Gatus als Monitoring-System nutzen. Bisher fehlte uns jedoch eine Überwachung der Funktionsfähigkeit von n8n und der Vektordatenbank Qdrant, die wir gerne für KI-basierte Workflows nutzen.
Diese Lücke haben wir jetzt geschlossen. Dabei hat sich gezeigt, dass hinter einem scheinbar einfachen "Health-Check einrichten" deutlich mehr steckt als zunächst gedacht.
Schritt 1: Bereit?
Health Checks richtig einsetzen
Viele Dienste haben bereits einen sogenannten Health-Check. Das ist eine URL, die kein eigentliches Feature liefert, sondern nur eine Aussage über den Zustand der Anwendung selbst. Ein externes Tool wie Gatus ruft diese URL in einem festen Intervall auf und schlägt Alarm, wenn die Antwort nicht mehr passt.
Dabei lohnt sich eine Unterscheidung, die ursprünglich aus der Kubernetes-Welt kommt und sich inzwischen in vielen Anwendungen wiederfindet: Liveness und Readiness. Eine Liveness-Prüfung beantwortet nur "läuft der Prozess überhaupt", nichts weiter. Eine Readiness-Prüfung geht einen Schritt weiter und beantwortet "kann die Anwendung gerade tatsächlich Anfragen sinnvoll bearbeiten". Ein Prozess kann laufen und trotzdem nicht bereit sein, zum Beispiel weil die Datenbankverbindung gerade unterbrochen ist.
n8n bringt für genau diese beiden Fragen zwei unterschiedliche öffentliche Endpunkte mit. /healthz beantwortet nur die Liveness-Frage, ein 200 bedeutet lediglich, dass der Prozess antwortet. /healthz/readiness geht weiter. Der Endpunkt liefert erst dann ein 200, wenn zusätzlich die Datenbankverbindung steht und alle Migrationen abgeschlossen sind. Erst dann ist n8n tatsächlich in der Lage, Workflows entgegenzunehmen und auszuführen. Wer n8n produktiv betreibt, ohne sich tiefer mit dem Unterbau zu beschäftigen, bekommt mit dem Readiness-Endpunkt also von Haus aus schon eine deutlich aussagekräftigere Prüfung, als der schlichte Name vermuten lässt.
Daher testen wir beide Endpunkte getrennt, einmal /healthz und einmal /healthz/readiness
endpoints:
- name: n8n-health
url: "https://n8n.example.com/healthz"
interval: 5m
conditions:
- "[STATUS] == 200"
- name: n8n-readiness
url: "https://n8n.example.com/healthz/readiness"
interval: 5m
conditions:
- "[STATUS] == 200"Das ist ein sinnvoller, solider erster Schritt, und an dieser Stelle hören die meisten Monitoring-Setups auch auf. Verständlich, denn genau dafür wurde ein Readiness-Endpunkt gebaut.
Schritt 2: Fertig?
Wenn "erreichbar" noch nicht "funktionierend" bedeutet
Für uns ist Schritt 1 aber nicht das Ende, sondern die Grundlage. Denn /healthz/readiness sagt nichts darüber, ob der Workflow, der darauf aufbaut, gerade tut, was er soll. Ein Workflow kann technisch fehlerfrei erreichbar sein und trotzdem mittendrin an einem Node scheitern, eine Klassifizierung falsch auswerten oder schlicht hängen bleiben, ohne dass ein Status-Code davon etwas mitbekommt. Bei Automatisierung, die mehrere Systeme miteinander verbindet, Formulare, KI-Klassifizierung, eine Vektordatenbank, E-Mail-Versand, sind "die Schnittstelle antwortet" und "die Fachlogik hat funktioniert" zwei komplett unterschiedliche Aussagen. Nur die zweite ist es, worauf es am Ende ankommt, und genau die prüft ein Readiness-Check nicht.
Die Ausführungshistorie eines Workflows lässt sich über die REST API von n8n abrufen. Die Antwort ist ein JSON Objekt. Also fragen wir regelmäßig den Status der Workflows ab und analysieren die Antwort.
Die REST API erlaubt zwar eine Filterung nach Status und Anzahl der Ausführungen, nicht jedoch nach einem Zeitraum. Gleichzeitig kennt Gatus keine Datumsfunktionen. Wir haben nur den Status der letzten Läufe und können nicht sehen, wie viele Läufe in dem letzten Zeitintervall fehlgeschlagen sind. Ein einzelner erfolgreicher Lauf könnte deshalb einen zuvor fehlgeschlagenen Lauf verdecken.
So sieht das in der Konfiguration für Gatus aus.
endpoints:
- name: n8n-workflow-spamschutz
url: "https://n8n.example.com/api/v1/executions?workflowId=123&limit=5"
headers:
X-N8N-API-KEY: "${N8N_API_KEY}"
interval: 5m
conditions:
- "[STATUS] == 200"
- "[CERTIFICATE_EXPIRATION] > 120h"
- "[BODY].data[0].status == success"
- '[BODY] != pat(*"status":"error"*)'Vier Bedingungen, die zusammen mehr aussagen als jede einzelne für sich:
[STATUS] == 200: die n8n-API antwortet überhaupt.[CERTIFICATE_EXPIRATION] > 120h: das TLS-Zertifikat läuft nicht in Kürze ab, ein Klassiker, der gerne übersehen wird und dann einen ganz anderen, unnötigen Ausfall verursacht.[BODY].data[0].status == success: die zeitlich letzte Ausführung des Workflows war erfolgreich.[BODY] != pat(*"status":"error"*): keine der letzten fünf Ausführungen (überlimit=5abgefragt) enthält irgendwo im Ergebnis ein"status":"error".
Die letzten beiden Zeilen wirken redundant, sind es aber bewusst nicht. Die erste ist präzise: Es wird der letzte Lauf des Workflows analysiert. Die zweite ist breiter gefasst, es wird geprüft, ob in den letzten Läufen kein Error enthalten ist. Das ist zum einen weniger Schreibarbeit als genau alle 5 Läufe zu prüfen und funktioniert außerdem unabhängig davon, ob tatsächlich fünf Ausführungen vorhanden sind.
Hinweis: Im Gegensatz zu den Health-Checks benötigt die REST-API eine Authentifizierung.
Abgrenzung
Eine offene Frage, die wir bewusst offen lassen: Nicht jeder technische Fehlschlag ist ein Vorfall. Wird ein Workflow während der Entwicklung manuell im Editor getestet und schlägt dabei fehl, ist das kein Betriebsproblem, taucht in der Ausführungshistorie aber genauso auf wie ein echter Fehler im Produktivbetrieb. n8n unterscheidet Ausführungen unter anderem danach, wie sie ausgelöst wurden, eine Trennung zwischen "im Editor getestet" und "produktiv gelaufen" wäre also grundsätzlich möglich. Ob sich das in der Praxis überhaupt so oft überschneidet, dass sich der zusätzliche Aufwand lohnt, schauen wir uns erst einmal an, statt vorschnell weitere Komplexität einzubauen, die sich später vielleicht als unnötig erweist.
Schritt 3: Anders!
Warum Datenbanken andere Checks brauchen
Bei n8n war der zusätzliche Schritt über den Readiness-Check hinaus wichtig für den Betrieb des Gesamtsystems. Bei Datenbanken ist ein erweiterter Check nicht sinnvoll, denn dann würde man unweigerlich den Inhalt der Datenbank prüfen. Daher überwachen wir auch hier die beiden Endpunkte /healthz und /readyz. Auch hier der Unterschied: /healthz bestätigt nur, dass der Prozess antwortet, /readyz liefert erst dann den HTTP Statuscode 200 zurück, wenn die Collections tatsächlich geladen sind.
endpoints:
- name: qdrant-health
url: "https://qdrant.example.com/healthz"
interval: 5m
conditions:
- "[STATUS] == 200"
- name: qdrant-readiness
url: "https://qdrant.example.com/readyz"
interval: 5m
conditions:
- "[STATUS] == 200"Hinweis aus der Praxis
Eine Automatisierung, die man einmal aufsetzt und danach sich selbst überlässt, trägt im Kern das gleiche Risiko wie ein Server ohne Backup, mit dem Unterschied, dass es niemandem auffällt, bis der Ausfall bereits Kosten verursacht hat. Wer produktiv mit n8n, KI-Klassifizierung oder einer Vektordatenbank arbeitet, betreibt ein System, das genauso viel Aufmerksamkeit braucht wie jede andere Produktivumgebung.
Kurzer Ausblick
Für den Moment beobachten wir, wie sich die aktuelle Lösung im Alltag schlägt, statt im Voraus weitere Ebenen einzuziehen. Sollte sich zeigen, dass die Unterscheidung zwischen echten und harmlosen Fehlschlägen in der Praxis relevant wird, ist das der nächste logische Schritt, aber eben erst dann.
Wir freuen uns, wenn Ihr diesen Beitrag teilt.
Kommentare
Keine Kommentare gefunden.