Zum Inhalt springen
Deutsch

Health Checks

Der Management-Server stellt zwei Health-Endpunkte bereit:

  • GET /healthLiveness. Gibt 200 zurück, wenn der Prozess operational ist.
  • GET /readyReadiness. Gibt 200 zurück, wenn der Pod bereit ist, Traffic zu empfangen (Cluster up + eigene Checks bestehen).

Das Framework registriert ein paar Defaults (cluster-up für ready), plus du kannst eigene Checks für app-spezifische Health einklinken.

import { HttpManagement } from 'actor-ts';
const { health } = await HttpManagement.start(system, { port: 8558 });
health.addCheck('database', async () => {
const ok = await db.ping();
return ok ? { ok: true } : { ok: false, reason: 'db unreachable' };
});
health.addCheck('cache', async () => {
try {
await redis.ping();
return { ok: true };
} catch (e) {
return { ok: false, reason: (e as Error).message };
}
});

Wenn irgendein Check { ok: false } zurückgibt, antwortet der Endpunkt mit 503 und einem JSON-Body, der die fehlgeschlagenen Checks auflistet.

type HealthCheck = () => Promise<HealthCheckResult>;
interface HealthCheckResult {
ok: boolean;
reason?: string; // menschenlesbare Fehlerbeschreibung
details?: unknown; // strukturierte Info für Diagnostik
}

Checks sind async — gib eine Promise zurück. Lang laufende Checks blockieren die Response, also halte sie schnell (sub-Sekunde, idealerweise < 100 ms).

ProbeWas sie beantwortetWas K8s bei Fehler macht
Liveness (/health)„Ist dieser Prozess fundamental kaputt?”Pod neu starten.
Readiness (/ready)„Sollte dieser Pod gerade Traffic bekommen?”Routing stoppen zu diesem Pod (Pod laufen lassen).

Unterschiedliche Semantik treibt unterschiedliche Checks:

  • Liveness sollte nur bei nicht behebbaren Problemen fehlschlagen — Actor-System gecrashed, Deadlock erkannt, fundamentale Invarianten gebrochen. Restart ist der einzige Fix.
  • Readiness darf bei transienten Problemen fehlschlagen — DB kurz nicht erreichbar, Cache wärmt sich auf, Cluster rejoint. Kein Restart nötig; einfach noch nicht hierhin routen.

Stecke nicht alle Checks in beide — einen Pod neu zu starten, weil die externe DB geblinkt hat, ist falsch; der DB-Blinker geht vorbei. Stecke DB-Checks nur in Readiness.

Wenn der Management-Server mit einem cluster konfiguriert ist, schlägt der Default-Readiness-Check fehl, falls der lokale Node nicht Up ist:

GET /ready
{ ok: false, reason: 'cluster not joined yet' }

Gibt 200 zurück, sobald SelfUp feuert. Das ist der kanonische „warte auf den Cluster”-Check.

health.addCheck('database', dbCheck);
health.addCheck('cache', cacheCheck);
health.addCheck('downstream-api', apiCheck);

Alle Checks laufen parallel, wenn der Endpunkt getroffen wird. Die Response enthält Per-Check-Status:

{
"ok": false,
"checks": {
"cluster": { "ok": true },
"database": { "ok": false, "reason": "connection refused" },
"cache": { "ok": true },
"downstream-api": { "ok": true }
}
}

Das Aggregat-ok ist true, wenn jeder Check wahr ist.

health.addCheck('actor-system-alive', async () => {
return {
ok: !system.isTerminated,
reason: system.isTerminated ? 'system terminated' : undefined,
};
}, { liveness: true, readiness: false });

Das optionale zweite Argument routet einen Check zu Liveness-only (Default readiness: true) oder Readiness-only.

Der „System nicht terminated”-Check wird vom Framework automatisch als Liveness-only registriert — es ist ein nicht behebbarer Zustand.

import { TestKit } from 'actor-ts/testkit';
it('Health Check schlägt fehl, wenn DB unten ist', async () => {
const tk = TestKit.create();
const { health } = await HttpManagement.start(tk.system, { port: 0 });
health.addCheck('db', async () => ({ ok: false, reason: 'mock' }));
const result = await health.run();
expect(result.ok).toBe(false);
expect(result.checks!.db).toEqual({ ok: false, reason: 'mock' });
await tk.shutdown();
});

HealthCheckRegistry.run() exponiert die gleiche Logik wie der Endpunkt — nützlich für Unit-Tests deiner Checks.

health.addCheck('slow-thing', slowCheck, { timeoutMs: 2_000 });

Per-Check-Timeout. Ein Check, der das Timeout überschreitet, wird als { ok: false, reason: 'timeout' } behandelt.

Ohne Timeout blockiert ein hängender Check die ganze /health-Response — und löst irgendwann das eigene Probe- Timeout von K8s (10 s Default) und einen Restart aus. Setze Check-Timeouts konservativ.