TL;DR Chaos engineering is je eigen cluster expres kapot maken, met de hypothese vooraf op papier en een rollback binnen handbereik. Ik draai het op de homelab met Litmus: een wekelijkse pod-delete, een maandelijkse node drain, elk kwartaal een game day. Tot nu toe heeft het vier dingen gevonden die het dashboard me nooit had laten zien.
M’n dashboard is een muur van groen. Pods draaien, replicas kloppen, CPU rustig, geen alert te bekennen. Ik kijk ernaar en krijg dat kleine shotje “alles is in orde.” En grotendeels klopt dat ook. Het cluster draait al weken en er is niks omgevallen.
Die muur is tegelijk het gevaarlijkste ding in m’n homelab. Hij zegt niks over wat er gebeurt als het misgaat. Alleen dat er, op dit moment, nog niks mis is.
Stel je nu de andere versie van die ochtend voor. Een node valt weg op precies het moment van de dag dat de load ertoe doet. Een DNS-lookup begint te timeouten en sleept elke readiness probe mee. Een service die ik acht maanden geleden heb neergezet en vergeten ben, vreet geheugen tot de OOM-killer van de kernel ingrijpt. Niks daarvan staat op een gezond dashboard. Het komt boven als ik slaap, en degene die dan zit te debuggen ben ik, slecht uitgerust en chagrijnig.
Dus hoe kom ik daar achter vóór productie het doet? Door het zelf kapot te maken. Op een doordeweekse middag, met koffie erbij en een rollback klaar. Dat is de hele pitch van chaos engineering, en ik vind m’n zwakke plekken liever op m’n eigen moment dan dat productie het moment kiest.
Wat expres dingen kapot maken betekent
Een gedistribueerd systeem faalt niet op één plek. Je kunt elke regel code lezen, het mooiste architectuurdiagram ter wereld tekenen, en nog steeds geen idee hebben hoe het ding zich gedraagt als een node halverwege een request wegvalt. Dat gedrag zit in de gaten tussen de componenten. De enige manier om het te zien is de failure veroorzaken en kijken.
Ik kom hier op deze blog niet voor niks steeds op terug. Ik wil begrijpen wat ik draai. Een groen dashboard vraagt me om vertrouwen, en iets wat ik nog nooit heb zien falen vertrouw ik niet helemaal. Na een chaos-experiment heb ik het zien herstellen, in plaats van aan te nemen dat het wel goed komt.
Even een verduidelijking, want de naam schrikt mensen af. Chaos engineering is niet willekeurig dingen slopen om te zien wat er gebeurt, en het is ook niet fault injection de prod in gooien zonder plan. Elk experiment heeft drie dingen aan zich hangen:
- Een hypothese. “Als één node faalt, reschedulen pods binnen vijf minuten.” Je test iets wat je al gelooft.
- Een blast radius die jij bepaalt. Bekend target, vaste duur, een rollback die je meteen kunt indrukken.
- Iets wat je leert. Of de overtuiging houdt stand, of je hebt een bug die je nog niet kende. Allebei prima wat mij betreft.
Netflix maakte dit beroemd met Chaos Monkey, dat willekeurige instances doodde zodat engineers wel services moesten bouwen die dat overleefden. De tooling is sindsdien flink volwassen geworden. Het idee is niet veranderd. Je veroorzaakt de failure op een moment dat je zelf koos, in plaats van erop te wachten.
De chaos-engineering-cyclus
1. Schrijf de hypothese op
Schrijf op wat je gelooft dat waar is, vóór je iets aanraakt. Je legt je vast op een voorspelling, zodat het resultaat je kan verrassen:
- “Als één node faalt, zullen pods binnen 5 minuten reschedulen”
- “Als de database traag wordt, zal de API graceful degraderen, niet crashen”
- “Als DNS faalt, zullen gecachete responses requests bedienen voor 30 seconden”
2. Ontwerp het experiment
Nu scopen. Vier vragen:
- Blast radius. Wat kan er geraakt worden?
- Duur. Hoe lang blijft de fault bestaan?
- Rollback. Hoe stop je het nú?
- Metrics. Waaraan zie je of het slaagde of faalde?
De rollback-vraag slaan mensen over. En dat is precies de vraag die voorkomt dat een experiment een incident wordt.
3. Draai het waar het geen kwaad kan
Begin klein. Eerst dev, dan staging, dan productie, en productie alleen met safeguards. Die volgorde heb ik precies één keer overgeslagen. Daarna niet meer.
4. Observeer en meet
Hier verdient je observability-stack zichzelf terug. Kun je niet zien wat er gebeurde, dan heb je alleen schade aangericht. Let op de voor de hand liggende dingen. Gingen de juiste alerts af? Bewoog de latency? Schoot de error rate omhoog? Hoe lang duurde het herstel, naast wat je had gegokt?
5. Leer en fix
Schrijf op wat je vond, fix de zwakke plek en werk het runbook bij, zodat de volgende niet vanaf nul begint. Die volgende ben ik meestal zelf, zes maanden later. Ontwerp dan het volgende experiment.
Litmus, chaos op de Kubernetes-manier
Litmus is een CNCF -project dat chaos engineering doet zoals Kubernetes alles doet. Je declareert een experiment als YAML en applyt het. Daarom koos ik het voor de homelab. Het experiment staat in Git naast de workload die het aanvalt, dus het is reviewbaar en reproduceerbaar, en het is geen slim commando dat iemand ooit één keer draaide en nooit opschreef.
Litmus installeren
# Voeg Litmus helm repo toe
helm repo add litmuschaos https://litmuschaos.github.io/litmus-helm/
helm repo update
# Installeer Litmus
helm install litmus litmuschaos/litmus \
--namespace litmus \
--create-namespace \
--set portal.frontend.service.type=ClusterIP
Litmus-architectuur
Een experiment declareren met ChaosEngine
apiVersion: litmuschaos.io/v1alpha1
kind: ChaosEngine
metadata:
name: nginx-chaos
namespace: default
spec:
appinfo:
appns: 'default'
applabel: 'app=nginx'
appkind: 'deployment'
engineState: 'active'
chaosServiceAccount: litmus-admin
experiments:
- name: pod-delete
spec:
components:
env:
- name: TOTAL_CHAOS_DURATION
value: '30'
- name: CHAOS_INTERVAL
value: '10'
- name: FORCE
value: 'false'
Dit doodt nginx-pods elke 10 seconden, 30 seconden lang. Klein, afgebakend, makkelijk te volgen. Begin hier.
De experimenten die je moet kennen
Je hebt ze niet allemaal nodig op dag één. Het menu kennen helpt wel, want elk experiment test een andere aanname die je stilletjes over je cluster maakt.
Chaos op pod-niveau
Pod delete. Doodt pods om restart-gedrag te testen.
experiments:
- name: pod-delete
spec:
components:
env:
- name: TOTAL_CHAOS_DURATION
value: '60'
- name: CHAOS_INTERVAL
value: '10'
- name: PODS_AFFECTED_PERC
value: '50' # Dood 50% van de pods
Container kill. Doodt één container binnen een pod.
experiments:
- name: container-kill
spec:
components:
env:
- name: TARGET_CONTAINER
value: 'sidecar'
- name: CHAOS_INTERVAL
value: '10'
Chaos op node-niveau
Node drain. Simuleert node-onderhoud.
apiVersion: litmuschaos.io/v1alpha1
kind: ChaosEngine
metadata:
name: node-drain-chaos
spec:
engineState: 'active'
auxiliaryAppInfo: ''
chaosServiceAccount: litmus-admin
experiments:
- name: node-drain
spec:
components:
env:
- name: TOTAL_CHAOS_DURATION
value: '60'
- name: TARGET_NODE
value: 'worker-1'
Node CPU hog. Simuleert CPU-druk.
experiments:
- name: node-cpu-hog
spec:
components:
env:
- name: TOTAL_CHAOS_DURATION
value: '60'
- name: NODE_CPU_CORE
value: '2' # Verbruik 2 cores
Netwerk-chaos
Pod network loss. Simuleert een netwerkpartitie.
experiments:
- name: pod-network-loss
spec:
components:
env:
- name: NETWORK_INTERFACE
value: 'eth0'
- name: NETWORK_PACKET_LOSS_PERCENTAGE
value: '100' # Totaal verlies
- name: TOTAL_CHAOS_DURATION
value: '30'
Pod network latency. Injecteert latency.
experiments:
- name: pod-network-latency
spec:
components:
env:
- name: NETWORK_LATENCY
value: '300' # 300ms latency
- name: JITTER
value: '100' # 100ms jitter
Storage-chaos
Disk fill. Test hoe de workload omgaat met een volle disk.
experiments:
- name: disk-fill
spec:
components:
env:
- name: FILL_PERCENTAGE
value: '90'
- name: TOTAL_CHAOS_DURATION
value: '60'
Je eerste experiment draaien
Genoeg menu gelezen. Hier een compleet experiment tegen een wegwerp-app, van begin tot eind, zodat je het kunt kopiëren en iets kapot ziet gaan.
1. Deploy een testapplicatie
apiVersion: apps/v1
kind: Deployment
metadata:
name: demo-app
labels:
app: demo
spec:
replicas: 3
selector:
matchLabels:
app: demo
template:
metadata:
labels:
app: demo
spec:
containers:
- name: nginx
image: nginx:alpine
ports:
- containerPort: 80
readinessProbe:
httpGet:
path: /
port: 80
initialDelaySeconds: 5
periodSeconds: 5
2. Maak een ServiceAccount voor Litmus
apiVersion: v1
kind: ServiceAccount
metadata:
name: litmus-admin
namespace: default
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: litmus-admin
rules:
- apiGroups: [""]
resources: ["pods", "pods/exec", "pods/log", "events"]
verbs: ["create", "delete", "get", "list", "patch", "update", "watch"]
- apiGroups: ["apps"]
resources: ["deployments", "replicasets"]
verbs: ["get", "list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: litmus-admin
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: litmus-admin
subjects:
- kind: ServiceAccount
name: litmus-admin
namespace: default
3. Draai het pod-delete-experiment
apiVersion: litmuschaos.io/v1alpha1
kind: ChaosEngine
metadata:
name: demo-chaos
namespace: default
spec:
appinfo:
appns: 'default'
applabel: 'app=demo'
appkind: 'deployment'
engineState: 'active'
chaosServiceAccount: litmus-admin
experiments:
- name: pod-delete
spec:
components:
env:
- name: TOTAL_CHAOS_DURATION
value: '30'
- name: CHAOS_INTERVAL
value: '10'
- name: FORCE
value: 'false'
probe:
- name: "check-endpoint"
type: "httpProbe"
httpProbe/inputs:
url: "http://demo-app.default.svc:80"
insecureSkipVerify: false
method:
get:
criteria: "=="
responseCode: "200"
mode: "Continuous"
runProperties:
probeTimeout: 5
interval: 2
retry: 2
4. Bekijk de resultaten
# Watch chaos engine status
kubectl get chaosengine demo-chaos -w
# Check chaos result
kubectl get chaosresult demo-chaos-pod-delete -o yaml
# Watch pod gedrag
kubectl get pods -l app=demo -w
Game days, hetzelfde idee gericht op het team
Hier komt het deel dat de YAML niet oplost. Een chaos-experiment vertelt je of het systeem het overleeft. Het zegt niks over of de mensen erachter weten wat ze moeten doen als de pager afgaat. De eerste keer dat iemand een runbook openslaat hoort niet tijdens een echte storing te zijn. Een game day is hoe je dat voorkomt.
Je neemt dezelfde gecontroleerde failure en richt hem op het team in plaats van op het cluster.
Een game day plannen
- Scope. Welke systemen, welke failure-scenario’s.
- Team. De engineers die op het incident zelf zouden reageren.
- Runbooks. Vooraf opgeschreven, anders valt er niks te testen.
- Succescriteria. Hoe ziet “goed afgehandeld” eruit?
- Tijd. Een blok in de agenda, niet een gaatje tussen twee meetings.
Opbouw van een game day
09:00 - Briefing: Leg scope uit, regels, hoe af te breken
09:15 - Injecteer failure (team weet niet welke)
09:15 - Team reageert alsof echt incident
10:00 - Failure onthuld, extra scenarios geïntroduceerd
11:00 - Debrief: Wat werkte, wat niet, wat te verbeteren
Voorbeeldscenario’s
| Scenario | Wat Je Leert |
|---|---|
| Dood primaire database | Failover snelheid, data integriteit |
| DNS storing | Caching gedrag, timeout handling |
| Certificaat verloopt | Monitoring gaps, renewal proces |
| Cloud regio onbeschikbaar | Multi-regio readiness |
| Secrets manager down | Applicatie gedrag zonder secrets |
In Git houden
De experimenten zijn YAML, dus ze horen in version control bij al het andere. Ik beheer de mijne met ArgoCD, gedefinieerd maar slapend, klaar voor als ik er één wil draaien:
# chaos-experiments/pod-delete.yaml (in Git)
apiVersion: litmuschaos.io/v1alpha1
kind: ChaosEngine
metadata:
name: scheduled-pod-delete
namespace: chaos-testing
spec:
engineState: 'stop' # Handmatig geactiveerd
# ... experiment config
De flow is dezelfde als voor al het andere. Experiment in Git, ArgoCD synct het, engineState op active als ik het wil draaien, resultaat lezen, en de fix of de bevinding committen. Het experiment en z’n historie staan naast de workload die het test.
Veilig blijven terwijl je dit doet
Slordig gedaan is chaos engineering een storing die je zelf hebt veroorzaakt. Het verschil zit in discipline, dus hier is de mijne.
Begin buiten productie en blijf daar tot je het experiment vertrouwt. Heb de rollback klaar vóór je begint, niet als het al misgaat. Kijk live mee in plaats van starten en weglopen. Zeg het tegen iedereen die gepaged kan worden. En schrijf op wat je vond, want over een maand weet niemand meer wat het vond.
De fouten zijn net zo voorspelbaar. Draaien in je piekuur terwijl je helemaal geen piekgedrag wilde testen. Een failure injecteren zonder de on-call te waarschuwen. De hypothese overslaan, zodat je signaal niet van ruis kunt onderscheiden. De bevinding negeren omdat het cluster “goed genoeg” herstelde.
Het belangrijkste van allemaal, bepaal vooraf wanneer je afbreekt. Error rate boven een drempel, latency boven een plafond, echte gebruikers die klagen, of welk gedrag dan ook dat je niet had voorspeld. Litmus kan een deel daarvan afdwingen met een probe die het experiment voor je afbreekt:
# Afbreken bij hoge error rate
probe:
- name: "error-rate-check"
type: "promProbe"
promProbe/inputs:
endpoint: "http://prometheus:9090"
query: "rate(http_requests_total{status=~'5..'}[1m])"
comparator:
type: "float"
criteria: "<"
value: "0.1" # Afbreken als > 10% errors
Waarom niet iedereen dit al doet
Als chaos engineering zo nuttig is, waarom zie je het dan niet overal? De obstakels bestaan, en als ik ze niet benoem leest dit als een makkelijke verkoop. Dat is het niet.
De grootste is angst, en dat is terechte angst. Een systeem waar jij verantwoordelijk voor bent expres kapot maken voelt de eerste keer krankzinnig, zeker als je het nog nooit veilig hebt zien falen. “Het werkt” is een sterk argument tegen “laten we het gaan slopen.” Dan is er de tijd. Een goed experiment ontwerpen, ernaar kijken, documenteren, fixen wat het vond, niks daarvan is gratis en het concurreert met features shippen. En er is cultuur. In een team waar de schuldvraag rondgaat, is expres een failure veroorzaken een carrièrerisico, ook als het het verantwoordelijke is om te doen.
Geen van die drie verdwijnt vanzelf. Wat bij mij werkte was zo klein beginnen dat er niks was om bang voor te zijn, een pod-delete tegen een wegwerp-nginx in een dev-namespace.
Wat ik in de homelab draai
Dus hier is de versie die ik op m’n eigen cluster draai. Niks heldhaftigs. Een wekelijkse pod-delete tegen willekeurige pods in niet-kritieke namespaces, een maandelijkse node drain om het verlies van een machine te oefenen, en ongeveer eens per kwartaal een game day met een paar gestapelde failures.
Dat kleine ritme heeft zichzelf al terugverdiend. Wat het me leerde:
- Longhorn vangt een verdwijnende node een stuk rustiger op dan ik verwachtte. Dat hoopte ik altijd. Nu heb ik het gezien.
- Meerdere van m’n workloads hadden een nutteloze
terminationGracePerioden werden halverwege een request hard-killed, en dat was me in normaal bedrijf nooit opgevallen. - M’n alerting gaat consequent later af dan zou moeten. Dat leer ik liever op een doordeweekse middag dan tijdens een incident.
- Herstel duurt langer dan ik gok. Altijd.
Elk van die vier was een page die ik midden in de nacht had gekregen, ingeruild voor een middag licht ongemak. Die ruil doe ik zo weer.
Het dashboard is vanochtend weer groen. Ik vertrouw het iets meer dan eerst, omdat ik het op m’n eigen moment rood heb zien worden en weer terug.
