Chaos Engineering on Kubernetes with Chaos Mesh: Hands-On Fault Injection Guide

DevOps tutorial - IT technology blog
DevOps tutorial - IT technology blog

Quick start (5 min): Getting Chaos Mesh Up and Running

Production distributed systems fail in messy, unpredictable ways. A cloud node drops without warning. A third-party payment gateway introduces a sudden 450ms latency spike. An auth service runs out of memory under load. When downstream components lack strict timeouts and isolation, these localized hiccups cascade into total platform outages.

Chaos engineering flips the script. Instead of waiting for a 3 AM incident to uncover weak spots, you inject controlled disruptions during working hours. Chaos Mesh makes this straightforward on Kubernetes by driving experiments through native Custom Resource Definitions (CRDs) and granular kernel-level injection.

Step 1: Install Chaos Mesh with Helm

Install Chaos Mesh into an existing Kubernetes cluster (v1.19+) using Helm 3. Start by adding the official chart repository and updating your local index:

helm repo add chaos-mesh https://charts.chaos-mesh.org
helm repo update

Next, install Chaos Mesh into a dedicated chaos-mesh namespace and enable the web dashboard. For clusters running containerd (like standard EKS, GKE, or k3s), specify the runtime socket path:

kubectl create namespace chaos-mesh

helm install chaos-mesh chaos-mesh/chaos-mesh \
  --namespace=chaos-mesh \
  --set dashboard.create=true \
  --set chaosDaemon.runtime=containerd \
  --set chaosDaemon.socketPath=/run/containerd/containerd.sock

Verify that all core components—the controller manager, chaos daemon daemonset, and dashboard—transition to Running:

kubectl get pods -n chaos-mesh -o wide

Step 2: Deploy a Demo Target Application

To safely evaluate failure modes, deploy a lightweight 3-replica Nginx workload in a separate sandbox namespace:

kubectl create namespace app-test

kubectl create deployment web-target \
  --image=nginx:alpine \
  --replicas=3 \
  --namespace=app-test

Step 3: Run Your First Pod Termination Experiment

Test your service’s failover readiness. This manifest randomly kills one pod matching the app: web-target label:

apiVersion: chaos-mesh.org/v1alpha1
kind: PodChaos
metadata:
  name: pod-kill-demo
  namespace: app-test
spec:
  action: pod-kill
  mode: one
  selector:
    namespaces:
      - app-test
    labelSelectors:
      app: web-target

Apply the manifest and watch Kubernetes immediately provision a replacement replica:

kubectl apply -f pod-kill-demo.yaml

# Watch pod lifecycle events and recovery
kubectl get pods -n app-test -w

Deep dive: Simulating Real-World Failure Scenarios

1. The Cascading Timeout Problem (Network Latency)

Problem: In a microservices chain, the Checkout API makes synchronous calls to the Inventory Service. Under heavy database lock contention, Inventory response times climb from 15ms to 2,400ms. If the Checkout API lacks tight client timeouts and circuit breakers, its thread pool saturates within seconds. The entire frontend locks up.

Root Cause: Upstream services assume low-latency LAN conditions. They omit circuit breakers, fail to set fallback responses, and configure unbounded HTTP connection pools.

Practical Solution: Inject artificial latency into the target service using NetworkChaos. This tests whether upstream callers trip circuit breakers or degrade gracefully without dropping user requests.

apiVersion: chaos-mesh.org/v1alpha1
kind: NetworkChaos
metadata:
  name: network-delay-experiment
  namespace: app-test
spec:
  action: delay
  mode: all
  selector:
    namespaces:
      - app-test
    labelSelectors:
      app: web-target
  delay:
    latency: '800ms'
    jitter: '150ms'
    correlation: '25'
  duration: '5m'

Monitor your Grafana dashboards while this runs. If upstream p99 latencies climb across unrelated endpoints, your client timeout configurations need tuning.

2. The Intermittent Packet Loss Problem

Problem: Cross-AZ network congestion or saturated CNI overlays (like Cilium or Calico) intermittently drop 10% to 15% of packets. Users encounter sporadic 502 Bad Gateway or 504 Gateway Timeout errors. Meanwhile, standard Kubernetes liveness probes stay green.

Root Cause: Synthetic liveness probes run over single TCP handshakes every 10 seconds. They rarely detect random packet drops across active, high-throughput gRPC and HTTP/2 multiplexed streams.

Practical Solution: Apply NetworkChaos with a packet loss profile against two specific replicas to verify application-level retries:

apiVersion: chaos-mesh.org/v1alpha1
kind: NetworkChaos
metadata:
  name: packet-loss-experiment
  namespace: app-test
spec:
  action: loss
  mode: fixed
  value: '2'
  selector:
    namespaces:
      - app-test
    labelSelectors:
      app: web-target
  loss:
    loss: '15'
    correlation: '50'
  duration: '3m'

3. Memory Pressure and OOM Evictions (Resource Stress)

Problem: A sudden query load causes memory consumption to surge past container limits. The Linux kernel OOM-killer immediately terminates the container. If multiple replicas hit this threshold together, the service goes dark before the Horizontal Pod Autoscaler (HPA) can schedule new pods.

Root Cause: Tight container cgroup memory limits combined with slow HPA scaling metrics (e.g., 60-second evaluation windows) and memory-hungry JVM/Node.js runtimes.

Practical Solution: Apply StressChaos to push memory consumption to 512MB across two worker threads. This reveals whether your HPA scales out before pods get killed:

apiVersion: chaos-mesh.org/v1alpha1
kind: StressChaos
metadata:
  name: memory-stress-test
  namespace: app-test
spec:
  mode: one
  selector:
    namespaces:
      - app-test
    labelSelectors:
      app: web-target
  stressors:
    memory:
      workers: 2
      size: '512Mi'
  duration: '2m'

Advanced usage: Orchestrating Chaos Workflows and CI/CD Automation

Automated Experiment Pipelines with Workflow CRD

Isolated chaos tests are useful for debugging, but recurring resilience drills demand structured execution. The Chaos Mesh Workflow CRD lets you chain and parallelize complex failure sequences with built-in recovery buffers.

apiVersion: chaos-mesh.org/v1alpha1
kind: Workflow
metadata:
  name: microservice-disaster-drill
  namespace: app-test
spec:
  entry: main-pipeline
  templates:
    - name: main-pipeline
      templateType: Serial
      children:
        - network-delay-step
        - recovery-pause
        - pod-kill-step
    - name: network-delay-step
      templateType: NetworkChaos
      deadline: 2m
      networkChaos:
        action: delay
        mode: all
        selector:
          namespaces:
            - app-test
          labelSelectors:
            app: web-target
        delay:
          latency: '500ms'
    - name: recovery-pause
      templateType: Suspend
      deadline: 1m
    - name: pod-kill-step
      templateType: PodChaos
      deadline: 1m
      podChaos:
        action: pod-kill
        mode: one
        selector:
          namespaces:
            - app-test
          labelSelectors:
            app: web-target

Integrating Chaos Experiments into CI/CD Pipelines

Validate resilience before merging PRs. You can trigger chaos workflows in staging environments directly from GitHub Actions, GitLab CI, or Argo Workflows alongside synthetic load tests:

# 1. Trigger the chaos workflow during an automated integration run
kubectl apply -f workflows/microservice-disaster-drill.yaml

# 2. Run synthetic load tests against the degraded service
k6 run --vus 50 --duration 90s loadtests/checkout-flow.js

# 3. Check if the workflow completed successfully
kubectl get workflow microservice-disaster-drill -n app-test \
  -o jsonpath='{.status.conditions[?(@.type=="Accomplished")].status}'

# 4. Clean up workflow resources
kubectl delete -f workflows/microservice-disaster-drill.yaml

Practical tips: Running Chaos Tests Safely

  • Define steady-state metrics upfront: Establish explicit service level indicators before touching any cluster. For example: “During 500ms injected latency, error rates must remain under 0.5% and p95 latency must stay below 750ms.” If your observability stack cannot measure this in real time, abort the experiment and fix your telemetry first.
  • Isolate chaos targets with RBAC: Never grant cluster-wide fault injection privileges. Bind Chaos Mesh controllers to target specific staging or test namespaces using Kubernetes Role-Based Access Control (RBAC) and explicit namespaceSelectors.
  • Prepare an emergency abort command: Always know your rollback path before triggering an experiment. Deleting a chaos resource instantly flushes underlying kernel iptables rules and Traffic Control (TC) qdiscs:
# Instantly terminate all active network chaos experiments
kubectl delete networkchaos --all -n app-test
  • Test during office hours with team coverage: When moving experiments into staging or canary production environments, run them when on-call engineers and domain experts are available to observe system telemetry.
Share: