kubelet und kube-proxy
Agent auf Worker Nodes: Pod-Lifecycle und Netzwerk-Regeln.
Lernziele
Nach dieser Lektion kannst du erklären, wie das kubelet eine PodSpec über das Container Runtime Interface (CRI) in laufende Container übersetzt und welche Rolle Liveness-, Readiness- und Startup-Probes dabei spielen. Du kennst die kube-proxy-Modi iptables, ipvs und nftables samt ihrer Unterschiede und kannst einordnen, wo Node-Probleme entstehen, die keine der beiden Komponenten von selbst meldet.
Das Problem: Die Entscheidung ist gefallen — wer setzt sie um?
Nach Lektion 3 steht im Pod-Objekt ein spec.nodeName. Aber ein Eintrag in etcd startet keinen Prozess. Auf dem Node muss jemand das Image ziehen, Namespaces und cgroups einrichten, den Container starten, ihn überwachen und den Status zurückmelden. Und parallel dazu gibt es ein zweites Umsetzungsproblem: Services haben virtuelle IPs, die auf keinem Interface existieren — irgendjemand muss dafür sorgen, dass Pakete an eine Service-IP trotzdem bei einem echten Pod ankommen.
Beide Jobs erledigen Agents, die auf jedem Node laufen: das kubelet für den Pod-Lifecycle, kube-proxy für die Service-Regeln. Sie sind die Hände des Clusters — und die häufigste Fehlerquelle, wenn „der Cluster” eigentlich gesund ist, aber ein einzelner Node nicht.
kubelet: von der PodSpec zum laufenden Container
Das kubelet watched die API nach Pods mit seinem Node-Namen. Für jeden zugewiesenen Pod arbeitet es eine klare Sequenz ab:
- Sandbox erstellen: Zuerst entsteht die Pod-Sandbox — der gemeinsame Netzwerk-Namespace, in dem alle Container des Pods dieselbe IP teilen. Das CNI-Plugin (eigene Lektion später) vergibt dabei die Pod-IP.
- Images ziehen: gemäß
imagePullPolicy, mit Registry-Credentials aus imagePullSecrets. - Init-Container nacheinander ausführen (Lektion 6), dann die App-Container starten.
- Überwachen und melden: Probes ausführen, Restarts gemäß Restart Policy, Status in
pod.statuszurückschreiben — daher weißkubectl get pods, was läuft.
Entscheidend ist, wie das kubelet mit Containern spricht: über das CRI (Container Runtime Interface), eine gRPC-Schnittstelle. Das kubelet kennt kein Docker und kein containerd im Speziellen — es ruft standardisierte Calls wie RunPodSandbox und CreateContainer auf, und die Runtime (heute meist containerd, alternativ CRI-O) setzt sie um. Diese Abstraktion ist die Antwort auf die beliebte Interview-Frage zum „Docker-Aus” in Kubernetes: Entfernt wurde nur der Sonderweg Dockershim, weil Docker kein CRI sprach — Docker-Images funktionieren unverändert, denn sie folgen dem OCI-Standard.
Probes sind der unterschätzte Teil des kubelet-Jobs, weil sie direkt über Verfügbarkeit entscheiden:
- Liveness Probe: „Lebt der Prozess noch sinnvoll?” Schlägt sie wiederholt fehl, killt das kubelet den Container und startet ihn neu.
- Readiness Probe: „Kann er gerade Traffic verarbeiten?” Schlägt sie fehl, wird der Pod aus den Service-Endpoints genommen — kein Restart, nur Traffic-Entzug.
- Startup Probe: schützt langsam startende Anwendungen, indem Liveness/Readiness erst nach erfolgreichem Start greifen.
Die Verwechslung von Liveness und Readiness ist eine der teuersten Fehlkonfigurationen überhaupt: Eine Liveness Probe auf einen Endpoint, der bei Überlast langsam antwortet, produziert Restart-Schleifen genau dann, wenn die App sie am wenigsten braucht. Faustregel: Liveness prüft nur „Prozess deadlocked?”, Readiness prüft „kann ich arbeiten?“.
kube-proxy: Service-IPs sind eine Illusion — eine gut gepflegte
Eine ClusterIP wie 10.96.0.10 existiert auf keinem Netzwerk-Interface. Damit Pakete dorthin trotzdem ankommen, programmiert kube-proxy auf jedem Node Regeln ins Linux-Netzwerk-Stack: „Ziel 10.96.0.10:80 → ersetze durch eine der Pod-IPs aus den Endpoints (DNAT)”. kube-proxy watched Services und EndpointSlices und hält diese Regeln aktuell. Es ist also kein Proxy im Datenpfad — die Pakete fließen durch den Kernel, kube-proxy schreibt nur die Regeln.
Die drei Modi, nach denen gefragt wird:
- iptables (lange der Default): generiert Ketten von NAT-Regeln. Robust und überall verfügbar; bei sehr vielen Services wird das Regelwerk groß, Updates teurer, und die Backend-Auswahl ist zufällig statt lastbasiert.
- ipvs: nutzt den Kernel-Load-Balancer mit Hash-Tabellen — bessere Skalierung bei tausenden Services und echte LB-Algorithmen (round-robin, least connections).
- nftables: der modernere Nachfolger des iptables-Backends, der dessen Performance-Probleme bei großen Regelmengen adressiert.
Die ehrliche Einordnung fürs Interview: Für kleine und mittlere Cluster ist der Modus praktisch egal. Relevanter ist der Trend, kube-proxy ganz zu ersetzen — CNIs wie Cilium übernehmen die Service-Implementierung mit eBPF und machen kube-proxy optional.
Wenn der Node selbst krank ist
kubelet und kube-proxy melden viel — aber nicht alles. Ein kaputtes Kernel-Modul, ein read-only gemountetes Dateisystem, defekte Speicherriegel oder ein hängender containerd: Solche Probleme tauchen in den Standard-Node-Conditions nur grob oder gar nicht auf. Dafür gibt es den Node Problem Detector — ein DaemonSet, das Kernel-Logs und Systemzustand überwacht und Probleme als Node Conditions oder Events sichtbar macht, auf die dann Automatisierung (oder ein Mensch) reagieren kann.
Was das kubelet von sich aus meldet, sind die Conditions Ready, MemoryPressure, DiskPressure und PIDPressure. Bei Druck reagiert es selbst mit Node-Pressure Eviction: Es beendet Pods nach Priorität und QoS-Klasse (der Bogen zu Lektion 10), um den Node zu retten.
Praxis: beide Agents bei der Arbeit beobachten
kind create cluster --name nodelab
# 1. Probes in Aktion: ein Pod, dessen Liveness nach 30s dauerhaft fehlschlägt
kubectl apply -f - <<EOF
apiVersion: v1
kind: Pod
metadata:
name: probedemo
spec:
containers:
- name: app
image: busybox:1.36
args: ["sh", "-c", "touch /tmp/healthy; sleep 30; rm /tmp/healthy; sleep 600"]
livenessProbe:
exec:
command: ["cat", "/tmp/healthy"]
initialDelaySeconds: 5
periodSeconds: 5
EOF
# Nach ~45s: RESTARTS zählt hoch, Events zeigen "Liveness probe failed"
sleep 50 && kubectl get pod probedemo
kubectl describe pod probedemo | tail -8
# 2. kube-proxy: Modus und Regeln ansehen
kubectl get cm kube-proxy -n kube-system -o jsonpath='{.data.config\.conf}' | grep mode
kubectl create deployment web --image=nginx && kubectl expose deployment web --port=80
docker exec nodelab-control-plane iptables -t nat -L KUBE-SERVICES | grep web
# 3. Node-Sicht des kubelet
kubectl describe node nodelab-control-plane | grep -A6 "Conditions:"
Du siehst in Schritt 2 die DNAT-Kette, die kube-proxy für deinen Service angelegt hat — die „Illusion” der ClusterIP als konkrete iptables-Regel. Aufräumen: kind delete cluster --name nodelab.
Typische Stolperfallen
Liveness Probe als Dependency-Check: Prüft die Liveness Probe die Datenbank-Verbindung, restartet das kubelet bei einem DB-Ausfall die komplette App-Flotte — und macht aus einem Incident zwei. Abhängigkeiten gehören höchstens in die Readiness.
Fehlende Readiness Probe bei Rolling Updates: Ohne Readiness gilt ein Container ab Start als bereit — der Service schickt Traffic an Pods, deren App noch bootet. Die 502er beim Deployment haben fast immer diese Ursache.
kube-proxy für einen Daten-Proxy halten: kube-proxy sitzt nicht im Traffic-Pfad. Wenn Service-Traffic hakt, liegt es an den programmierten Regeln, an EndpointSlices ohne ready Pods oder am CNI — nicht an einem „überlasteten Proxy-Prozess”.
Node NotReady reflexhaft dem Cluster zuschreiben: NotReady heißt zunächst nur: Das kubelet meldet sich nicht (sauber). Die Diagnose läuft auf dem Node: systemctl status kubelet, journalctl -u kubelet, Zustand von containerd, Disk voll? — bevor man am Cluster schraubt.
Interview-Vorbereitung
Die Standardfrage „Was machen kubelet und kube-proxy?” beantwortest du über die Arbeitsteilung: Das kubelet ist der Pod-Lifecycle-Agent — watched seine Pods, erstellt die Sandbox, spricht über CRI mit containerd, führt Probes aus und meldet Status zurück. kube-proxy ist der Service-Regel-Programmierer — übersetzt Services und EndpointSlices in iptables/ipvs/nftables-Regeln, steht aber selbst nicht im Datenpfad. Wer dann noch die Probe-Semantik sauber trennt (Liveness = Restart, Readiness = Traffic-Entzug), hebt sich ab.
Typische Follow-ups:
- „Warum wurde Docker-Support entfernt — laufen Docker-Images noch?” — Nur Dockershim entfiel, weil Docker kein CRI spricht; OCI-Images laufen unverändert auf containerd.
- „Unterschied Liveness vs. Readiness?” — Liveness: Neustart bei Deadlock. Readiness: temporär aus den Endpoints. Falsche Liveness erzeugt Restart-Stürme.
- „iptables vs. ipvs?” — iptables: Regelketten, fein bei normaler Größe; ipvs: Hash-basiert, skaliert bei sehr vielen Services und bietet echte LB-Algorithmen.
- „Node ist NotReady — erste Schritte?” — Auf dem Node: kubelet-Status und -Logs, Runtime, Disk; im Cluster:
kubectl describe nodefür Conditions und Events.
Zusammenfassung
Das kubelet ist der ausführende Agent auf jedem Node: PodSpec entgegennehmen, Sandbox bauen, über CRI containerd dirigieren, mit Liveness-, Readiness- und Startup-Probes überwachen und Status melden — inklusive Selbstschutz durch Node-Pressure Eviction. kube-proxy macht Service-IPs real, indem es sie als iptables-, ipvs- oder nftables-Regeln in den Kernel schreibt, ohne selbst Traffic anzufassen. Für alles, was beide nicht sehen, ergänzt der Node Problem Detector die Diagnose.
Damit kennst du alle Einzelkomponenten aus Lektion 1 im Detail. Die nächste Lektion setzt sie unter Ausfall-Bedingungen zusammen: Wie baut man eine Control Plane ohne Single Point of Failure — und was davon nimmt dir Managed Kubernetes ab?