Einführung
Running bedeutet lediglich, dass ein Containerprozess existiert. Es beweist weder, dass die Anwendung bereit ist, Datenverkehr entgegenzunehmen, noch dass sie sich selbstständig von Fehlern erholen kann. Kubernetes-Probes übertragen anwendungsspezifische Zustandsprüfungen in das Verhalten von Controllern und Services.
Du wirst alle drei Probe-Typen hinzufügen, beobachten, wie eine Readiness-Prüfung einen Pod aus den Service-Endpunkten entfernt, ihn anschließend reparieren und außerdem einen Hook für das kontrollierte Herunterfahren einrichten.
Halte die Zuständigkeiten klar getrennt: Eine Startup-Probe schützt vor Problemen durch langsame Initialisierung, eine Readiness-Probe steuert, ob ein Pod Service-Datenverkehr empfängt, und eine Liveness-Probe entscheidet, wann der Kubelet einen festgefahrenen Container neu starten soll. Wird dieselbe Probe für alle Zwecke verwendet, führt das häufig zu vermeidbaren Neustarts oder dazu, dass Datenverkehr zu früh weitergeleitet wird.
Eine Anwendung mit Probes bereitstellen
In diesem Schritt stellst du NGINX mit Startup-, Readiness- und Liveness-Prüfungen gegen / am benannten Port http bereit.
Das Manifest ist länger, weil sich alle drei Probes innerhalb der Containerspezifikation befinden. Lies es vor dem Anwenden in dieser Reihenfolge: Deployment und Labels, Container und benannter Port, anschließend Startup-, Readiness- und Liveness-Probe. In dieser Übung verwenden alle drei Prüfungen denselben HTTP-Endpunkt, ihre Auswirkungen bei einem Fehler unterscheiden sich jedoch.
Einige einzeilige Zuordnungen und Listen verwenden den kompakten YAML-„Flow-Style“, zum Beispiel {app: healthy-web}. Geschweifte Klammern stehen für eine Zuordnung, eckige Klammern für eine Liste. Inhaltlich entsprechen sie den mehrzeiligen YAML-Formen. Verwende diese Kurzschreibweise hier nur, wenn der gesamte Wert gut lesbar bleibt.
Der Schwellenwert der Startup-Probe erlaubt bis zu 24 Sekunden (12 × 2), bevor Kubernetes den Start als fehlgeschlagen betrachtet. Nach erfolgreichem Start wird die Readiness-Probe alle drei Sekunden und die Liveness-Probe alle fünf Sekunden ausgeführt.
cd /home/labex/project/application-health
cat <<'EOF' > healthy-web.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: healthy-web
namespace: practice-health
spec:
replicas: 2
selector:
matchLabels: {app: healthy-web}
template:
metadata:
labels: {app: healthy-web}
spec:
containers:
- name: web
image: nginx:1.27-alpine
imagePullPolicy: IfNotPresent
ports:
- {name: http, containerPort: 80, protocol: TCP}
startupProbe:
httpGet: {path: /, port: http}
failureThreshold: 12
periodSeconds: 2
readinessProbe:
httpGet: {path: /, port: http}
periodSeconds: 3
livenessProbe:
httpGet: {path: /, port: http}
periodSeconds: 5
failureThreshold: 3
---
apiVersion: v1
kind: Service
metadata:
name: healthy-web
namespace: practice-health
spec:
selector: {app: healthy-web}
ports:
- {name: http, port: 80, targetPort: http, protocol: TCP}
EOF
Erstelle den Namespace und wende das Manifest an. rollout status wartet darauf, dass beide Replikate die Readiness-Anforderungen erfüllen, nicht nur darauf, dass ihre Prozesse gestartet wurden:
kubectl create namespace practice-health
kubectl apply -f healthy-web.yaml
kubectl rollout status deployment/healthy-web -n practice-health --timeout=60s
Die Startup-Probe schützt vor langsamer Initialisierung, die Readiness-Probe steuert die Berechtigung für Datenverkehr, und die Liveness-Probe ermöglicht einen Neustart nach einem nicht behebbaren Fehler.
Wenn das Rollout abgeschlossen wird, weißt du bereits mehr als nur, dass „die Container laufen“: Beide Pods haben die Readiness-Anfrage erfolgreich beantwortet.
Die abschließende Rollout-Zeile ist der erste Nachweis für einen gesunden Zustand:
Waiting for deployment "healthy-web" rollout to finish: 0 of 2 updated replicas are available...
Waiting for deployment "healthy-web" rollout to finish: 1 of 2 updated replicas are available...
deployment "healthy-web" successfully rolled out
Die Zwischenmeldungen sind normal. Kubernetes meldet den Fortschritt, sobald jeder Pod Startup- und Readiness-Prüfung besteht. Erst die letzte Zeile bedeutet, dass beide Replikate verfügbar sind.
Readiness mit Service-Endpunkten verknüpfen
In diesem Schritt untersuchst du die Readiness-Bedingung und die EndpointSlices.
Beginne mit der vertrauten Pod-Tabelle und beschränke die Ausgabe anschließend auf die Readiness-Bedingung und die Anzahl der Neustarts. Die benutzerdefinierte Tabelle macht es leicht, zwischen „nicht bereit“ und „wird neu gestartet“ zu unterscheiden:
kubectl get pods -n practice-health -l app=healthy-web
kubectl get pods -n practice-health -l app=healthy-web -o custom-columns='POD:.metadata.name,READY_CONDITION:.status.conditions[?(@.type=="Ready")].status,RESTARTS:.status.containerStatuses[0].restartCount'
kubectl get endpointslices -n practice-health -l kubernetes.io/service-name=healthy-web -o jsonpath='{range .items[].endpoints[*]}{.addresses[0]} ready={.conditions.ready}{"\n"}{end}'
Beide Adressen sollten ready=true anzeigen. Wenn die Readiness-Prüfung fehlschlägt, läuft der Container weiter, seine Adresse wird jedoch aus dem normalen Service-Routing entfernt.
EndpointSlices bilden die Verbindung zwischen einem Service-Selector und den tatsächlichen Pod-Adressen. Der Service selbst bleibt bestehen, wenn ein Pod nicht bereit ist. Seine Endpunktbedingung verhindert, dass normaler Datenverkehr an diesen Pod gesendet wird.
Ein typischer Nachweis für einen gesunden Zustand sieht so aus:
POD READY_CONDITION RESTARTS
healthy-web-... True 0
healthy-web-... True 0
10.244.104.11 ready=true
10.244.104.12 ready=true
Pod-Namen und IP-Adressen unterscheiden sich in deiner Umgebung. Entscheidend ist die Beziehung: Zwei Pods mit Ready=True entsprechen zwei Endpunkten mit ready=true, und keiner der Container wurde neu gestartet.
Einen Readiness-Fehler beobachten
In diesem Schritt änderst du absichtlich nur den Readiness-Pfad in eine nicht vorhandene URL.
Dies ist ein kontrollierter Fehler. JSON Patch ersetzt ein einzelnes verschachteltes Feld und lässt die Startup- und Liveness-Probes daher unverändert. NGINX gibt für /not-ready den HTTP-Status 404 zurück, was die Readiness-Prüfung als Fehler wertet.
kubectl patch deployment healthy-web -n practice-health --type='json' -p='[{"op":"replace","path":"/spec/template/spec/containers/0/readinessProbe/httpGet/path","value":"/not-ready"}]'
kubectl rollout status deployment/healthy-web -n practice-health --timeout=20s || true
kubectl get pods -n practice-health -l app=healthy-web
kubectl get endpointslices -n practice-health -l kubernetes.io/service-name=healthy-web -o jsonpath='{range .items[].endpoints[*]}{.addresses[0]} ready={.conditions.ready}{"\n"}{end}'
|| true kennzeichnet das erwartete Rollout-Timeout als erfolgreich verarbeitet, damit die Shell fortfährt. Die neuen Container laufen, aber die Readiness-Prüfung schlägt fehl, sodass das Deployment nicht vollständig verfügbar werden kann.
Das getestete Rollout führte zu einem Zustand wie diesem:
error: timed out waiting for the condition
NAME READY STATUS RESTARTS
healthy-web-old-... 1/1 Running 0
healthy-web-old-... 1/1 Running 0
healthy-web-new-... 0/1 Running 0
...
10.244.104.17 ready=false
Der neue Pod befindet sich im Status Running, ist aber nicht bereit und wurde nicht neu gestartet. Die beiden alten, bereiten Pods bleiben bestehen, weil fortlaufende Deployments die Verfügbarkeit schützen: Kubernetes ersetzt keine weiteren gesunden Replikate, solange die neue Version nicht bereit werden kann. Das Rollout-Timeout belegt daher sowohl das Verhalten der Readiness-Prüfung als auch den Sicherheitsmechanismus des Controllers. In der EndpointSlice-Ausgabe zeigt die neue Adresse ready=false, sodass das normale Service-Routing weiterhin ausschließlich die gesunden Endpunkte verwendet.
Readiness reparieren und Datenverkehr überprüfen
In diesem Schritt stellst du den Readiness-Pfad wieder her und wartest darauf, dass die Service-Endpunkte zurückkehren.
Verwende die umgekehrte Variante des vorherigen Patches, damit das Experiment nur eine Variable verändert. Das Warten auf den Rollout-Status ist nun erfolgreich, weil die Ersatz-Pods / mit HTTP 200 beantworten.
kubectl run health-client erstellt einen temporären, eigenständigen Pod für die Anfrage. --image wählt BusyBox aus, --image-pull-policy=IfNotPresent verwendet das bereits zwischengespeicherte Image wieder, und --restart=Never fordert einen einzelnen Pod statt eines Controllers an. --rm -i hält den Befehl verbunden und löscht den Pod nach dem Beenden. Das zweite -- trennt die kubectl-Optionen vom wget-Befehl, der innerhalb des Containers ausgeführt wird.
Bei wget -qO- unterdrückt -q Fortschrittsmeldungen, während -O- den Antwortinhalt auf die Standardausgabe schreibt. Die Pipe übergibt diesen Inhalt an head, das nur die ersten Zeilen anzeigt. Die URL verwendet den Service-Namen healthy-web, den der Cluster-DNS innerhalb des Namespace auflöst.
kubectl patch deployment healthy-web -n practice-health --type='json' -p='[{"op":"replace","path":"/spec/template/spec/containers/0/readinessProbe/httpGet/path","value":"/"}]'
kubectl rollout status deployment/healthy-web -n practice-health --timeout=60s
kubectl run health-client -n practice-health --image=busybox:1.36 --image-pull-policy=IfNotPresent --restart=Never --rm -i -- wget -qO- http://healthy-web | head
Der temporäre Client verwendet den Cluster-DNS. Durch die Begrenzung der angezeigten HTML-Ausgabe bleibt der Nachweis übersichtlich. Eine erfolgreiche Antwort beweist zugleich, dass Readiness und Service-Routing übereinstimmen.
Die Antwort beginnt mit erkennbarem NGINX-HTML:
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
HTML nach einem erfolgreichen Rollout zu sehen, belegt den vollständigen Pfad: Der DNS hat den Service aufgelöst, der Service einen bereiten Endpunkt ausgewählt, und NGINX hat die Anfrage verarbeitet.
Lösche den temporären Pod anschließend, damit keine Diagnose-Clients im Namespace zurückbleiben.
Kontrollierte Beendigung hinzufügen
In diesem Schritt fügst du einen preStop-Hook und eine großzügige Beendigungsfrist hinzu. Der Hook verzögert das Herunterfahren kurz, damit sich die Entfernung des Endpunkts verbreiten kann, bevor der Prozess beendet wird.
Lies die beiden JSON-Patch-Operationen getrennt. Die erste fügt ein Zeitfenster von 20 Sekunden für die Beendigung des Pods hinzu. Die zweite ergänzt für Container-Index 0 einen fünf Sekunden langen Sleep-Hook. Kubernetes startet den Hook, sobald die Beendigung beginnt, während die verbleibende Frist weiter heruntergezählt wird.
kubectl patch deployment healthy-web -n practice-health --type=json -p='[
{"op":"add","path":"/spec/template/spec/terminationGracePeriodSeconds","value":20},
{"op":"add","path":"/spec/template/spec/containers/0/lifecycle","value":{"preStop":{"sleep":{"seconds":5}}}}
]'
kubectl rollout status deployment/healthy-web -n practice-health --timeout=60s
kubectl get deployment healthy-web -n practice-health -o jsonpath='grace={.spec.template.spec.terminationGracePeriodSeconds}s preStop={.spec.template.spec.containers[0].lifecycle.preStop.sleep.seconds}s{"\n"}'
Der JSON-Patch ändert nur die beiden genannten Felder, sodass die Probes und alle anderen Containereinstellungen erhalten bleiben. Eine kontrollierte Beendigung ergänzt die Readiness-Prüfung: Die eine steuert die Zulassung zum Datenverkehr, die andere gibt Kubernetes Zeit, den Pod vor dem vollständigen Herunterfahren aus dem Datenverkehr zu entfernen.
Die abschließende JSONPath-Ausgabe sollte grace=20s preStop=5s lauten. Der fünf Sekunden lange Hook liegt innerhalb des 20-Sekunden-Zeitfensters, sodass NGINX genügend Zeit für ein normales Beenden bleibt.
Das Rollout kann bei Meldungen wie 1 old replicas are pending termination kurz pausieren, weil der neue preStop-Hook das Herunterfahren absichtlich um fünf Sekunden verlängert. Die abschließende Ausgabe bleibt:
deployment "healthy-web" successfully rolled out
grace=20s preStop=5s
Zusammenfassung
Du hast Startup-, Readiness- und Liveness-Prüfungen mit dem Verhalten von Kubernetes verknüpft, beobachtet, wie Readiness-Endpunkte entfernt, ohne Container anzuhalten, den Datenverkehr wiederhergestellt und eine kontrollierte Beendigung hinzugefügt. Dadurch wird der Zustand einer Anwendung nicht nur sichtbar, sondern unmittelbar nutzbar.


