云原生混沌工程实战:基于Litmus的故障注入与弹性测试

微服务上云后的"黑天鹅":为什么需要混沌工程?

当微服务从开发环境迁移到生产 Kubernetes 集群后,我们往往依赖 Prometheus 告警和熔断降级来应对故障。但**"已知的未知"并不可怕,真正致命的是"未知的未知"**------比如某个依赖的 Redis 连接池耗尽后引发雪崩,或者网络抖动导致分布式事务超时。混沌工程的核心思想,正是主动制造故障,验证系统在真实异常下的弹性边界。

本文基于 Litmus(CNCF 沙箱项目)在 K8s 中实施混沌工程,覆盖从实验设计、自动化编排到可观测性闭环的全流程,并给出生产环境的最小爆炸半径策略。所有 YAML 和命令均可在 K8s 1.24+ 上直接运行。


1. Litmus 架构与工作流:从实验定义到结果报告

1.1 核心组件

Litmus 采用 Operator 模式,主要组件包括:

  • Chaos-Operator :监听自定义资源 ChaosExperiment 和 ChaosEngine,负责调度实验 Job。
  • Chaos-Exporter:将实验指标(如注入时长、目标 Pod 状态)暴露为 Prometheus metrics。
  • Litmus Portal(可选):Web UI,用于可视化管理实验和查看报告。

1.2 工作流

  1. 用户定义 ChaosExperiment(实验模板)和 ChaosEngine(实验实例,指定目标、参数)。
  2. Operator 根据 Engine 创建 Job,Job 在目标 Pod 的同一节点上运行 sidecar 或直接执行 chaos 二进制。
  3. 实验完成后,结果写入 ChaosResult 自定义资源,并可通过 Prometheus 采集。

关键点 :ChaosExperiment 是全局模板(ClusterScope),ChaosEngine 是命名空间级别的实例化,允许复用实验逻辑。

1.3 快速安装

bash 复制代码
# 使用 Helm 安装 Litmus
helm repo add litmuschaos https://litmuschaos.github.io/litmus-helm/
helm install chaos litmuschaos/litmus --namespace=litmus --create-namespace

# 验证组件
kubectl get pods -n litmus -l app.kubernetes.io/component=chaos-operator

2. 故障注入实验设计:四类典型故障

我们以一个订单服务(order-service,3 副本)为例,分别演示 Pod 删除、网络延迟、CPU 压力、HTTP 错误注入。

2.1 Pod 删除(模拟节点宕机或滚动更新失败)

原理 :Litmus 的 pod-delete 实验通过直接删除目标 Pod 并观察 Deployment 的 ReplicaSet 重建能力。

yaml 复制代码
# pod-delete-experiment.yaml
apiVersion: litmuschaos.io/v1alpha1
kind: ChaosEngine
metadata:
  name: order-pod-delete
  namespace: default
spec:
  appinfo:
    appns: default
    applabel: "app=order-service"
    appkind: deployment
  chaosServiceAccount: litmus-sa
  experiments:
    - name: pod-delete
      spec:
        components:
          env:
            # 设置删除比例,50% 的 Pod 会被删除
            - name: TOTAL_CHAOS_DURATION
              value: "30"          # 秒
            - name: CHAOS_INTERVAL
              value: "10"          # 每次删除间隔
            - name: FORCE
              value: "true"        # 强制删除 (等同于 kubectl delete --force)
            - name: RAMP_TIME
              value: "10"          # 实验前等待 10s 让监控数据稳定

注意事项 :

  • 如果 Pod 数量为 3,TOTAL_CHAOS_DURATION=30 且 CHAOS_INTERVAL=10,则实验期间会删除 3 个 Pod(每个 10s 间隔)。

  • 生产环境必须设置 TARGET_PODS 指定最大删除数,避免无限制删除导致服务完全不可用。

错误写法(未限制范围):

yaml 复制代码
# 错误示范:没有 TARGET_PODS,可能导致全部 Pod 被删除
- name: TOTAL_CHAOS_DURATION
  value: "300"

正确做法:

yaml 复制代码
- name: TARGET_PODS
  value: "1"   # 每次最多删除 1 个 Pod,保障最小副本数

2.2 网络延迟(模拟跨 AZ 或 Service Mesh 抖动)

使用 pod-network-latency 实验,在指定 Pod 的网卡上注入延迟。

yaml 复制代码
# network-latency.yaml
spec:
  experiments:
    - name: pod-network-latency
      spec:
        components:
          env:
            - name: NETWORK_LATENCY
              value: "2000"        # 延迟 2000ms
            - name: TARGET_CONTAINER
              value: "order-service"
            - name: NETWORK_INTERFACE
              value: "eth0"
            - name: TOTAL_CHAOS_DURATION
              value: "60"
            - name: JITTER
              value: "500"         # 抖动 500ms,模拟真实网络不稳定

效果验证:实验期间调用订单接口,P99 延迟从基线 35ms 飙升到 2.5s,之后恢复正常。若服务未配置超时重试,则会出现大量 5xx 错误。

2.3 CPU 压力(模拟突增计算任务或容器资源限制不足)

yaml 复制代码
# cpu-stress.yaml
spec:
  experiments:
    - name: pod-cpu-hog
      spec:
        components:
          env:
            - name: CPU_CORE
              value: "0.5"         # 占用 0.5 核
            - name: TOTAL_CHAOS_DURATION
              value: "120"
            - name: TARGET_CONTAINER
              value: "order-service"
            # 注意:若 Pod 的 resource.limits.cpu 为 1,注入 0.5 会占用 50% 分配

踩坑点 :

  • CPU_CORE 是绝对值,不是百分比。如果节点只有 2 核,设置 2 会导致整个节点 CPU 被打满,影响其他 Pod。

  • 生产环境建议使用 STRESSOR_ARGS 配合 --cpu 1 --timeout 60 更精细控制。

对比 :pod-cpu-hog vs pod-cpu-hog-exec(后者通过 exec 在容器内执行 stress 命令,无需额外镜像)。对于有 securityContext 限制的容器,优先使用 exec 模式。

2.4 HTTP 错误注入(模拟下游服务返回 500)

使用 pod-http-status 实验,拦截目标 Pod 的指定 HTTP 请求并返回自定义状态码。

yaml 复制代码
# http-error.yaml
spec:
  experiments:
    - name: pod-http-status
      spec:
        components:
          env:
            - name: TARGET_SERVICE_PORT
              value: "8080"
            - name: STATUS_CODE
              value: "500"
            - name: RESPONSE_HEADERS
              value: "Content-Type: application/json"
            - name: ROUTE
              value: "/api/orders"  # 只对 /api/orders 路径生效
            - name: TARGET_CONTAINER
              value: "order-service"

实现原理 :Litmus 使用 ebpf 或 iptables (取决于内核版本)劫持流量,在应用层返回错误,无需修改代码。

关键注意事项 :ROUTE 必须与真实请求路径完全匹配,否则劫持失败。可搭配 TARGET_PODS 限制影响范围。


3. 实验编排与自动化:基于 GitOps 的混沌实验流水线

手动执行 ChaosEngine 无法在生产环境常态化。我们需要将混沌实验视为"基础设施即代码",通过 GitOps 工具(如 ArgoCD)自动同步,并集成 CI/CD 触发。

3.1 Git 仓库结构

复制代码
chaos-experiments/
├── engines/
│   ├── prod-pod-delete.yaml       # 生产环境,TARGET_PODS=1
│   ├── prod-network-latency.yaml
│   └── staging-all-experiments.yaml
├── experiments/                    # 复用 ChaosExperiment 模板
│   ├── pod-delete.yaml
│   └── network-latency.yaml
├── dashboards/                     # Grafana 面板 JSON
└── .argocd-app.yaml

3.2 ArgoCD 自动同步

yaml 复制代码
# .argocd-app.yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: chaos-experiments
spec:
  destination:
    namespace: litmus
    server: https://kubernetes.default.svc
  project: default
  source:
    path: chaos-experiments/engines
    repoURL: https://github.com/your-team/chaos-gitops.git
    targetRevision: main
  syncPolicy:
    automated: {}

改进点 :将 ChaosEngine 的 spec.schedule 字段设置为 Cron 语法,实现定期混沌实验(如每周三凌晨 3 点执行 15 分钟低风险实验)。但注意 Cron 模式只支持 Litmus 3.0+。

3.3 集成 CI 触发(仅对 staging 环境)

在 Jenkins/GitLab CI 中,可以通过 kubectl apply 临时创建 ChaosEngine,并等待 ChaosResult 状态:

bash 复制代码
# CI 脚本
kubectl apply -f chaos-engine-staging.yaml
# 等待实验完成(最多 5 分钟)
kubectl wait --for=condition=Complete chaosresult/order-pod-delete --timeout=300s
if [ $? -ne 0 ]; then
  echo "Chaos experiment failed! Failing build."
  exit 1
fi

4. 弹性验证与可观测性:结合 Prometheus/Grafana 分析 SLO 影响

实验本身没有价值,价值在于衡量故障对业务 SLO 的影响。我们需要同时采集 Litmus 指标和应用性能指标。

4.1 关键指标

指标来源 Prometheus Metric 含义
Litmus Exporter litmus_experiment_verdict 实验通过/失败
Istio/Envoy (若用 Service Mesh) istio_request_duration_milliseconds_bucket 请求延迟分布
K8s Metrics Server kube_pod_status_ready Pod 就绪数
Application Metrics http_requests_total 业务 QPS

4.2 自定义 Grafana 面板

分析逻辑 :

  1. 在面板上叠加 Litmus 实验时间窗口 (通过 litmus_experiment_start_time 和 litmus_experiment_end_time 计算)。

  2. 对比异常窗口前后的 P99 延迟、错误率、QPS。

示例 PromQL(计算实验期间的错误率变化):

promql 复制代码
# 错误率(5xx 占总请求比例)
sum(rate(http_requests_total{code=~"5.."}[1m])) / sum(rate(http_requests_total[1m]))

典型数据 (来自生产环境模拟):

| 实验类型 | 基线错误率 | 峰值错误率 | 恢复时间 | P99 延迟峰值 |

|---------|-----------|-----------|---------|------------|

| Pod 删除 | 0.01% | 0.5% | 20s | 120ms |

| 网络延迟 2s | 0.01% | 32% | 60s | 2500ms |

| CPU 压力 0.5核 | 0.01% | 1.2% | 90s | 800ms |

分析结论 :网络延迟实验暴露出下游服务的 重试风暴 和 超时设置过短,导致大量 5xx。优化后将超时从 1s 改为 3s,并增加指数退避,再实验时峰值错误率降至 2.3%。


5. 生产环境最小爆炸半径策略

放任混沌实验随意运行是对生产的不负责。以下是必须遵守的策略:

5.1 实验范围限制

  • 命名空间过滤 :在 ChaosEngine 中通过 spec.appinfo.appns 指定单个命名空间,或使用 AUXILIARY_APPINFO 排除关键服务(如 etcd、kube-system)。
  • Pod 选择器黑名单 :添加环境变量 TARGET_PODS 限制最大数量,配合 PODS_AFFECTED_PERC(百分比)双重保险。
  • 时间窗口 :结合 SERVICEACCOUNT 的 RBAC,只允许在 01:00-04:00 创建 ChaosEngine(通过 OPA Gatekeeper 或 Kyverno)。

5.2 自动中止条件(Steady State Hypothesis)

Litmus 3.0 引入了 Probes 机制,可以在实验持续期间检查系统状态,一旦违反假设立即中止实验。

yaml 复制代码
# 在 ChaosEngine 中添加 probes
spec:
  experiments:
    - name: pod-network-latency
      probes:
        - name: error-rate-probe
          type: PromProbe
          mode: Continuous   # 持续检查
          runProperties:
            probeTimeout: 5s
            retry: 0
          promProbe/inputs:
            query: "sum(rate(http_requests_total{code=~\"5..\"}[1m])) / sum(rate(http_requests_total[1m])) > 0.05"
            comparator:
              type: float
              criteria: equals  # 如果错误率 >5% 则 fail
              value: "1"
          promProbe/data:
            # 需要提供 Prometheus 地址
            serverUrl: "http://prometheus-server.monitoring:9090"

工作原理 :当 Probe 返回 true(即错误率 >5%),Litmus 立即标记实验为 Failed,并执行 REMEDIATION_ACTION(默认终止实验并恢复)。

5.3 安全审计与回滚

  • 使用 Litmus Portal 的 审计日志 记录谁在何时执行了什么实验。
  • 所有 ChaosEngine 必须有对应的 rollback 策略:对于 Pod 删除这类无状态实验,Debployment 自动回滚;对于网络延迟,Litmus 会在实验结束后自动清除 iptables 规则。

总结

  • Litmus 的核心优势:声明式 API + 可编程探针,能嵌入 GitOps 和 CI/CD,实现"混沌即代码"。
  • 落地必须做的三件事 :
    1. 为每个实验配置 TARGET_PODS 和 PODS_AFFECTED_PERC 双限制。
    2. 在 Prometheus 上建立 SLO 仪表盘,并配置 Probe 自动中止条件。
    3. 先从小范围(单副本、非核心服务)开始,稳定后再扩大至 2 副本和关键业务。
  • 常见踩坑 :网络延迟实验中 JITTER 值过大导致容器内进程卡死;CPU_CORE 超过节点容量引发节点 NotReady;ROUTE 路径不匹配导致劫持失效。

混沌工程不是破坏,而是用科学实验验证系统的韧性。当生产真的发生故障时,你会感谢这些提前暴露的"已知未知"。

相关推荐
分布式存储与RustFS5 天前
MinIO 官方 Docker 镜像被移除:依赖它的项目该怎么办
docker·云原生·devops·对象存储·minio·分布式存储
codeejun5 天前
每日一Go·MySQL-5、锁机制全解析
云原生·golang
赵文宇(温玉)5 天前
CoreDNS的hosts插件的缺行配置导致k8s集群异常
容器·kubernetes·rancher
张洛闻Eren5 天前
k8s云原生【第十课】:水平 Pod 自动扩缩容
运维·数据库·云原生·kubernetes·github
Thneonl5 天前
你的 Pod 为什么 Pending:调度器两阶段
后端·kubernetes
不吃香菜kkk、5 天前
CI/CD(GitOps)学习与部署手册
运维·云原生·容器·kubernetes·云计算·jenkins·argocd
ChaITSimpleLove5 天前
云原生性能对决:JDK21虚拟线程+GraalVM vs .NET10/11 技术栈深度对比
微服务·云原生·serverless·graalvm native·native aot·runtime async·.net11/java21
あ-5 天前
python3 ModuleNotFoundError: No module named ‘_sqlite3‘
devops
研发小能5 天前
制造行业 DevOps 国产替代怎么落地?面向离散制造与汽车集团的横向比较
研发效能·devops·嘉为蓝鲸
春天花会开1316 天前
Supabase 自部署 Edge Functions 超时从 60s 延长到 360s 完整复盘
运维·云原生