Health Checks
Die Management-Routen exponieren zwei Health-Endpunkte:
GET /health— Liveness. Liefert 200, wenn der Prozess betriebsbereit ist.GET /ready— Readiness. Liefert 200, wenn der Pod bereit ist, Traffic zu empfangen (Cluster up + jeder Readiness-Check besteht).
managementRoutes(...) reicht dir eine HealthCheckRegistry (das
Feld health), auf der du eigene Checks für app-spezifische
Gesundheit registrierst:
import { managementRoutes } from 'actor-ts';
const { routes, health } = managementRoutes(system, cluster);
health.addReadiness(async () => { const ok = await db.ping(); return { name: 'database', status: ok, detail: ok ? undefined : 'db unreachable' };});
health.addReadiness(async () => { try { await redis.ping(); return { name: 'cache', status: true }; } catch (e) { return { name: 'cache', status: false, detail: (e as Error).message }; }});
await system.http(8558).bind(routes);Wenn irgendein Check status: false zurückgibt, liefert der
entsprechende Endpunkt 503 mit einem JSON-Body, der das
Ergebnis jedes Checks auflistet.
Die Check-Signatur
Abschnitt betitelt „Die Check-Signatur“type HealthCheckFn = () => Promise<HealthCheckResult> | HealthCheckResult;
type HealthCheckResult = { name: string; // identifies the check in the response status: boolean; // true = healthy detail?: string; // human-readable note, usually on failure};Ein Check kann sync oder async sein. addLiveness /
addReadiness geben jeweils eine Unsubscribe-Funktion zurück —
rufe sie auf, um den Check wieder zu entfernen:
const remove = health.addReadiness(() => ({ name: 'warmup', status: warmedUp }));// ... once warm-up is permanently done:remove();Lang laufende Checks blockieren die Response, also halte sie schnell (sub-sekündlich, idealerweise < 100 ms).
Liveness vs. Readiness
Abschnitt betitelt „Liveness vs. Readiness“| Probe | Was sie beantwortet | Was K8s bei Fehler tut |
|---|---|---|
Liveness (/health) | „Ist dieser Prozess grundlegend kaputt?” | Pod neu starten. |
Readiness (/ready) | „Soll dieser Pod gerade Traffic bekommen?” | Kein Routing mehr hierher (Pod läuft weiter). |
Ein Check ist Liveness oder Readiness, je nachdem, welche Methode
du aufrufst — addLiveness oder addReadiness. Es sind
getrennte Listen; um denselben Check für beide laufen zu lassen,
registriere ihn mit beiden.
Unterschiedliche Semantik treibt unterschiedliche Checks:
- Liveness sollte nur bei nicht behebbaren Problemen
fehlschlagen — Actor-System abgestürzt, Deadlock erkannt,
fundamentale Invarianten gebrochen. Neustart ist der einzige
Fix. Registriere diese mit
addLiveness. - Readiness darf bei vorübergehenden Problemen
fehlschlagen — DB kurz nicht erreichbar, Cache wärmt auf,
Cluster tritt neu bei. Kein Neustart nötig; route nur noch
nicht hierher. Registriere diese mit
addReadiness.
Registriere DB- / Downstream-Checks nicht als Liveness — einen Pod neu zu starten, weil die externe DB kurz zuckte, ist falsch; das Zucken geht vorbei.
Cluster-Readiness
Abschnitt betitelt „Cluster-Readiness“/ready meldet zusätzlich ein clusterReady-Flag, berechnet aus
der Cluster-Mitgliedschaft — unabhängig von deinen registrierten
Checks. Es ist false, bis der lokale Node den Zustand Up
erreicht (und immer true, wenn die Routen ohne Cluster gebaut
wurden):
GET /ready→ 503{ "status": "DOWN", "clusterReady": false, "checks": [] }Liefert clusterReady: true (und 200, falls auch deine
Readiness-Checks bestehen), sobald der Node Up ist — das
kanonische „auf den Cluster warten”-Gate.
Mehrere Checks
Abschnitt betitelt „Mehrere Checks“health.addReadiness(dbCheck);health.addReadiness(cacheCheck);health.addReadiness(downstreamApiCheck);Alle Readiness-Checks laufen parallel, wenn /ready getroffen
wird. Die Response listet das Ergebnis jedes Checks:
{ "status": "DOWN", "clusterReady": true, "checks": [ { "name": "database", "status": false, "detail": "connection refused" }, { "name": "cache", "status": true }, { "name": "downstream-api", "status": true } ]}Das Aggregat ist UP, wenn clusterReady und der status
jedes Checks true ist.
Checks testen
Abschnitt betitelt „Checks testen“import { HealthCheckRegistry } from 'actor-ts';
it('readiness fails when the DB is down', async () => { const health = new HealthCheckRegistry(); health.addReadiness(async () => ({ name: 'db', status: false, detail: 'mock' }));
const results = await health.checkReadiness(); expect(results).toEqual([{ name: 'db', status: false, detail: 'mock' }]);});checkLiveness() / checkReadiness() führen die registrierten
Checks aus und geben das HealthCheckResult[] zurück, das die
Endpunkte aggregieren — praktisch, um Checks isoliert zu testen.
Ein Check, der wirft, wird abgefangen und als
{ name: 'unknown', status: false, detail } gemeldet. Der
isHealthy(results)-Helfer ist dasselbe „alle bestehen”-Prädikat,
das die Endpunkte nutzen.
Timeouts
Abschnitt betitelt „Timeouts“Es gibt kein eingebautes Per-Check-Timeout — ein hängender
Check blockiert die gesamte /health- (oder /ready-)Response.
Für einen Check, der stocken kann, race ihn gegen deine eigene
Deadline:
health.addReadiness(async () => { const status = await Promise.race([ slowProbe().then(() => true), new Promise<boolean>((r) => setTimeout(() => r(false), 2_000)), ]); return { name: 'downstream', status };});Ohne Guard trifft ein steckengebliebener Check irgendwann K8s’ eigenes Probe-Timeout (10 s Default) und löst einen Neustart aus. Halte Checks schnell.
Wo es weitergeht
Abschnitt betitelt „Wo es weitergeht“- Management — Überblick — das Gesamtbild.
- HTTP-Endpunkte — die volle Endpunkt-Referenz.
- Kubernetes-Deployment — die Probe-Konfiguration, mit der das zusammenspielt.
