Liveness, Readiness und Startup Probe in Kubernetes
1. Übersicht
Wenn du eine Anwendung auf Kubernetes ausführst, weiß der Cluster nicht automatisch, ob dein Pod „am Leben” ist oder „bereit für Traffic” — er muss dies über Probes abfragen. Dieser Artikel erklärt die Unterschiede zwischen Liveness-, Readiness- und Startup-Probe, zusammen mit praktischen Beispielen für Spring Boot Actuator und Deployment YAML. Nach dem Lesen kannst du Probes korrekt für einen echten Service wie OrderService konfigurieren, ohne dass Pods unnötig neu gestartet werden.
2. Warum Probes notwendig sind
Standardmäßig weiß Kubernetes nur, ob der Prozess im Container läuft (Container-Status Running) oder nicht. Es weiß nicht, ob die Anwendung darin Deadlock-Zustände hat, die Datenbankverbindung verloren hat oder noch hochfährt.
Die Folgen ohne Probes:
- Pod bleibt hängen (Deadlock), empfängt aber immer noch Traffic → zahlreiche Request-Timeouts.
- Pod startet gerade, hat die DB-Verbindung noch nicht hergestellt, wird aber bereits von Service als Endpoint hinzugefügt → 500-Fehler.
Drei Probe-Typen lösen drei verschiedene Probleme:
| Probe | Beantwortet die Frage | Im Fehlerfall |
|---|---|---|
| Liveness | Läuft der Container noch? | Kubernetes startet den Container neu |
| Readiness | Ist der Container bereit für Traffic? | Kubernetes entfernt den Pod aus Service-Endpoints (kein Neustart) |
| Startup | Ist der Container hochgefahren? | Kubernetes wartet, deaktiviert Liveness/Readiness vorübergehend |
3. Liveness Probe
Liveness Probe beantwortet die Frage: „Befindet sich dieser Prozess in einem Zustand, aus dem er sich nicht selbst erholen kann?“. Falls die Probe mehr als failureThreshold mal fehlschlägt, startet kubelet den Container neu.
3.1. Spring Boot Actuator hinzufügen
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
management.endpoint.health.probes.enabled=true
management.endpoints.web.exposure.include=health,info
Die Zeile management.endpoint.health.probes.enabled=true aktiviert zwei Unter-Endpoints: /actuator/health/liveness und /actuator/health/readiness, genau wie Kubernetes es erwartet.
3.2. Im Deployment deklarieren
apiVersion: apps/v1
kind: Deployment
metadata:
name: order-service
spec:
template:
spec:
containers:
- name: order-service
image: nonladev/order-service:1.0
ports:
- containerPort: 8080
livenessProbe:
httpGet:
path: /actuator/health/liveness
port: 8080
initialDelaySeconds: 20
periodSeconds: 10
failureThreshold: 3
initialDelaySeconds: 20: Warte 20 Sekunden nach dem Start des Containers, bevor die erste Probe läuft.periodSeconds: 10: Probe alle 10 Sekunden.failureThreshold: 3: Nach 3 aufeinanderfolgenden Fehlschlägen gilt der Container als defekt → Neustart.
Wichtig: Liveness Probe sollte nur überprüfen, „läuft die Anwendung noch?“, nicht externe Abhängigkeiten (DB, Redis…). Wenn die DB ausfällt und Liveness dadurch fehlschlägt, wird Kubernetes den Pod ständig neu starten, ohne das eigentliche Problem zu lösen — das ist ein sehr häufiger Fehler.
4. Readiness Probe
Readiness Probe beantwortet: „Kann der Pod Traffic verarbeiten?“. Bei Fehlschlag wird der Pod aus den Endpoints des Service entfernt — Traffic gelangt nicht mehr hin, der Container wird aber nicht neu gestartet.
4.1. Benutzerdefinierter Readiness-Indikator
Angenommen, OrderService muss sich erst mit PaymentGateway verbinden, bevor er bereit ist:
@Component
public class PaymentGatewayReadinessIndicator implements HealthIndicator {
private final PaymentGatewayClient paymentGatewayClient;
public PaymentGatewayReadinessIndicator(PaymentGatewayClient paymentGatewayClient) {
this.paymentGatewayClient = paymentGatewayClient;
}
@Override
public Health health() {
if (paymentGatewayClient.isConnected()) {
return Health.up().withDetail("gateway", "connected").build();
}
return Health.down().withDetail("gateway", "unreachable").build();
}
}
Um diesen Indikator in die readiness-Gruppe einzubeziehen (ohne Liveness zu beeinflussen), definiere eine Gruppe:
management.endpoint.health.group.readiness.include=readinessState,paymentGateway
health()gibtHealth.down()zurück, wenn sichPaymentGatewayClientnicht verbinden kann — die Readiness-Probe schlägt fehl, der Pod erhält keinen Traffic, bis das Gateway bereit ist.- Spring nennt automatisch das Bean
paymentGatewayReadinessIndicatorim Health-Response um zupaymentGateway.
4.2. Test für den Indikator
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<scope>test</scope>
</dependency>
class PaymentGatewayReadinessIndicatorTest {
@Test
void shouldReportDownWhenGatewayNotConnected() {
PaymentGatewayClient client = mock(PaymentGatewayClient.class);
when(client.isConnected()).thenReturn(false);
PaymentGatewayReadinessIndicator indicator = new PaymentGatewayReadinessIndicator(client);
Health health = indicator.health();
assertThat(health.getStatus()).isEqualTo(Status.DOWN);
assertThat(health.getDetails()).containsEntry("gateway", "unreachable");
}
}
mock(PaymentGatewayClient.class)simuliert die externe Abhängigkeit, ohne echte Verbindung.assertThat(health.getStatus())bestätigt, dass der StatusDOWNist, wenn das Gateway nicht verbunden ist — das erwartete Verhalten der Readiness.
4.3. Readiness im Deployment deklarieren
readinessProbe:
httpGet:
path: /actuator/health/readiness
port: 8080
initialDelaySeconds: 10
periodSeconds: 5
failureThreshold: 3
successThreshold: 1
successThreshold: 1: Nur eine erfolgreiche Probe reicht, um wieder als bereit zu gelten (bei Liveness brauchst du das normalerweise nicht setzen, da der Default bereits 1 ist).periodSeconds: 5ist kürzer als bei Liveness, da Readiness schnell auf Änderungen in Abhängigkeiten reagieren muss.
5. Startup Probe
Bei Anwendungen mit langsamen Starts (große Caches laden, DB-Migrationen) ist ein festes initialDelaySeconds riskant — du musst eine Zahl raten, die groß genug ist, und verschwendest Zeit bei jedem späteren Neustart.
Startup Probe löst das: Während die Startup-Probe noch nicht erfolgreich war, deaktiviert Kubernetes temporär die Liveness- und Readiness-Probes und verhindert so, dass die App während des legitimen Starts unnötig beendet wird.
startupProbe:
httpGet:
path: /actuator/health/liveness
port: 8080
failureThreshold: 30
periodSeconds: 10
livenessProbe:
httpGet:
path: /actuator/health/liveness
port: 8080
periodSeconds: 10
failureThreshold: 3
startupProbenutztfailureThreshold: 30undperiodSeconds: 10→ erlaubt maximal 300 Sekunden (30 × 10s) für den Start vor Timeout.- Nach dem ersten erfolgreichen
startupProbeschaltet Kubernetes auflivenessProbemit kleinerer Schwelle um (3 × 10s = 30s) — reagiert schnell auf echte Fehler im stabilen Betrieb.
Hinweis: Wenn deine Anwendung schnell startet (unter wenigen Sekunden, wie eine einfache REST API ohne große Cache-Laden), brauchst du
startupProbenicht — es macht das YAML nur länger, ohne echten Nutzen.
6. Alle drei in einem Deployment kombinieren
apiVersion: apps/v1
kind: Deployment
metadata:
name: order-service
spec:
replicas: 3
selector:
matchLabels:
app: order-service
template:
metadata:
labels:
app: order-service
spec:
containers:
- name: order-service
image: nonladev/order-service:1.0
ports:
- containerPort: 8080
startupProbe:
httpGet:
path: /actuator/health/liveness
port: 8080
failureThreshold: 30
periodSeconds: 10
livenessProbe:
httpGet:
path: /actuator/health/liveness
port: 8080
periodSeconds: 10
failureThreshold: 3
readinessProbe:
httpGet:
path: /actuator/health/readiness
port: 8080
periodSeconds: 5
failureThreshold: 3
Ablauf, wenn ein neuer Pod erstellt wird:
- Container startet, Kubernetes beginnt sofort
startupProbezu rufen (keininitialDelaySeconds, Startup verwaltet das selbst). - Währenddessen laufen
livenessProbeundreadinessProbenicht. - Wenn
startupProbezum ersten Mal HTTP 200 antwortet, stoppt Kubernetes die Startup-Probe und startet Liveness und Readiness parallel. - Readiness schlägt fehl → Pod wird aus Service-Endpoints entfernt, kein Traffic, Container läuft aber.
- Liveness schlägt fehl → Container wird beendet, Kubernetes erstellt einen neuen (im selben Pod), Ablauf beginnt neu bei Schritt 1.
Prüfe den realen Probe-Status mit kubectl describe pod:
kubectl describe pod order-service-7d9f8c6b5-xk2pq
Conditions:
Type Status
Initialized True
Ready True
ContainersReady True
PodScheduled True
Events:
Type Reason Age From Message
Warning Unhealthy 2m kubelet Readiness probe failed: HTTP probe failed with statuscode: 503
Die Zeile Readiness probe failed zeigt, dass Readiness derzeit fehlschlägt — prüfe das Anwendungs-Log oder rufe /actuator/health/readiness direkt auf, um zu sehen, welche Details DOWN sind.
7. Häufige Fehler
- Denselben Endpoint für Liveness und Readiness verwenden: Wenn externe Abhängigkeiten (DB, Cache) ausfallen, schlagen beide Probes fehl → Pod wird ständig neu gestartet (Crash Loop), obwohl die Anwendung selbst noch läuft. Nutze
/actuator/health/livenessnur für interne Checks und/actuator/health/readinessfür Abhängigkeiten. initialDelaySecondszu klein für langsamen App-Start: Liveness schlägt fehl, während die App noch hochfährt, verursacht endlose Restart-Loops. Lösung: NutzestartupProbestatt willkürlicheinitialDelaySeconds.periodSeconds×failureThresholdzu groß: Reale Fehler werden erst nach Minuten erkannt, beeinflusst SLA.- Readiness-Probe ruft einen unwichtigen Downstream-Service auf: Z.B. einen Logging-Service — fällt dieser aus, wird
OrderServicefälschlicherweise als not-ready markiert, obwohl die Kernfunktion noch funktioniert.
8. Fazit
Die drei Probe-Typen erfüllen drei unterschiedliche Aufgaben: startupProbe schützt die Anwendung in der langsamen Startphase, livenessProbe erkennt Deadlocks, damit Kubernetes neu startet, und readinessProbe steuert Traffic basierend auf echtem Dependency-Status. Das wichtigste Prinzip: Lass die Liveness-Probe nicht von externen Systemen abhängen — das verwandelt einen Fehler eines Downstream-Service in einen Crash Loop deines eigenen Service. Mit den drei korrekt konfigurierten Probes heilt dein Cluster sich selbst eleganter, und die Downtime bei Deployments oder Infrastruktur-Ausfällen sinkt deutlich.