Kubernetes health probes configureren: liveness, readiness en startup

Kubernetes health probes vertellen de kubelet wanneer een container herstart moet worden, wanneer verkeer gestopt moet worden, en wanneer gewacht moet worden op een trage opstart. Verkeerd geconfigureerde probes zijn een van de meest voorkomende oorzaken van CrashLoopBackOff en doorrollende outages. Dit artikel doorloopt alle drie probetypen, de vier probemechanismen, timingparameters en de configuratiepatronen die workloads stabiel houden in productie.

Wat elk probetype doet en wanneer het afgaat

Drie probes, drie verschillende gevolgen bij falen. Dit door elkaar halen is de hoofdoorzaak van de meeste probe-gerelateerde incidenten.

Liveness probe. Draait continu op periodSeconds-interval. Als de probe failureThreshold keer achter elkaar faalt, herstart de kubelet de container (niet de pod; het pod-object blijft bestaan). Gebruik dit om te herstellen van deadlocks of vastgelopen processen die nooit vanzelf herstellen.

Readiness probe. Draait ook continu, niet alleen bij opstarten. Bij falen verwijdert de kubelet de pod uit alle bijbehorende Service-endpoints. De container blijft draaien. Verkeer stopt met binnenkomen. Zodra de probe weer slaagt, wordt de pod opnieuw toegevoegd. Gebruik dit om aan te geven dat een container tijdelijk geen requests kan verwerken.

Startup probe. Draait eenmalig bij het starten van de container. Blokkeert liveness- en readiness-probes totdat hij slaagt. Als hij failureThreshold keer achter elkaar faalt, herstart de kubelet de container. Zodra hij slaagt, draait hij nooit meer. Gebruik dit voor containers met trage of wisselende opstarttijden.

Probe Actie bij falen Wanneer Blokkeert andere probes?
Startup Container herstart (na threshold) Eenmalig bij opstarten Ja
Liveness Container herstart (na threshold) Continu Nee
Readiness Pod verwijderd uit endpoints Continu Nee

Een veelvoorkomend misverstand: readiness-probes zijn geen opstart-mechanisme. Ze draaien gedurende de volledige levensduur van de pod. Een pod die vijf minuten geleden ready was, kan op elk moment not-ready worden als de probe begint te falen.

De vier probemechanismen

Elk probetype ondersteunt vier mechanismen. Kies het mechanisme dat past bij de interface van je service.

HTTP-probe (httpGet)

De kubelet stuurt een HTTP GET-request. Statuscodes 200 tot en met 399 tellen als succes. Alles anders is een fout.

livenessProbe:
  httpGet:
    path: /healthz
    port: 8080
    httpHeaders:            # optioneel; kubelet stuurt standaard User-Agent: kube-probe
      - name: X-Custom
        value: probe
  periodSeconds: 10
  failureThreshold: 3

Meest geschikt voor: elke service die HTTP aanbiedt. Veruit de meest gebruikte keuze.

TCP-probe (tcpSocket)

De kubelet probeert een TCP-connectie te openen. Succes betekent dat de verbinding tot stand kwam. Het verifieert niet of de applicatie daadwerkelijk requests verwerkt.

readinessProbe:
  tcpSocket:
    port: 5432
  periodSeconds: 10
  failureThreshold: 3

Meest geschikt voor: databases, message brokers en andere TCP-services die geen HTTP aanbieden.

Exec-probe (exec)

De kubelet voert een commando uit in de container. Exit code 0 is succes; alles anders is een fout.

livenessProbe:
  exec:
    command:
      - cat
      - /tmp/healthy
  periodSeconds: 10
  failureThreshold: 3

Bekend probleem: exec-probes starten een child-proces per uitvoering. Als PID 1 in je container geen init-systeem is, worden die children zombieprocessen. Bij lage periodSeconds met veel pods raakt de PID-ruimte op de node uitgeput. Gebruik tini of dumb-init als PID 1 als je exec-probes gebruikt.

Meest geschikt voor: custom health-logica die niet als HTTP of TCP uit te drukken is. Geef de voorkeur aan HTTP of TCP waar mogelijk.

gRPC-probe (grpc)

De kubelet roept het gRPC Health Checking Protocol aan (grpc.health.v1.Health/Check). GA sinds Kubernetes 1.27; geen feature gate nodig.

readinessProbe:
  grpc:
    port: 50051          # moet numeriek zijn; named ports worden niet ondersteund
  periodSeconds: 10
  failureThreshold: 3

Beperkingen: geen client-certificaatondersteuning, geen TLS-certificaatvalidatie, geen service name chaining.

Meest geschikt voor: gRPC-services die het standaard health checking protocol implementeren.

Timingparameters

Zes parameters bepalen hoe snel probes afgaan, hoe lang ze wachten en hoeveel fouten actie triggeren.

Parameter Default Wat het bepaalt
initialDelaySeconds 0 Seconden voor de eerste probe afgaat
periodSeconds 10 Seconden tussen probe-uitvoeringen
timeoutSeconds 1 Seconden dat de kubelet wacht op antwoord
successThreshold 1 Opeenvolgende successen om healthy te markeren
failureThreshold 3 Opeenvolgende fouten voor actie
terminationGracePeriodSeconds Erft van pod-level Override voor probe-getriggerde restarts (1.25+)

Defaultwaarden staan in de officiele Kubernetes probe-configuratiereferentie.

Twee beperkingen om te weten:

  • successThreshold voor liveness- en startup-probes moet 1 zijn. De API weigert andere waarden.
  • terminationGracePeriodSeconds op probe-level (beschikbaar sinds Kubernetes 1.25) overschrijft de pod-level-waarde. Handig als deadlock-recovery snel moet herstarten, maar een normale shutdown een lang drain-venster nodig heeft.

Startup-probes configureren voor traag opstartende applicaties

Voor de startup-probe bestond, was een grote initialDelaySeconds op de liveness-probe de enige optie voor traag startende containers. Dat creert een vaste wachttijd, ook bij snelle opstart, en past zich niet aan bij wisselende opstarttijden.

De startup-probe lost dit op. Hij blokkeert liveness en readiness totdat de applicatie expliciet aangeeft klaar te zijn. De formule:

failureThreshold x periodSeconds >= worst-case opstarttijd

Een Java-applicatie die tot 3 minuten nodig heeft om te starten:

startupProbe:
  httpGet:
    path: /healthz
    port: 8080
  failureThreshold: 18     # 18 x 10 = 180 seconden = 3 minuten
  periodSeconds: 10
livenessProbe:
  httpGet:
    path: /healthz
    port: 8080
  periodSeconds: 10
  failureThreshold: 3

Zodra de startup-probe slaagt, draait hij nooit meer. Vanaf dat punt neemt de liveness-probe het over.

Ontwerp je health-endpoints

De belangrijkste regel: controleer nooit externe dependencies in een liveness probe. Als je database uitvalt en elke pod's liveness-probe de databaseverbinding checkt, herstarten alle pods tegelijk. De herstartstorm verergert de outage in plaats van ervan te herstellen.

Aanbevolen endpointpatroon

Scheid je liveness- en readiness-endpoints:

/healthz (liveness): retourneert 200 als de HTTP-loop leeft. Checkt niets extern. Stel het je voor als "is dit proces vastgelopen?" Als het antwoord nee is, retourneer 200.

// Liveness: bewijs dat het proces kan reageren
http.HandleFunc("/healthz", func(w http.ResponseWriter, r *http.Request) {
    w.WriteHeader(http.StatusOK)
})

/ready (readiness): retourneert 200 als de applicatie klaar is met initialiseren en kritieke dependencies bereikbaar zijn. Retourneert 503 tijdens opstart, cache-opwarming of dependency-onbereikbaarheid.

// Readiness: verifieer dat de app echte requests kan bedienen
http.HandleFunc("/ready", func(w http.ResponseWriter, r *http.Request) {
    if !appReady || !dbPool.Ping() {
        w.WriteHeader(http.StatusServiceUnavailable)
        return
    }
    w.WriteHeader(http.StatusOK)
})

Wees ook bij readiness voorzichtig met dependency-checks. Zet timeoutSeconds boven de P99-responstijd van de dependency en verhoog failureThreshold boven de standaard 3. Een latencyspike op een gedeelde database mag niet tegelijkertijd elke pod uit elke Service verwijderen.

Veelvoorkomende configuratiefouten

Liveness-probe killt traag opstartende containers

Symptoom: pod gaat direct na deployment in CrashLoopBackOff. kubectl describe pod toont Liveness probe failed-events voordat de applicatie klaar is met opstarten.

Fix: voeg een startup-probe toe met failureThreshold x periodSeconds die de worst-case opstarttijd dekt. Verwijder eventuele grote initialDelaySeconds van de liveness-probe.

Identieke liveness- en readiness-configuratie

Symptoom: onder belasting worden pods tegelijkertijd uit endpoints verwijderd (readiness) en herstart (liveness). Actieve verbindingen vallen weg zonder graceful shutdown.

Fix: geef liveness een hogere failureThreshold dan readiness. Readiness moet de eerste verdedigingslinie zijn (stop verkeer), liveness het laatste redmiddel (herstart).

readinessProbe:
  httpGet:
    path: /ready
    port: 8080
  periodSeconds: 5
  failureThreshold: 3      # verwijderd uit verkeer na 15 seconden
livenessProbe:
  httpGet:
    path: /healthz
    port: 8080
  periodSeconds: 10
  failureThreshold: 6      # herstart na 60 seconden, niet 15

Externe dependency in liveness-probe

Symptoom: een tijdelijke database-outage laat alle pods tegelijk herstarten. Recovery duurt minuten in plaats van seconden.

Fix: verplaats de dependency-check naar de readiness-probe. Houd het liveness-endpoint puur intern.

Verkeerd pad of poort

Symptoom: de probe retourneert een 404 of connection refused vanaf de eerste poging. Pod herstart voordat hij ooit verkeer heeft bediend.

Diagnose:

kubectl exec <pod> -- curl -v http://localhost:8080/healthz

Fix: zorg dat het probe-path en de port overeenkomen met wat de applicatie daadwerkelijk bindt. Check je Dockerfile EXPOSE-directive en de listen-configuratie van je applicatie. Let op: Kubernetes negeert de Docker HEALTHCHECK-directive volledig; die vervangt geen probe-configuratie.

Readiness-timeout korter dan dependency-latency

Symptoom: alle pods worden not-ready tijdens belastingspieken, ook al functioneert de applicatie zelf. Het readiness-endpoint bevraagt een dependency die in 1,2 seconde antwoordt, maar timeoutSeconds is 1 (de standaard).

Fix: zet timeoutSeconds ruim boven de P99-responstijd van wat het readiness-endpoint controleert.

Probes en rolling updates

Zonder readiness-probes markeert Kubernetes een pod als Ready op het moment dat de container start. Tijdens een rolling update betekent dat: de nieuwe pod ontvangt verkeer voordat de applicatie klaar is met initialiseren.

Met readiness-probes wacht de rollout-controller tot elke nieuwe pod zijn readiness-probe haalt voordat er verkeer naartoe gerouteerd wordt. De oude pod wordt pas getermineerd als de nieuwe Ready is.

Voor zero-downtime rolling updates combineer je readiness-probes met deze Deployment-instellingen:

spec:
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxUnavailable: 0    # verwijder nooit een oude pod tot een nieuwe ready is
      maxSurge: 1           # sta een extra pod toe tijdens de transitie
  minReadySeconds: 30       # wacht 30 seconden na readiness voordat je verdergaat

minReadySeconds voegt een buffer toe na readiness-succes. Als de nieuwe pod binnen dat venster crasht, pauzeert de rollout in plaats van de volgende oude pod af te breken.

Compleet voorbeeld: alle drie de probes

Een productie-deployment voor een webapplicatie met een worst-case opstarttijd van 90 seconden:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: web-app
spec:
  replicas: 3
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxUnavailable: 0
      maxSurge: 1
  minReadySeconds: 15
  selector:
    matchLabels:
      app: web-app
  template:
    metadata:
      labels:
        app: web-app
    spec:
      terminationGracePeriodSeconds: 60
      containers:
        - name: web-app
          image: registry.internal/web-app:4.2.1
          ports:
            - containerPort: 8080
          startupProbe:
            httpGet:
              path: /healthz
              port: 8080
            failureThreshold: 10   # 10 x 10 = 100 seconden max opstart-venster
            periodSeconds: 10
          livenessProbe:
            httpGet:
              path: /healthz
              port: 8080
            periodSeconds: 10
            failureThreshold: 6    # herstart na 60 seconden falen
            timeoutSeconds: 2
            terminationGracePeriodSeconds: 10  # snelle herstart bij deadlock (K8s 1.25+)
          readinessProbe:
            httpGet:
              path: /ready
              port: 8080
            periodSeconds: 5
            failureThreshold: 3    # verwijderd uit verkeer na 15 seconden
            timeoutSeconds: 3      # ruim voor dependency-checks in /ready

Controleer of je probes werken

Bevestig probe-gedrag na deployment:

# Controleer op probe-gerelateerde events
kubectl describe pod <pod-name> | grep -A 5 "Events:"

# Bekijk pod-statustransities
kubectl get pods -w

# Test het endpoint handmatig vanuit de pod
kubectl exec <pod-name> -- curl -s -o /dev/null -w "%{http_code}" http://localhost:8080/healthz

Gezonde output van kubectl describe pod toont geen Unhealthy-events in het events-gedeelte. Als je Liveness probe failed- of Readiness probe failed-events ziet, check dan het pad, de poort en de timingparameters van de probe tegen het daadwerkelijke gedrag van de applicatie.

Wanneer escaleren

Als probes blijven falen nadat je de configuratie hebt geverifieerd, verzamel dan het volgende voordat je escaleert:

  • Output van kubectl describe pod <pod-name> (volledig, niet afgekapt)
  • Applicatielogs: kubectl logs <pod-name> --previous (voor de gecrasht container)
  • Node-resourcegebruik: kubectl top node en kubectl top pod
  • De exacte probe-configuratie uit de Deployment-spec
  • Kubernetes-versie: kubectl version
  • Of de fout consistent of intermitterend is

Terugkerende server- of deploymentproblemen?

Ik help teams productie betrouwbaar maken met CI/CD, Kubernetes en cloud—zodat fixes blijven en deploys geen stress meer zijn.

Bekijk DevOps consultancy

Doorzoek deze site

Begin met typen om te zoeken, of blader door de kennisbank en blog.