What You'll Build
In this exercise you will plan and execute a structured Game Day for CricketPulse with two fault injection experiments: pod failure on the score API, and latency injection on the payment service. You will deploy Chaos Mesh, define experiment CRDs, run experiments with Grafana monitoring active, document findings, and produce a Game Day report with action items. The exercise is designed to run in a staging Kubernetes cluster.
Prerequisites
- Lessons 17, 18, and 19 completed.
- A Kubernetes staging cluster with CricketPulse deployed (score-api and payment-service).
- Horizontal Pod Autoscaler configured on score-api (min: 2, max: 10 replicas).
- Grafana dashboard from the Observability course showing error rate, p99 latency, and pod count.
- Chaos Mesh installed (helm install chaos-mesh chaos-mesh/chaos-mesh).
- A circuit breaker configured on the payment service (e.g., Resilience4j or Istio retry policy).
Step 1 — Pre-Game Day Setup
Verify all prerequisite systems are in place before starting the Game Day clock. A pre-flight checklist prevents discovering missing infrastructure mid-experiment.
#!/bin/bash
# Pre-Game Day preflight checklist
set -e
echo "=== CricketPulse Game Day Preflight ==="
# 1. Confirm Chaos Mesh is running
echo "Checking Chaos Mesh..."
kubectl get pods -n chaos-testing -l app.kubernetes.io/instance=chaos-mesh --field-selector=status.phase=Running | grep -c Running
# Expected: >= 3 pods running
# 2. Confirm CricketPulse services are healthy
echo "Checking CricketPulse services..."
kubectl get pods -n cricketpulse --field-selector=status.phase=Running
for deploy in score-api payment-service checkout-api; do
kubectl rollout status deployment/$deploy -n cricketpulse --timeout=30s
echo " $deploy: OK"
done
# 3. Confirm steady state metrics (all should be in green range)
echo "Checking steady state..."
curl -s "http://prometheus:9090/api/v1/query" --data-urlencode 'query=rate(http_requests_total{status=~"5..",namespace="cricketpulse"}[5m])' | python3 -c "import json,sys; d=json.load(sys.stdin); print('Error rate:', d['data']['result'])"
# Expected: empty result or 0.0 (no errors)
# 4. Confirm HPA is configured on score-api
kubectl get hpa score-api -n cricketpulse
# Expected: MINPODS=2, MAXPODS=10, CURRENT=2
# 5. Confirm Grafana dashboard is accessible
curl -s -o /dev/null -w "%{http_code}" http://grafana:3000/api/health
# Expected: 200
echo "=== Preflight PASSED — Ready for Game Day ==="Step 2 — Experiment 1: Score API Pod Failure
Hypothesis: if one score-api pod is deleted, the HPA creates a replacement within 60 seconds and error rate remains below 1%. Deploy the experiment CRD, observe the response, and record actual outcomes.
# Define Experiment 1: Pod Failure
cat > experiment-01-pod-failure.yaml << 'EOF'
apiVersion: chaos-mesh.org/v1alpha1
kind: PodChaos
metadata:
name: score-api-pod-failure
namespace: cricketpulse
spec:
action: pod-kill
mode: one # kill exactly one pod
selector:
namespaces: [cricketpulse]
labelSelectors:
app: score-api
duration: "30s" # experiment duration (pod stays killed until HPA recreates)
gracePeriod: 0 # kill immediately, no graceful shutdown
EOF
# T+00:00 — Record steady state
echo "BASELINE at $(date -u)"
kubectl get pods -n cricketpulse -l app=score-api
# Record replica count: should be 2
# Take Grafana screenshot: error rate and p99 latency panels
# T+05:00 — Apply experiment
kubectl apply -f experiment-01-pod-failure.yaml
echo "EXPERIMENT STARTED at $(date -u)"
# T+05:00 - T+07:00 — OBSERVE (do not fix, do not panic, just watch)
# Watch pod count in real time
watch kubectl get pods -n cricketpulse -l app=score-api
# Watch Grafana: does error rate spike? If yes: how high, for how long?
# Watch Grafana: does HPA show replica count changing?
# Watch Grafana: does p99 latency increase?
# Expected observations:
# T+05:00: one pod shows Terminating
# T+05:05: pod count drops to 1
# T+05:15: HPA detects low replica count, creates new pod
# T+05:45: new pod shows Running and passes readiness probe
# T+06:00: error rate returns to <0.1%
# Error rate during gap: should be <1% if connection draining works
# T+07:00 — Record post-experiment state
echo "POST-EXPERIMENT at $(date -u)"
kubectl get pods -n cricketpulse -l app=score-api
# Expected: 2 Running pods again# Observations log for Experiment 1 (fill in during the experiment)
EXP1_LOG = """
## Experiment 1: Score API Pod Failure
**Hypothesis**: HPA replaces pod within 60s; error rate < 1% during gap
**Baseline** (T+00:00):
- Replica count: [fill]
- Error rate: [fill]%
- P99 latency: [fill]ms
**Fault Injected** (T+05:00):
- Action: kubectl apply experiment-01-pod-failure.yaml
- Confirmed pod killed: [fill pod name]
**Observations**:
- T+05:05: pod count dropped to [fill]
- T+05:[fill]: HPA triggered — new pod creation started
- T+05:[fill]: new pod Running and ready
- Error rate during gap: [fill]% (peak)
- P99 latency during gap: [fill]ms (peak)
- Duration of degradation: [fill] seconds
**Hypothesis result**: PASSED / DEVIATED
If deviated: [describe what was different]
**Findings**:
- [fill: what did you learn about your system?]
"""Step 3 — Experiment 2: Payment Service Latency Injection
Hypothesis: injecting 1-second latency on the payment service triggers the circuit breaker within 3 retries, and the checkout API returns a graceful degraded response (503 with a retry-after header) rather than queuing indefinitely.
# Define Experiment 2: Payment Latency Injection
cat > experiment-02-payment-latency.yaml << 'EOF'
apiVersion: chaos-mesh.org/v1alpha1
kind: NetworkChaos
metadata:
name: payment-latency
namespace: cricketpulse
spec:
action: delay
mode: all # affect all payment-service pods
selector:
namespaces: [cricketpulse]
labelSelectors:
app: payment-service
delay:
latency: "1000ms" # 1 second added latency
jitter: "100ms"
correlation: "100" # consistent latency (not random)
direction: from # delay incoming requests to payment service
duration: "3m"
EOF
# Wait 5 minutes between experiments for system to fully recover
echo "Waiting 5 minutes for recovery from Experiment 1..."
sleep 300
# T+15:00 — Record steady state for Experiment 2
echo "BASELINE (Exp 2) at $(date -u)"
# Record checkout error rate, checkout p99 latency
# T+20:00 — Apply experiment
kubectl apply -f experiment-02-payment-latency.yaml
echo "EXPERIMENT 2 STARTED at $(date -u)"
# T+20:00 - T+23:00 — OBSERVE
# Watch Grafana: checkout p99 latency — does it climb by ~1 second?
# Watch Grafana: does checkout error rate increase?
# Watch Grafana: circuit breaker state (if exposed as metric)
# Watch logs: kubectl logs -l app=checkout-api -n cricketpulse -f | grep -i "circuit\|timeout\|retry"
# T+23:00 — Remove experiment
kubectl delete -f experiment-02-payment-latency.yaml
echo "EXPERIMENT 2 ENDED at $(date -u)"
# Verify system recovery (error rate drops within 30s of experiment removal)Step 4 — Produce the Game Day Report
Compile all observations into a Game Day report document. This is the primary output of the exercise — a structured record of what was tested, what happened, and what will be fixed.
# Game Day report template
GAMEDAY_REPORT = """
# CricketPulse Game Day Report — {date}
## Participants
- IC: [your name]
- Technical Lead: [name]
- Observer: [names]
## Steady State Definition
- Score API success rate: >99%
- Score API p99 latency: <500ms
- Checkout success rate: >99.5%
- Checkout p99 latency: <1000ms
## Experiment 1: Score API Pod Failure
**Hypothesis**: HPA replaces pod within 60s; error rate <1%
**Result**: PASSED / DEVIATED
**Actual replacement time**: [fill] seconds
**Peak error rate during gap**: [fill]%
**Findings**: [fill]
## Experiment 2: Payment Service Latency (1s injection)
**Hypothesis**: Circuit breaker trips within 3 retries; checkout returns 503 gracefully
**Result**: PASSED / DEVIATED
**Circuit breaker activated**: Yes/No at T+[fill] seconds
**Peak checkout error rate**: [fill]%
**Checkout p99 latency during experiment**: [fill]ms
**Findings**: [fill]
## Action Items
| # | Finding | Action | Owner | Due |
|---|---------|--------|-------|-----|
| A1 | [finding from Exp 1] | [specific fix] | [name] | [date] |
| A2 | [finding from Exp 2] | [specific fix] | [name] | [date] |
## What Worked Well
- [fill: at least 2 items]
## What to Improve
- [fill: at least 2 items]
## Next Game Day
- Date: [4-8 weeks from today]
- Proposed scenarios: [fill]
"""
# Save to: gamedayreports/2025-06-03-cricketpulse.mdVerify Your Work
Review your Game Day against this checklist. A successful Game Day meets all six criteria.
# Game Day success criteria
# 1. Preflight passed — all services healthy before experiments started
# Yes / No
# 2. Experiments were run in sequence with 5-minute recovery gap between them
# Yes / No
# 3. Baseline metrics recorded before each experiment (screenshots or values)
# Yes / No
# 4. All experiments had an automatic duration (no manual "I'll stop it later")
# Yes / No: experiment-01 duration: 30s, experiment-02 duration: 3m
# 5. Game Day report produced with:
# - Hypothesis for each experiment
# - Actual observations vs hypothesis
# - At least one action item per experiment that deviated
# Yes / No
# 6. System returned to steady state after both experiments
# Verify: kubectl get pods -n cricketpulse (all Running)
# Verify: error rate back to <0.1% in Grafana
# Scoring:
# 6/6: Game Day complete — schedule next one in 4-8 weeks
# 4-5/6: Good — note gaps and address before next Game Day
# <4: Review experiment structure from Lesson 18 and repeatIf either experiment causes a cascade that does not self-recover within 5 minutes of the experiment ending, do not run Experiment 2 in the same session. Restore the system to steady state first, document the cascade as a finding, and investigate the root cause before scheduling the next Game Day. A cascading failure during a Game Day is a learning opportunity, not a failure of the Game Day process.
After your first Game Day, review the experiments against your runbooks from Module 4. If a Game Day experiment reveals a failure mode that is not covered by any runbook, create a new runbook for it before the next Game Day. Game Day findings and runbook coverage are complementary: Game Days discover failure modes, runbooks enable efficient response when those modes occur in production.
- Preflight checklist must pass before starting any Game Day experiment.
- Always record baseline metrics before injecting a fault — comparison requires a before state.
- Wait for full system recovery between experiments — back-to-back experiments can mask independent findings.
- Set duration on all Chaos Mesh experiments — no open-ended experiments that outlast the session.
- The Game Day report is the primary output — without documentation, the session produces no organisational learning.