Chaos Engineering Practice: Litmus and Gremlin for Kubernetes Reliability

Breaking Things on Purpose

Your team says the system is resilient. Your architecture diagrams show redundancy at every layer. Your runbooks cover common failure scenarios. But nobody has actually tested what happens when an entire availability zone drops, or when the database failover takes 90 seconds instead of the documented 30, or when the message queue silently drops 5% of messages under load.

Chaos engineering fills that gap. It's not about breaking things for fun. It's a disciplined practice of injecting controlled failures to discover weaknesses before production incidents find them for you. Litmus and Gremlin are two tools that make this practical for Kubernetes environments.

Litmus: Open Source, Kubernetes-Native

Litmus runs chaos experiments as Kubernetes custom resources. The ChaosHub provides a library of pre-built experiments, and you define workflows that chain multiple experiments together.

# Install Litmus
helm repo add litmuschaos https://litmuschaos.github.io/litmus-helm
helm install litmus litmuschaos/litmus \
  --namespace litmus --create-namespace \
  --set portal.frontend.service.type=ClusterIP

Pod-Level Chaos

Start simple. Kill pods and verify that your deployment recovers within the expected timeframe.

apiVersion: litmuschaos.io/v1alpha1
kind: ChaosEngine
metadata:
  name: api-pod-kill
  namespace: production
spec:
  appinfo:
    appns: production
    applabel: "app=api-server"
    appkind: deployment
  engineState: active
  chaosServiceAccount: litmus-admin
  experiments:
    - name: pod-delete
      spec:
        components:
          env:
            - name: TOTAL_CHAOS_DURATION
              value: "60"
            - name: CHAOS_INTERVAL
              value: "10"
            - name: FORCE
              value: "false"
            - name: PODS_AFFECTED_PERC
              value: "50"

The experiment kills 50% of API server pods every 10 seconds for 60 seconds. Your Kubernetes deployment should replace them. If it doesn't -- or if the replacement takes longer than your readiness probe timeout -- you've found a problem before users did.

Network Chaos

Pod kills are the easy test. Network chaos reveals deeper problems. Latency injection catches timeout misconfiguration, and packet loss exposes retry logic that doesn't actually work.

apiVersion: litmuschaos.io/v1alpha1
kind: ChaosEngine
metadata:
  name: network-latency-test
spec:
  appinfo:
    appns: production
    applabel: "app=api-server"
  experiments:
    - name: pod-network-latency
      spec:
        components:
          env:
            - name: NETWORK_LATENCY
              value: "300"
            - name: JITTER
              value: "100"
            - name: DESTINATION_IPS
              value: "10.0.0.0/8"
            - name: TOTAL_CHAOS_DURATION
              value: "120"

Adding 300ms +/- 100ms of latency to all internal traffic for two minutes. We ran this on a payment service and discovered that the checkout flow timed out at 500ms total, but the service made three sequential calls to internal APIs. Under normal conditions, each call took 50ms. With 300ms added, the chain exceeded the timeout and customers saw checkout failures. The fix was parallelizing those calls -- something nobody would have prioritized without the chaos test proving it was a real risk.

Gremlin: Enterprise Chaos Platform

Gremlin is commercial, polished, and broader than Kubernetes. It runs chaos on VMs, containers, bare metal, and cloud services. The UI makes it accessible to teams that aren't comfortable writing YAML experiment definitions.

Attack Types

Gremlin categorizes attacks into three groups: resource (CPU, memory, disk, IO), network (latency, packet loss, DNS, blackhole), and state (process kill, time travel, shutdown).

# Gremlin CLI: CPU stress test
gremlin attack cpu \
  --length 120 \
  --percent 90 \
  --cores 0 \
  --target-tags "service=api,env=staging"

# Gremlin CLI: DNS failure
gremlin attack dns \
  --length 60 \
  --target-tags "service=api,env=staging" \
  --domains "database.internal,cache.internal"

The DNS attack is particularly revealing. We used it to test a microservice that cached DNS results. The service handled DNS failure gracefully during the attack, but when DNS recovered, the stale cache entries persisted for hours because the TTL was set to the default of the DNS client library -- not the upstream TTL. Without the chaos test, we'd never have noticed until a real DNS issue caused prolonged stale routing.

Scenarios and Game Days

Gremlin's scenario builder chains attacks into sequences that simulate realistic failure cascades. A "zone failure" scenario might combine network blackhole for a specific IP range, increased latency on remaining connections, and elevated CPU from the surge of requests hitting surviving instances.

# Multi-step scenario via Gremlin API
{
  "name": "Zone B Failure Simulation",
  "steps": [
    {
      "attacks": [{
        "type": "blackhole",
        "args": {"hostnames": ["10.0.2.0/24"], "length": 300},
        "target": {"tags": {"zone": "us-east-1b"}}
      }],
      "delay": 0
    },
    {
      "attacks": [{
        "type": "latency",
        "args": {"ms": 200, "length": 300},
        "target": {"tags": {"zone": "us-east-1a"}}
      }],
      "delay": 30
    },
    {
      "attacks": [{
        "type": "cpu",
        "args": {"percent": 80, "length": 300},
        "target": {"tags": {"zone": "us-east-1a"}}
      }],
      "delay": 60
    }
  ]
}

Building a Chaos Practice

Most teams fail at chaos engineering not because the tools are wrong but because they skip the discipline around them.

Start with Steady State

Before breaking anything, define what "working" looks like in measurable terms. Error rate below 0.1%. P99 latency under 500ms. Order processing throughput above 100/minute. Without this baseline, you can't objectively determine whether the system survived the experiment.

Blast Radius Control

Run experiments in staging first. When you move to production, start with a single pod, then a percentage, then a full node, then a zone. Never skip levels just because staging looked fine -- production traffic patterns are different.

# Progressive blast radius
experiments:
  - phase: 1
    target: single_pod
    duration: 60s
    abort_condition: "error_rate > 1%"
  - phase: 2
    target: 25%_of_pods
    duration: 120s
    abort_condition: "error_rate > 0.5%"
  - phase: 3
    target: entire_node
    duration: 180s
    abort_condition: "error_rate > 0.1%"

Automated Abort

Every experiment needs a kill switch. Both Litmus and Gremlin support automatic abort conditions. If the error rate exceeds your threshold during the experiment, the chaos stops immediately and the system returns to steady state.

Common Findings

After running chaos experiments across 20+ services, these are the most frequent discoveries:

Timeouts configured wrong: services set 30-second timeouts but their callers have 10-second timeouts. The upstream gives up before the downstream even knows there's a problem.

Circuit breakers never tested: teams install Hystrix or resilience4j, configure circuit breakers, and never verify they actually trip under load. We found circuit breakers that were configured but never wired into the actual HTTP client.

Health checks that lie: readiness probes return 200 when the application starts, even before it's connected to its database. Kubernetes routes traffic to pods that aren't actually ready to serve requests.

Retry storms: service A retries failed calls to service B three times. Service B retries calls to service C three times. A single failure in C generates 9 requests. With more layers, this multiplies exponentially and turns a minor issue into a cascading outage.

Don't treat these as theoretical risks. Run the experiments and find out which ones apply to your systems. The answer is usually "more than you'd expect."