跳转至

第19章 混沌工程

混沌工程图

学习时间: 2.5小时 难度级别: 高级 重要性: ⭐⭐⭐⭐ 构建弹性系统的关键实践


🎯 学习目标

完成本章后,你将能够: - 理解混沌工程的核心原则与方法论 - 对比主流混沌工程工具并选择合适方案 - 使用 Litmus 在 Kubernetes 中实施混沌实验 - 将混沌工程集成到 CI/CD 流水线 - 组织和执行 GameDay 演练


1. 混沌工程原则

1.1 什么是混沌工程?

混沌工程是一种通过主动注入故障来验证系统弹性的工程实践。其核心理念是:在可控环境中提前发现问题,而不是在生产事故中被动发现

"混沌工程是在分布式系统上进行实验的学科,目的是建立对系统抵御生产环境中湍流条件的能力的信心。" — Principles of Chaos Engineering

1.2 五大核心原则

Text Only
1. 建立稳态假设(Steady State Hypothesis)
   ├── 定义系统正常行为的可量化指标
   ├── 例:p99 延迟 < 200ms、错误率 < 0.1%、订单成功率 > 99.9%
   └── 实验前后对比这些指标来判断系统是否弹性

2. 用真实世界事件做变量
   ├── 模拟真实生产故障(而非理论化的场景)
   ├── 例:节点宕机、网络分区、磁盘满、依赖服务超时
   └── 参考历史事故记录选择实验类型

3. 在生产环境运行实验
   ├── 预生产环境可能无法复现生产问题
   ├── 生产实验需要更严格的安全措施
   └── 从非关键路径开始,逐步扩大范围

4. 持续自动化运行
   ├── 手动实验无法持续发现回归问题
   ├── 集成到 CI/CD 流水线自动执行
   └── 定期在生产环境执行(如每周一次)

5. 最小化爆炸半径(Minimize Blast Radius)
   ├── 从最小范围开始(单个 Pod → 单个节点 → 单个 AZ)
   ├── 设置自动回滚机制和超时时间
   ├── 确保有人值守和监控告警
   └── 有明确的终止条件和回滚流程

1.3 混沌实验流程

Text Only
┌─────────────────────────────────────────────────┐
│                混沌实验生命周期                    │
│                                                   │
│  1. 稳态假设   →  2. 注入故障  →  3. 观察行为     │
│  ┌──────────┐    ┌──────────┐    ┌──────────┐    │
│  │ 定义正常  │    │ 执行混沌  │    │ 监控指标  │    │
│  │ 行为指标  │    │ 实验     │    │ 对比基线  │    │
│  └──────────┘    └──────────┘    └──────────┘    │
│       │                                │          │
│       └────────── 4. 分析结论 ─────────┘          │
│                  ┌──────────┐                     │
│                  │ 通过/失败 │                     │
│                  │ 改进建议  │                     │
│                  └──────────┘                     │
└─────────────────────────────────────────────────┘

2. 工具对比

2.1 主流工具概览

工具 来源 平台支持 特点 适用场景
Chaos Monkey Netflix AWS 随机终止实例 EC2 弹性验证
Litmus CNCF Kubernetes 声明式实验、Hub 丰富 K8s 原生混沌
ChaosBlade 阿里巴巴 K8s/主机/JVM 中文友好、故障类型丰富 国内企业
Chaos Mesh PingCAP Kubernetes Dashboard 直观、与 TiDB 深度集成 K8s 故障注入
Gremlin SaaS 多平台 企业级 SaaS、安全功能强 企业级合规
AWS FIS AWS AWS 服务 与 AWS 深度集成 AWS 原生

2.2 选择建议

Text Only
决策矩阵:

Kubernetes 环境?
├── YES → 需要 CNCF 生态?
│         ├── YES → Litmus(CNCF 孵化项目)
│         └── NO  → 需要中文支持?
│                   ├── YES → ChaosBlade
│                   └── NO  → Chaos Mesh
├── AWS 环境?
│   └── YES → AWS FIS(原生集成)
└── 多平台/企业级?
    └── Gremlin(SaaS 方案)

3. Litmus 实战

3.1 Litmus 架构

Text Only
┌─────────────────────────────────────┐
│           Litmus Control Plane       │
│  ┌───────────┐  ┌────────────────┐  │
│  │ Litmus    │  │ ChaosCenter    │  │
│  │ API Server│  │ (Web UI)       │  │
│  └─────┬─────┘  └───────┬────────┘  │
│        │                 │           │
├────────┼─────────────────┼───────────┤
│        │    Execution     │          │
│  ┌─────▼─────────────────▼────┐     │
│  │   Chaos Infrastructure     │     │
│  │  (Subscriber + Runner)     │     │
│  └─────────────┬──────────────┘     │
│                │                     │
│  ┌─────────────▼──────────────┐     │
│  │     ChaosExperiment        │     │
│  │     ChaosEngine            │     │
│  │     ChaosResult            │     │
│  └────────────────────────────┘     │
└─────────────────────────────────────┘

3.2 安装 Litmus

Bash
# 安装 LitmusChaos(Helm)
helm repo add litmuschaos https://litmuschaos.github.io/litmus-helm/
helm install litmus litmuschaos/litmus \
  --namespace litmus --create-namespace \
  --set portal.frontend.service.type=NodePort

# 安装 Chaos Infrastructure(连接目标集群)
# 通过 ChaosCenter UI 添加,或使用 CLI:
litmusctl connect chaos-infra \
  --name="production-cluster" \
  --namespace="litmus" \
  --non-interactive

3.3 Pod Kill 实验

YAML
# pod-kill-experiment.yaml
apiVersion: litmuschaos.io/v1alpha1
kind: ChaosEngine
metadata:
  name: pod-kill-chaos
  namespace: default
spec:
  engineState: active
  appinfo:
    appns: default
    applabel: app=nginx
    appkind: deployment
  chaosServiceAccount: litmus-admin
  experiments:
    - name: pod-delete
      spec:
        components:
          env:
            # 强制删除(不等待优雅关闭)
            - name: FORCE
              value: "true"
            # 混沌持续时间(秒)
            - name: TOTAL_CHAOS_DURATION
              value: "60"
            # 每次杀死的 Pod 数量
            - name: PODS_AFFECTED_PERC
              value: "50"
            # 两次杀 Pod 之间的间隔
            - name: CHAOS_INTERVAL
              value: "10"
            # 序列模式:parallel 或 serial
            - name: SEQUENCE
              value: "parallel"
        probe:
          - name: check-app-availability
            type: httpProbe
            mode: Continuous
            httpProbe/inputs:
              url: "http://nginx-service:80"
              method:
                get:
                  criteria: ==
                  responseCode: "200"
            runProperties:
              probeTimeout: 5s
              retry: 3
              interval: 5s
---  # YAML文档分隔符
# 稳态假设验证
apiVersion: litmuschaos.io/v1alpha1
kind: ChaosResult
metadata:
  name: pod-kill-chaos-pod-delete
  namespace: default
# 实验完后检查此对象的 status

3.4 Network Chaos 实验

YAML
# network-chaos-experiment.yaml
apiVersion: litmuschaos.io/v1alpha1
kind: ChaosEngine
metadata:
  name: network-chaos
  namespace: default
spec:
  engineState: active
  appinfo:
    appns: default
    applabel: app=order-service
    appkind: deployment
  chaosServiceAccount: litmus-admin
  experiments:
    - name: pod-network-latency
      spec:
        components:
          env:
            # 网络延迟(ms)
            - name: NETWORK_LATENCY
              value: "500"
            # 延迟抖动(ms)
            - name: JITTER
              value: "100"
            # 目标容器
            - name: TARGET_CONTAINER
              value: "order-service"
            # 影响的网络接口
            - name: NETWORK_INTERFACE
              value: "eth0"
            # 目标端口(可选)
            - name: DESTINATION_PORTS
              value: "3306,6379"
            # 持续时间
            - name: TOTAL_CHAOS_DURATION
              value: "120"
        probe:
          - name: check-order-latency
            type: promProbe
            mode: Edge
            promProbe/inputs:
              endpoint: "http://prometheus:9090"
              query: >
                histogram_quantile(0.99,
                  rate(http_request_duration_seconds_bucket{
                    service="order-service"
                  }[1m])
                )
              comparator:
                type: float
                criteria: "<="
                value: "2.0"  # p99 <= 2s
            runProperties:
              probeTimeout: 10s
              interval: 15s

    - name: pod-network-loss
      spec:
        components:
          env:
            # 丢包率(百分比)
            - name: NETWORK_PACKET_LOSS_PERCENTAGE
              value: "30"
            - name: TOTAL_CHAOS_DURATION
              value: "60"
            - name: TARGET_CONTAINER
              value: "order-service"

3.5 Stress Chaos 实验

YAML
# stress-chaos-experiment.yaml
apiVersion: litmuschaos.io/v1alpha1  # apiVersion指定K8s API版本
kind: ChaosEngine  # kind指定资源类型
metadata:
  name: stress-chaos
  namespace: default
spec:  # spec定义资源的期望状态
  engineState: active
  appinfo:
    appns: default
    applabel: app=payment-service
    appkind: deployment
  chaosServiceAccount: litmus-admin
  experiments:
    - name: pod-cpu-hog
      spec:
        components:
          env:
            # CPU 核心数
            - name: CPU_CORES
              value: "2"
            # CPU 使用百分比(每个核心)
            - name: CPU_LOAD
              value: "80"
            - name: TOTAL_CHAOS_DURATION
              value: "120"
            - name: TARGET_CONTAINER
              value: "payment-service"
        probe:
          - name: check-payment-health
            type: httpProbe
            mode: Continuous
            httpProbe/inputs:
              url: "http://payment-service:8080/health"
              method:
                get:
                  criteria: ==
                  responseCode: "200"
            runProperties:
              probeTimeout: 3s
              retry: 2
              interval: 10s

    - name: pod-memory-hog
      spec:
        components:
          env:
            # 内存消耗量(MB)
            - name: MEMORY_CONSUMPTION
              value: "500"
            - name: TOTAL_CHAOS_DURATION
              value: "90"
            - name: TARGET_CONTAINER
              value: "payment-service"

4. 混沌工程与 CI/CD 集成

4.1 GitHub Actions 集成

YAML
# .github/workflows/chaos-testing.yml
name: Chaos Testing Pipeline

on:
  schedule:
    - cron: "0 2 * * 1"  # 每周一凌晨2点
  workflow_dispatch:       # 支持手动触发

jobs:
  chaos-test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Setup Kubernetes cluster
        uses: helm/kind-action@v1
        with:
          cluster_name: chaos-test

      - name: Deploy application
        run: |
          kubectl apply -f k8s/
          kubectl wait --for=condition=ready pod -l app=my-app --timeout=120s

      - name: Install Litmus
        run: |
          helm repo add litmuschaos https://litmuschaos.github.io/litmus-helm/
          helm install litmus litmuschaos/litmus --namespace litmus --create-namespace
          kubectl wait --for=condition=ready pod -l app=chaos-operator --timeout=60s -n litmus

      - name: Verify steady state (before)
        run: |
          # 记录基线指标
          RESPONSE_CODE=$(curl -s -o /dev/null -w "%{http_code}" http://localhost:8080/health)
          if [ "$RESPONSE_CODE" != "200" ]; then
            echo "❌ Pre-chaos health check failed"
            exit 1
          fi
          echo "✅ Pre-chaos steady state verified"

      - name: Run Pod Kill experiment
        run: |
          kubectl apply -f chaos-experiments/pod-kill.yaml
          # 等待实验完成
          kubectl wait --for=condition=complete chaosresult/pod-kill-chaos --timeout=300s

      - name: Verify steady state (after)
        run: |
          sleep 30  # 等待系统恢复
          RESPONSE_CODE=$(curl -s -o /dev/null -w "%{http_code}" http://localhost:8080/health)
          if [ "$RESPONSE_CODE" != "200" ]; then
            echo "❌ Post-chaos health check failed — system did not recover"
            exit 1
          fi
          echo "✅ System recovered successfully"

      - name: Collect chaos results
        if: always()
        run: |
          kubectl get chaosresult -o yaml > chaos-results.yaml
          kubectl logs -l app=chaos-operator -n litmus > chaos-operator.log

      - name: Upload results
        if: always()
        uses: actions/upload-artifact@v4
        with:
          name: chaos-results
          path: |
            chaos-results.yaml
            chaos-operator.log

4.2 渐进式混沌策略

Text Only
CI/CD 混沌注入策略:

Stage 1: 开发环境(每次 PR)
├── 基础健康检查
├── 单 Pod 重启验证
└── 通过后合并

Stage 2: 预发布环境(每次部署)
├── Pod Kill(单实例)
├── 网络延迟(100ms)
├── 验证服务降级机制
└── 通过后发布

Stage 3: 生产环境(每周定期)
├── Pod Kill(多实例,30%)
├── 网络延迟(500ms)+ 丢包(10%)
├── CPU/Memory 压力
├── 依赖服务故障
└── 全链路验证

Stage 4: GameDay(每月/每季度)
├── AZ 级别故障
├── 数据库故障转移
├── 全链路混沌
└── 事件响应流程演练

5. GameDay 演练流程

5.1 GameDay 完整流程

Text Only
┌──────────────────────────────────────────────────┐
│                GameDay 演练流程                    │
│                                                    │
│  Phase 1: 准备(1-2周前)                          │
│  ├── 确定演练目标和范围                             │
│  ├── 设计实验场景(基于风险评估)                    │
│  ├── 准备监控和告警                                │
│  ├── 通知相关团队和利益相关者                        │
│  ├── 准备回滚方案和紧急联系人                       │
│  └── 安排值班人员                                  │
│                                                    │
│  Phase 2: 执行(当天)                              │
│  ├── 09:00 集合,确认所有人就位                      │
│  ├── 09:15 验证稳态基线指标                         │
│  ├── 09:30 开始注入第1轮故障                        │
│  ├── 10:00 观察系统行为,记录异常                    │
│  ├── 10:30 验证自动恢复和告警                       │
│  ├── 11:00 注入第2轮更大范围故障                    │
│  ├── 11:30 事件响应团队演练                         │
│  ├── 12:00 停止实验,验证系统恢复                   │
│  └── 如遇紧急情况 → 立即执行回滚                    │
│                                                    │
│  Phase 3: 复盘(1-3天内)                           │
│  ├── 收集实验数据和监控截图                          │
│  ├── 分析系统表现 vs 预期                           │
│  ├── 识别弱点和改进项                               │
│  ├── 按优先级排列修复任务                            │
│  ├── 编写 GameDay 报告                              │
│  └── 分享经验到全公司                               │
└──────────────────────────────────────────────────┘

5.2 GameDay 报告模板

Markdown
# GameDay 报告 — [日期]

## 基本信息
- **日期**: 2026-01-15
- **参与团队**: SRE、后端、运维、DBA
- **演练环境**: 生产环境(东区)
- **持续时间**: 09:00 - 12:00

## 实验场景
| # | 场景 | 预期影响 | 实际影响 | 结果 |
|---|------|---------|---------|------|
| 1 | 杀死 50% API Pod | 延迟升高,无错误 | p99 从 150ms → 280ms,无错误 | ✅ 通过 |
| 2 | 数据库主从切换 | < 5s 中断 | 8s 中断,3个超时请求 | ⚠️ 部分通过 |
| 3 | Redis 集群 1/3 节点下线 | 自动故障转移 | 故障转移成功但耗时 12s | ⚠️ 部分通过 |

## 发现的问题
1. **P1 - 数据库切换超时**:连接池未配置自动重连(负责人:@DBA团队,截止:01/22)
2. **P2 - Redis 故障转移慢**:Sentinel 超时配置过长(负责人:@SRE,截止:01/29)
3. **P3 - 告警延迟**:CPU 告警在故障后 3 分钟才触发(负责人:@运维,截止:02/05)

## 下次改进
- 扩大爆炸半径到跨 AZ 故障
- 增加消息队列故障场景

6. 面试题精选

面试题 1:什么是混沌工程?它与传统测试有什么区别?

参考答案: - 混沌工程通过主动注入故障验证系统弹性,传统测试验证功能正确性 - 混沌工程关注未知的未知(系统在非预期条件下的行为),测试关注已知的场景 - 混沌工程在接近生产的环境中运行,传统测试通常在测试环境 - 混沌工程是持续性活动,不是一次性测试

面试题 2:解释"最小化爆炸半径"原则

参考答案: - 从最小范围开始实验(单 Pod → 单节点 → 单 AZ → 多 AZ) - 设置自动终止条件(超时、错误率阈值触发回滚) - 确保有回滚机制(一键停止实验并恢复系统) - 有人值守:实验期间必须有人监控系统 - 逐步增加故障强度,确认系统在每个级别都能恢复

面试题 3:混沌工程中的"稳态假设"是什么?

参考答案: - 稳态假设是对系统正常行为的可量化定义 - 例:p99 延迟 < 200ms、错误率 < 0.1%、成功率 > 99.9% - 实验前测量基线,实验后对比——若指标偏离超过阈值,实验"失败"(发现了问题) - 好的稳态假设应该是面向业务的(订单成功率),而非纯技术的(CPU 使用率)

面试题 4:如何将混沌工程集成到 CI/CD?

参考答案: 1. 预发布阶段:部署后自动运行轻量混沌实验(Pod Kill、短暂网络延迟) 2. 定期触发:使用 cron 或调度器每周在生产环境运行 3. 渐进策略:开发环境 → 预发布 → 生产,故障强度逐级升级 4. 自动验证:实验后自动检查稳态假设,失败则阻止发布或告警 5. 结果归档:将实验结果存入 artifact,便于回溯和趋势分析


7. 检查清单

混沌工程原则

  • 理解混沌工程的五大核心原则
  • 能为系统定义合理的稳态假设
  • 理解最小化爆炸半径的实践方法

工具使用

  • 了解主流混沌工程工具的特点和适用场景
  • 能安装和配置 Litmus
  • 能编写 Pod Kill、Network Chaos、Stress Chaos 实验 YAML
  • 理解 Probe 机制用于自动验证稳态假设

CI/CD 集成

  • 能将混沌实验集成到 GitHub Actions
  • 理解渐进式混沌策略(开发 → 预发布 → 生产)
  • 能设计自动化的实验结果验证流程

GameDay

  • 理解 GameDay 的完整流程(准备 → 执行 → 复盘)
  • 能编写 GameDay 报告并追踪改进事项
  • 了解如何逐步扩大 GameDay 的范围

上一章: 18-eBPF与可观测性 返回目录: README