Chaos Engineering in Kubernetes: Resilience Testing for Cloud-Native Apps
Kubernetes introduces a new class of failure modes that traditional testing misses: pods evicted under memory pressure, nodes drained during rolling updates, network policies blocking inter-service traffic, readiness probes misconfigured. Chaos engineering in Kubernetes targets these specifically — testing that your workloads survive the infrastructure events that Kubernetes itself causes.
Key Takeaways
Kubernetes already runs chaos on your cluster — you just don't control it. Scheduler preemption, node auto-scaling, rolling deployments, and eviction policies all kill pods regularly. Chaos engineering makes those events predictable and observable.
Pod disruption budgets are your safety net for chaos experiments. Configure PDBs before running any chaos that kills pods. Without them, an experiment can kill more pods than intended and cause a real outage.
Readiness and liveness probes are the first thing chaos experiments expose. Badly configured probes cause Kubernetes to route traffic to unhealthy pods or kill healthy ones. Chaos experiments surface this quickly.
Node chaos is riskier than pod chaos. Draining or killing a node affects all pods on it, not just the target service. Start with pod-level experiments and move to node-level only after establishing pod resilience.
Resource quotas prevent chaos experiments from affecting neighboring namespaces. Always scope chaos experiments to specific namespaces, and verify that your chaos tool respects namespace isolation.
Why Kubernetes Needs Its Own Chaos Approach
A Kubernetes cluster isn't a static environment. Under normal operation, Kubernetes constantly:
- Kills and reschedules pods when nodes are memory-pressured
- Drains nodes during upgrades and maintenance
- Evicts pods when resource limits are exceeded
- Fails health checks and routes traffic away from unhealthy pods
- Scales deployments up and down in response to load
These events happen in production whether you test for them or not. Chaos engineering in Kubernetes means making these events happen on purpose, in controlled circumstances, so you discover how your workloads respond before a production incident teaches you.
Failure Modes Specific to Kubernetes
Pod Scheduling Failures
If a pod's resource requests can't be satisfied, it stays in Pending state. Common causes: resource requests too high, no node has capacity, node affinity rules can't be satisfied.
Test: Create a pod with resource requests that can't be scheduled. Verify your monitoring alerts and that your deployment doesn't silently shed replicas.
Readiness Probe Failures
Readiness probes tell Kubernetes whether a pod is ready to receive traffic. A misconfigured probe (wrong path, too-short timeout, wrong port) causes Kubernetes to route traffic away from healthy pods or never route traffic to them.
Test: Temporarily fail the readiness probe endpoint. Verify that Kubernetes removes the pod from the load balancer but doesn't kill it. Verify that traffic is correctly distributed to remaining healthy pods.
Resource Exhaustion and Eviction
Pods running near their memory limits get evicted first during node memory pressure. If your limits are too low, a legitimate traffic spike evicts your pods. If limits are too high, you waste cluster capacity.
Test: Inject memory pressure into a pod and observe eviction behavior. Verify that your deployment recovers, that evicted pods are rescheduled, and that the cluster doesn't cascade.
Network Policy Misconfiguration
NetworkPolicy resources define which pods can talk to which. A missing or incorrect policy silently blocks traffic — no error, just connection refused. This is especially common when new services are added to a namespace that has default-deny policies.
Test: Apply a restrictive NetworkPolicy to a namespace, then verify that service-to-service calls succeed (or fail gracefully with clear error messages).
Setting Up Chaos Mesh for Kubernetes
Chaos Mesh is the most widely used Kubernetes-native chaos tool. Here's a working setup:
# Install Chaos Mesh via Helm
helm repo add chaos-mesh https://charts.chaos-mesh.org
helm repo update
helm install chaos-mesh chaos-mesh/chaos-mesh \
--namespace chaos-testing \
--create-namespace \
--set chaosDaemon.runtime=containerd \
--set chaosDaemon.socketPath=/run/containerd/containerd.sockVerify installation:
kubectl get pods -n chaos-testing
# Should show controller-manager, chaos-daemon, chaos-dashboard podsCore Kubernetes Chaos Experiments
1. Pod Kill
The simplest and safest starting point. Terminate a pod and verify that Kubernetes reschedules it and that traffic is redistributed.
apiVersion: chaos-mesh.org/v1alpha1
kind: PodChaos
metadata:
name: pod-kill-experiment
namespace: chaos-testing
spec:
action: pod-kill
mode: one
selector:
namespaces:
- production
labelSelectors:
app: web-api
duration: "1m"What to observe:
- Pod termination appears in
kubectl get events -n production - New pod starts within your rollout window (typically 30–60 seconds for a healthy deployment)
- Load balancer stops routing to the killed pod immediately
- No user-visible errors (or if there are, quantify how many)
2. Pod Failure (without termination)
Puts the pod in a failure state without terminating it — simulates a process crash or health check failure.
apiVersion: chaos-mesh.org/v1alpha1
kind: PodChaos
metadata:
name: pod-failure-experiment
namespace: chaos-testing
spec:
action: pod-failure
mode: fixed-percent
value: "25"
selector:
namespaces:
- production
labelSelectors:
app: worker-service
duration: "5m"With mode: fixed-percent and value: "25", this affects 25% of matching pods — useful for testing graceful degradation at reduced capacity.
3. Network Delay
Injects latency into network calls from or to a specific pod. Tests timeout handling, circuit breakers, and user-visible latency degradation.
apiVersion: chaos-mesh.org/v1alpha1
kind: NetworkChaos
metadata:
name: api-latency-experiment
namespace: chaos-testing
spec:
action: delay
mode: all
selector:
namespaces:
- production
labelSelectors:
app: payment-service
delay:
latency: "300ms"
jitter: "50ms"
correlation: "25"
duration: "10m"
direction: to
target:
selector:
namespaces:
- production
labelSelectors:
app: database-proxy
mode: allThis adds 300ms ± 50ms latency specifically on traffic from payment-service to database-proxy. Other traffic is unaffected.
What to observe:
- Does
payment-servicetimeout gracefully or hang? - Does latency propagate upstream (caller of
payment-servicealso slows down)? - Are timeout values configured correctly?
4. Network Partition
Simulates a complete network split — one service can't reach another. Tests circuit breaker behavior and graceful degradation.
apiVersion: chaos-mesh.org/v1alpha1
kind: NetworkChaos
metadata:
name: partition-experiment
namespace: chaos-testing
spec:
action: partition
mode: all
selector:
namespaces:
- production
labelSelectors:
app: feature-flag-service
direction: both
duration: "3m"What to observe:
- Does your application fail open or fail closed when the feature flag service is unreachable?
- Do circuit breakers open and prevent cascading requests?
- Do error rates spike and then stabilize as circuit breakers engage?
5. Memory Stress
Allocates memory inside a pod to push it toward its memory limit, triggering Kubernetes eviction or OOM.
apiVersion: chaos-mesh.org/v1alpha1
kind: StressChaos
metadata:
name: memory-stress-experiment
namespace: chaos-testing
spec:
mode: one
selector:
namespaces:
- production
labelSelectors:
app: report-generator
stressors:
memory:
workers: 2
size: "1GB"
duration: "5m"What to observe:
- Does Kubernetes evict the pod or OOM-kill the container?
- Is the memory limit set correctly for expected peak usage?
- Does the deployment recover after eviction?
6. Node Drain
For simpler node experiments, use kubectl drain manually:
# Cordon node (prevent new pods from being scheduled)
kubectl cordon node-name
# Drain node (evict all non-daemonset pods)
kubectl drain node-name --ignore-daemonsets --delete-emptydir-data
# Restore
kubectl uncordon node-nameWhat to observe:
- Are all pods rescheduled to remaining nodes within your SLA?
- Does the cluster have enough capacity to absorb one node's worth of pods?
- Are any pods stuck in
Pendingafter the drain?
Essential Prerequisites for Kubernetes Chaos
Pod Disruption Budgets
Before running any pod chaos, configure PDBs to limit how many pods can be disrupted simultaneously:
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: web-api-pdb
namespace: production
spec:
minAvailable: 2
selector:
matchLabels:
app: web-apiWith minAvailable: 2, Kubernetes will always maintain at least 2 healthy web-api pods, regardless of what chaos experiments or rolling updates try to do.
Namespace Scoping
Always scope chaos experiments to specific namespaces. Chaos Mesh selectors accept namespace filters — use them:
selector:
namespaces:
- production # Only target production namespace
labelSelectors:
app: web-apiNever use namespaces: [] (all namespaces) in early experiments.
Observability Requirements
You need to see the impact of chaos experiments in real time. At minimum:
- Pod health:
kubectl get pods -n production -w - Resource metrics: Kubernetes Dashboard or Prometheus/Grafana showing CPU, memory, request rates
- Application metrics: Request success rate, error rate, latency by percentile
- Logs: Structured logs from the affected service, accessible in near-real-time
Chaos Experiment Schedule
A sustainable cadence for a production Kubernetes service:
| Frequency | Experiment type |
|---|---|
| Every deploy | Automated rolling restart validation (Kubernetes does this natively) |
| Weekly | Pod kill test in staging |
| Monthly | Network latency injection in staging |
| Quarterly | Full game day including node failure scenarios |
| Before major releases | Capacity-relevant stress tests |
Continuous automated experiments (Chaos Monkey-style pod killing in production) are appropriate once you've validated each experiment type in staging and have established recovery SLOs.