Chaos engineering for Kubernetes — deliberately inject failures (kill pods, add network latency, stress CPU, corrupt I/O) to prove your system survives them before a real incident does. You'll install Chaos Mesh, run pod/network/stress experiments, target precisely with selectors, chain them into workflows, and practise it safely. Hands-on throughout.
The journey:
| You need | Why |
|---|---|
A Kubernetes cluster + kubectl |
Chaos Mesh injects faults into it |
| Helm (easiest install) | Deploys the controller + dashboard |
| A workload to test (with HA) | Chaos on a single-replica app just breaks it |
Start in a non-production cluster. Confirm access: kubectl get nodes.
Chaos engineering is the practice of injecting controlled failures to build confidence that a system tolerates them. It's not random breakage — it's a scientific method:
Chaos Mesh makes step 3 easy on Kubernetes: it defines faults as CRDs (PodChaos, NetworkChaos, StressChaos…), a controller injects them, and a dashboard drives it. The goal is learning, not destruction — always with a hypothesis and a way to stop.
helm repo add chaos-mesh https://charts.chaos-mesh.org
helm repo update
helm install chaos-mesh chaos-mesh/chaos-mesh \
--namespace chaos-mesh --create-namespace --version 2.6.3
kubectl get pods -n chaos-mesh # controller-manager, daemon, dashboard
The chaos-daemon runs on each node to inject faults into pod namespaces; the controller-manager reconciles experiment CRDs. Access the dashboard with a port-forward to visualize and launch experiments.
PodChaos kills or disrupts pods. The classic experiment: does your app survive a pod dying?
apiVersion: chaos-mesh.org/v1alpha1
kind: PodChaos
metadata:
name: kill-one-api-pod
namespace: app
spec:
action: pod-kill # also: pod-failure, container-kill
mode: one # kill exactly one matching pod
selector:
namespaces: [app]
labelSelectors:
app: api
duration: "30s"
kubectl apply -f podchaos.yaml
kubectl get podchaos -n app # experiment status
kubectl describe podchaos kill-one-api-pod -n app
If your Deployment has ≥2 replicas and a PodDisruptionBudget, killing one pod should be invisible to users — the Service routes around it and the ReplicaSet replaces it. If it isn't invisible, you just found a gap. pod-failure makes a pod unavailable without deleting it; container-kill kills a container inside a pod.
Every experiment's blast radius is set by its selector and mode:
selector — namespaces, labelSelectors, annotationSelectors, pods (explicit), nodes. Narrow it tightly.mode — one, all, fixed: N, fixed-percent: 20, random-max-percent.spec:
mode: fixed-percent
value: "20" # affect 20% of matching pods
selector:
namespaces: [app]
labelSelectors: { tier: backend }
Getting the selector right is the safety mechanism — mode: all across a namespace is a self-inflicted outage. Start with mode: one, a labelled subset, and a short duration.
Network failures are the most instructive — most outages are partial, not total. NetworkChaos injects delay, loss, duplication, corruption, and partitions:
apiVersion: chaos-mesh.org/v1alpha1
kind: NetworkChaos
metadata: { name: db-latency, namespace: app }
spec:
action: delay
mode: all
selector:
namespaces: [app]
labelSelectors: { app: api }
delay:
latency: "200ms"
jitter: "50ms"
direction: to
target: # only traffic to the database
selector:
namespaces: [app]
labelSelectors: { app: postgres }
mode: all
duration: "2m"
This adds 200ms±50ms to API→DB traffic — revealing whether timeouts, retries, and circuit breakers behave. action: partition splits two groups entirely (a split-brain test); loss drops a percentage of packets. This is how you validate resilience assumptions that only show up under degraded networks.
Chaos Mesh covers the failure spectrum:
| Kind | Injects |
|---|---|
| StressChaos | CPU/memory pressure inside a pod |
| IOChaos | Filesystem latency, faults, or corruption |
| TimeChaos | Clock skew for a process |
| DNSChaos | DNS failures/errors |
| HTTPChaos | Faults on HTTP requests (abort, delay, patch) |
| KernelChaos | Kernel-level faults |
apiVersion: chaos-mesh.org/v1alpha1
kind: StressChaos
metadata: { name: cpu-hog, namespace: app }
spec:
mode: one
selector: { namespaces: [app], labelSelectors: { app: api } }
stressors:
cpu: { workers: 2, load: 90 }
duration: "1m"
Each maps to a real production failure mode — noisy neighbours (StressChaos), a slow disk (IOChaos), clock drift breaking TLS/tokens (TimeChaos), DNS outages (DNSChaos).
Run experiments on a schedule, or chain them into a scenario:
# Schedule: kill a pod every 10 minutes (game-day cadence)
apiVersion: chaos-mesh.org/v1alpha1
kind: Schedule
metadata: { name: periodic-kill, namespace: app }
spec:
schedule: "*/10 * * * *"
type: PodChaos
podChaos:
action: pod-kill
mode: one
selector: { namespaces: [app], labelSelectors: { app: api } }
A Workflow sequences and parallelizes experiments (add latency, then kill a pod, while stressing CPU) to reproduce compound failures. Combined with a load generator and your dashboards, this is a repeatable game day you can run in CI or on a cadence.
Chaos is only useful if it's controlled:
mode: one, labelled subsets, short duration; widen gradually.kubectl delete <chaos-kind> <name> stops it immediately; experiments auto-recover after duration.one/short, escalate deliberately.mode: all / no duration. A cluster-wide, open-ended fault is a self-inflicted incident.pod-kill PodChaos mode: one, and confirm users see nothing while a pod is replaced.Self-check:
selector and mode control blast radius?You now have the loop: hypothesis → inject (pod/network/stress) → narrow blast radius → observe steady state → schedule game days → run safely. That's chaos engineering that builds real confidence.