Iedereen herhaalt de zin dat Kubernetes zelf-herstellend is. Pods sterven, ze komen terug. Nodes vallen weg, workloads herschedulen. Het systeem reconcilieert zichzelf naar de staat die je hebt gedeclareerd, en de meeste dagen denk je er nooit over na.

Dan is op een dag het ding dat het herstel doet juist het ding dat kapot ging. De API server is down. etcd reageert niet. De scheduler zit vast. En nu?

Dat is de vraag waar het mij echt om gaat, want “zelf-herstellend” is alleen nuttig als ik de randen ervan begrijp. Ik wil weten wat netjes degradeert en wat het hele cluster meesleurt. Dus ik heb mijn clusters bewust door een hoop failures heen geleid: gepland, ongepland, en een paar “hold my beer” experimenten op hardware die ik niet erg vond om kwijt te raken. Hier is wat er daadwerkelijk gebeurt als elk onderdeel breekt, en waarom het meeste minder erg is dan mensen vrezen.

De componenten die kunnen falen

Voor de scenario’s eerst de kaart. Je kunt niet over blast radius nadenken als je niet weet welk onderdeel welke taak bezit.

Control Plane:

  • kube-apiserver: De API waar alles mee praat
  • etcd: De database die alle cluster state opslaat
  • kube-scheduler: Bepaalt waar pods draaien
  • kube-controller-manager: Draait controllers (ReplicaSet, Deployment, etc.)
  • cloud-controller-manager: Cloud provider integraties (indien van toepassing)

Node Componenten:

  • kubelet: Beheert pods op elke node
  • kube-proxy: Handelt netwerkregels af voor Services
  • Container runtime: Draait daadwerkelijk containers

Wat je vooraf wilt internaliseren: de meeste hiervan zijn control-plane breinen, en je draaiende workloads raadplegen het brein niet om verkeer te blijven serveren. Dat ene feit verklaart bijna elk scenario hieronder.

Scenario 1: API server down

De kube-apiserver is de enkele voordeur voor elke Kubernetes API request. Zet hem uit en je zou denken dat het cluster omvalt. Dat doet het niet.

Directe impact:

  • kubectl commando’s falen
  • Geen nieuwe deployments of updates mogelijk
  • Geen nieuwe pod scheduling
  • Bestaande pods blijven draaien

Wat blijft werken:

  • Draaiende pods blijven draaien
  • Containers blijven levend
  • Netwerkconnectiviteit tussen pods
  • Services blijven verkeer routeren

Wat breekt:

  • Geen nieuwe pods kunnen worden gemaakt
  • Gefaalde pods worden niet vervangen
  • Horizontal Pod Autoscaler stopt
  • Geen wijzigingen aan resources
flowchart TD
    subgraph api_down["API Server Down"]
        subgraph control["Control Plane"]
            etcd["etcd ✓"]
            sched["scheduler<br/>idle"]
            ctrl["controller-mgr<br/>idle"]
            apiX["✗ API Server"]
        end
        control -->|"geen updates"| nodes
        subgraph nodes["Worker Nodes"]
            N1["Node 1<br/>Pods ✓"]
            N2["Node 2<br/>Pods ✓"]
            N3["Node 3<br/>Pods ✓"]
        end
    end
    note["Pods blijven draaien - ze hebben de API niet nodig"]

Dit is de hele ontwerpfilosofie in één plaatje. Kubernetes verwacht dat de API server soms onbereikbaar is, dus het degradeert graceful: je workloads blijven serveren, je verliest alleen het vermogen om iets te veranderen. De control plane is de managementlaag, niet de runtime. Die scheiding is wat het systeem overleefbaar maakt.

Voor HA setups wil je meerdere API servers achter een load balancer. Zie Kubernetes High Availability: stacked vs external etcd voor architectuur opties.

Scenario 2: etcd down

etcd is het brein van Kubernetes. Elke bit cluster state leeft hier. Dit is de failure die mij echt angst aanjaagt, en degene die ik religieus backup.

Directe impact:

  • API server kan state niet lezen of schrijven
  • Effectief hetzelfde als API server down
  • Bestaande pods blijven draaien

Wat blijft werken:

  • Draaiende pods blijven draaien
  • Containers blijven levend
  • Netwerkconnectiviteit
  • Services werken

Wat breekt:

  • Zelfde als API server down, plus:
  • Risico op state inconsistentie bij recovery
  • Split-brain scenario’s bij gedeeltelijke failures
flowchart TD
    subgraph etcd_down["etcd Down"]
        subgraph control["Control Plane"]
            etcdX["etcd ✗"] --> apiX["API ✗"]
            apiX --> other["andere componenten"]
        end
    end
    note["Zonder etcd kan API server niet functioneren<br/>Nodes blijven draaien - ze cachen hun toewijzingen"]

Merk op dat de runtime blijft staan. Nodes cachen hun toewijzingen, dus de pods die draaiden blijven draaien ook al is het brein offline. Het gevaar is niet de outage, het is de recovery: breng etcd inconsistent terug of vanuit een stale snapshot en je kunt je beeld van de wereld corrumperen. Daarom zijn backups hier niet optioneel. Zie etcd Deep Dive voor hoe etcd werkt en hoe je het netjes backupt.

Scenario 3: scheduler down

De kube-scheduler bepaalt waar pods draaien. Als deze down is:

Directe impact:

  • Nieuwe pods blijven in Pending state
  • Geen scheduling beslissingen worden gemaakt

Wat blijft werken:

  • Bestaande pods blijven draaien
  • API server functioneert normaal
  • Je kunt resources aanmaken (ze worden alleen niet gescheduled)

Wat breekt:

  • Nieuwe pods kunnen niet gescheduled worden
  • Herscheduling na node failure gebeurt niet
  • HPA maakt pods die Pending blijven
flowchart TD
    subgraph sched_down["Scheduler Down"]
        apply["kubectl apply deployment"] --> api["API accepteert"]
        api --> pending["Pod: Pending..."]
        subgraph queue["Pending Queue"]
            web["Pod: web"]
            apiPod["Pod: api"]
            job["Pod: job"]
            waiting["... wacht"]
        end
    end
    note["Nodes hebben capaciteit maar niemand wijst pods toe"]

Het verklikkende teken hier is een stapel Pending pods op een cluster met capaciteit zat. De API accepteert je manifests, etcd slaat ze op, de nodes hebben ruimte, maar niets plaatst de pods op een node. In HA setups draai je meerdere schedulers met leader election: één actief, de rest wacht om over te nemen.

Scenario 4: controller manager down

De controller manager draait de controllers die Kubernetes het label “zelf-herstellend” opleveren.

Directe impact:

  • ReplicaSet controller stopt
  • Deployment controller stopt
  • Node controller stopt
  • Alle reconciliation loops stoppen

Wat blijft werken:

  • Bestaande pods blijven draaien
  • Scheduling werkt nog
  • API server werkt nog

Wat breekt:

  • Gefaalde pods worden niet vervangen
  • Deployments rollen niet uit
  • Node failures worden niet afgehandeld
  • Orphaned resources worden niet opgeruimd
flowchart TD
    subgraph ctrl_down["Controller Manager Down"]
        rs["ReplicaSet: 3 desired, 2 running → geen actie"]
        deploy["Deployment: rollout bezig → stuck"]
        node["Node marked NotReady → pods niet evicted"]
    end
    note["Het 'zelf-herstellende' deel van Kubernetes stopt"]

Dit is het scenario waar het woord “magie” eindelijk wegvalt. Het zelf-herstellen is gewoon software die reconciliation loops draait, gewenste staat vergelijkt met werkelijke staat en op het verschil acteert. Stop de loops en het verschil stopt met dichten. Een ReplicaSet die drie replicas wil blijft vrolijk op twee zitten, voor altijd, want niemand kijkt. Ik vind dat geruststellend in plaats van alarmerend: het betekent dat ik de source kan lezen, het gedrag kan voorspellen, en het om de juiste redenen kan vertrouwen in plaats van het als black box te behandelen.

Scenario 5: kubelet down op een node

De kubelet is de Kubernetes agent op elke node. Als deze faalt:

Directe impact (op die node):

  • Node gemarkeerd als NotReady na timeout (standaard ~40 seconden)
  • Pods op die node worden evicted (na nog een timeout)
  • Geen nieuwe pods gescheduled naar die node

Wat blijft werken:

  • Containers blijven draaien (ze hebben kubelet niet nodig)
  • Netwerk werkt mogelijk nog (hangt af van CNI)
  • Andere nodes onbeïnvloed

Wat breekt:

  • Geen pod lifecycle management op die node
  • Health checks stoppen
  • Resource updates stoppen
  • Uiteindelijk worden pods elders herscheduled
flowchart LR
    subgraph kubelet_down["Kubelet Down op Node 2"]
        subgraph N1["Node 1 ✓ Ready"]
            P1["pod ✓"]
        end
        subgraph N2["Node 2 ✗ NotReady"]
            P2["pod ?<br/>orphaned"]
        end
        subgraph N3["Node 3 ✓ Ready"]
            P3["pod ✓<br/>rescheduled"]
        end
        P2 -.->|"rescheduled"| P3
    end
    note["Na pod-eviction-timeout worden pods herscheduled"]

Het deel dat mensen verrast: de containers blijven draaien zelfs met de kubelet dood. De container runtime houdt ze levend, de kubelet beheert alleen hun lifecycle. Dood de beheerder en de werkers werken door tot iets besluit ze te evicten en te herschedulen op een plek met een gezonde kubelet.

Scenario 6: container runtime down

Als de container runtime (containerd, CRI-O) faalt:

Directe impact:

  • Draaiende containers kunnen sterven
  • Nieuwe containers kunnen niet starten
  • Health checks falen

Wat daarna gebeurt:

  • kubelet detecteert failures
  • Pods gemarkeerd als Failed
  • Pods worden herscheduled naar andere nodes

Dit is de ene node-level failure die je workloads wél meeneemt, want de runtime is het ding dat ze daadwerkelijk draait. Het triggert dezelfde eviction-en-reschedule dans als een dode kubelet, alleen vanuit de andere kant.

Scenario 7: netwerk partitie

Netwerk partities zijn de gemeenste failures, want er is niets daadwerkelijk gecrasht. Een node verliest zijn pad naar de control plane terwijl hij vrolijk containers blijft draaien.

Wat er gebeurt:

  • Node gemarkeerd NotReady (kan API server niet bereiken)
  • Pods uiteindelijk evicted
  • Maar ze draaien mogelijk nog op de gepartitioneerde node
  • Potentieel voor “split brain”: dezelfde pod draait op twee plekken
flowchart LR
    subgraph partition["Netwerk Partitie"]
        subgraph control["Control Plane"]
            cp["Node 2 is NotReady<br/>Evicting pods..."]
        end
        control x--x|"✗"| partitioned
        subgraph partitioned["Gepartitioneerde Node"]
            pn["Ik ben prima, draai<br/>deze pods..."]
        end
    end
    note["Pod 'web-abc123' draait nu op Node 1 EN Node 2<br/>Beide denken dat zij de echte zijn"]

De control plane geeft de onbereikbare node op en herschedult zijn pods elders. De gepartitioneerde node kreeg het memo nooit en blijft de originelen draaien. Nu bestaat dezelfde pod twee keer, elk overtuigd dat hij de enige is. Voor stateless web pods haal je je schouders op. Voor een stateful database is het een data-corruptie incident dat staat te gebeuren, en daarom hebben stateful workloads fencing en quorum nodig, niet alleen “herschedule en hoop”.

Failure timeouts om te kennen

Deze timeouts bepalen hoe snel Kubernetes reageert op failures:

TimeoutDefaultWat het doet
node-monitor-grace-period40sHoe lang voordat node NotReady wordt
pod-eviction-timeout5mHoe lang voordat pods van NotReady node evicted worden
node-monitor-period5sHoe vaak node status wordt gecheckt

Je kunt deze omlaag tunen voor snellere failover, maar er zit een echte trade-off in: zet ze te strak en een netwerkhik van twee seconden evict gezonde pods. Snelle detectie en false positives zijn de twee uiteinden van dezelfde knop.

Het blast radius principe

Elke failure heeft een blast radius. De radius kennen vertelt je waar je je redundantie-budget moet besteden.

ComponentBlast Radius
ContainerEnkele container
PodAlle containers in pod
KubeletAlle pods op node
NodeAlle pods op node
SchedulerNieuwe pod scheduling cluster-wide
Controller ManagerSelf-healing cluster-wide
API ServerAlle management operaties
etcdAlles

Lees die tabel van onder naar boven en je HA prioriteiten schrijven zichzelf. etcd en de API server hebben de breedste radius, dus die krijgen de meeste redundantie. Een enkele container die faalt is ruis die het systeem al afhandelt.

Wat dit betekent voor jou

  1. Draaiende workloads zijn resilient: bestaande pods overleven de meeste control plane failures
  2. Management operaties niet: je hebt control plane HA nodig voor continuous deployment
  3. etcd is het kritieke pad: bescherm het, backup het, monitor het
  4. Failures cascaderen: API server down lijkt alsof alles down is, ook als de workloads prima zijn
  5. Timeouts zijn belangrijk: ken je failure detection tijden voordat je ze tuned

De reden dat ik Kubernetes vertrouw is niet dat het nooit faalt. Het is dat het faalt op manieren die ik kan voorspellen en beredeneren. De control plane en de runtime zijn met opzet gescheiden, dus het brein verliezen doodt het lichaam niet. De echte vraag voor je cluster is nooit óf een component zal falen. Het is of je voor de radius hebt ontworpen wanneer het gebeurt.

Failures testen

Wacht niet op productie om je te leren hoe je cluster zich gedraagt. Breek het zelf, op hardware die je niet erg vindt om kwijt te raken:

# Simuleer API server failure (op een test cluster!)
kubectl exec -it -n kube-system kube-apiserver-xxx -- kill 1

# Simuleer scheduler failure
kubectl scale deployment kube-scheduler -n kube-system --replicas=0

# Simuleer kubelet failure op een node
ssh node-1 'sudo systemctl stop kubelet'

Voor gecontroleerde, herhaalbare experimenten grijp je naar een chaos engineering tool zoals Litmus Chaos in plaats van met de hand kill 1.


Failure modes begrijpen is geen pessimisme, het is hoe je het recht verdient om een systeem resilient te noemen. Elk systeem faalt. De enige keuze die je krijgt is of je ervoor ontworpen hebt of erdoor verrast werd.