8.7 混沌工程入门——Chaos Mesh 与 Litmus 实战
预计阅读时间:15 分钟
📖 目录
6.2:Kubernetes 入门 搭建了 K8s 集群,6.15:HA 集群 保障了高可用。但生产环境永远有意外——节点宕机、网络抖动、磁盘满。混沌工程通过在受控环境中主动注入故障,验证系统的韧性,在真正事故发生前发现问题。
学习目标
- 理解"建立稳态假设 → 注入故障 → 观察反应 → 持续验证"的混沌工程循环
- 能对比 Chaos Mesh 与 Litmus 的定位与能力,并做出选型
- 掌握 PodChaos、NetworkChaos、StressChaos 等故障注入资源的编写
- 能用 ChaosWorkflow 编排多步骤的混沌实验
- 能通过爆炸半径、deadline 与 staging 先行控制实验风险
- 能结合监控指标评估系统韧性并沉淀韧性手册
前置知识
- 6.2:Kubernetes 入门 Kubernetes 入门——Pod、Deployment 与集群基础操作
- 6.15:HA 集群 Linux 高可用集群——HA 架构与故障恢复机制
- 4.7:压力测试实战 压力测试实战——负载压测与性能指标基线
- 6.1:容器底层原理 容器底层原理——容器运行时、网络与存储隔离机制
混沌工程原则
- 建立稳态假设:先定义正常状态的指标(延迟 P99 < 200ms、错误率 < 1%)
- 注入真实故障:模拟生产中实际会发生的问题
- 观察系统反应:监控指标是否偏离稳态
- 持续验证:故障恢复后指标应回到稳态
方案对比
| Chaos Mesh | Litmus Chaos | |
|---|---|---|
| CNCF 阶段 | 孵化项目 | 毕业项目 |
| 故障类型 | Pod/网络/文件系统/JVM/HTTP | Pod/节点/网络/IO/Chaos Hub |
| 实验编排 | Chaos Dashboard + CRD | ChaosHub + Workflow |
| 多集群 | 支持 | 支持 |
| 学习曲线 | 低 | 中 |
1. Chaos Mesh 安装
# 安装 Chaos Mesh
helm repo add chaos-mesh https://charts.chaos-mesh.org
helm install chaos-mesh chaos-mesh/chaos-mesh \
-n chaos-mesh --create-namespace \
--set chaosDaemon.runtime=containerd \
--set chaosDaemon.socketPath=/run/containerd/containerd.sock
# 验证
kubectl get pods -n chaos-mesh
# 访问 Dashboard
kubectl port-forward -n chaos-mesh svc/chaos-dashboard 2333:2333
# 浏览器打开 http://localhost:2333
2. 常见故障注入
Pod 故障——杀 Pod
# 随机杀掉 1 个 frontend Pod
apiVersion: chaos-mesh.org/v1alpha1
kind: PodChaos
metadata:
name: pod-kill
namespace: default
spec:
action: pod-kill
mode: one
selector:
namespaces: [default]
labelSelectors:
app: frontend
scheduler:
cron: "@every 5m" # 每 5 分钟执行一次
网络故障——延迟注入
# 给 backend 服务注入 200ms 网络延迟
apiVersion: chaos-mesh.org/v1alpha1
kind: NetworkChaos
metadata:
name: network-delay
spec:
action: delay
mode: all
selector:
namespaces: [default]
labelSelectors:
app: backend
delay:
latency: "200ms"
jitter: "50ms"
correlation: "50"
direction: to
target:
selector:
labelSelectors:
app: database
mode: all
网络故障——丢包
# 给指定 Pod 注入 30% 丢包率
apiVersion: chaos-mesh.org/v1alpha1
kind: NetworkChaos
metadata:
name: network-loss
spec:
action: loss
mode: one
selector:
namespaces: [default]
labelSelectors:
app: payment
loss:
loss: "30"
correlation: "50"
磁盘故障——填充磁盘
# 填充 /var/lib/docker 目录 5GB
apiVersion: chaos-mesh.org/v1alpha1
kind: StressChaos
metadata:
name: disk-fill
spec:
mode: one
selector:
namespaces: [default]
labelSelectors:
app: worker
stressors:
memory:
workers: 1
size: "5GB"
stressngStressors: "--hdd 1 --hdd-bytes 5G --hdd-dir /var/lib/docker"
CPU 压力
# 消耗 2 个 CPU 核心
apiVersion: chaos-mesh.org/v1alpha1
kind: StressChaos
metadata:
name: cpu-stress
spec:
mode: all
selector:
namespaces: [default]
labelSelectors:
app: api
stressors:
cpu:
workers: 2
load: 80
I/O 故障——磁盘读写延迟与错误
# 让 worker 的 /data 目录写入延迟 50ms、每 10 次写 1 次失败
apiVersion: chaos-mesh.org/v1alpha1
kind: IOChaos
metadata:
name: io-fault
spec:
action: latency
mode: one
selector:
namespaces: [default]
labelSelectors:
app: worker
volumePath: /data
path: "/var/lib/mysql/**"
delay: "50ms"
percent: 100
duration: "5m"
errno: 5
# 指定路径 100% 注入 EIO 错误(模拟磁盘损坏)
apiVersion: chaos-mesh.org/v1alpha1
kind: IOChaos
metadata:
name: io-error
spec:
action: fault
mode: all
selector:
labelSelectors:
app: worker
volumePath: /data
path: "/data/**"
percent: 100
errno: 5 # EIO
HTTP 故障——接口延迟与返回码篡改
# 对 frontend 的 /api 路径注入 500 错误,占比 50%
apiVersion: chaos-mesh.org/v1alpha1
kind: HTTPChaos
metadata:
name: http-500
spec:
mode: one
selector:
namespaces: [default]
labelSelectors:
app: frontend
target: Request
port: 8080
path: "/api/**"
abort: true
code: 500
duration: "3m"
时间故障——时钟偏移
# 将认证服务的时间向前拨 30 分钟,验证 JWT/会话是否过期
apiVersion: chaos-mesh.org/v1alpha1
kind: TimeChaos
metadata:
name: clock-skew
spec:
mode: one
selector:
labelSelectors:
app: auth
timeOffset: "-30m"
clockIds: ["CLOCK_REALTIME"]
故障注入类型一览
| 类型 | 资源 | 典型动作 | 适用验证场景 |
|---|---|---|---|
| Pod 故障 | PodChaos | pod-kill / pod-failure / container-kill | Deployment 副本自愈、重启策略 |
| 网络故障 | NetworkChaos | delay / loss / duplicate / corrupt / partition | 超时重试、熔断降级 |
| 磁盘 I/O | IOChaos | latency / fault / attrOverride | 存储层限流、慢盘降级 |
| 压力注入 | StressChaos | CPU / 内存 / 磁盘压满 | OOMKilled、驱逐策略、容量上限 |
| HTTP 故障 | HTTPChaos | abort / delay / replace body | API 网关容错、重试语义 |
| 时间偏移 | TimeChaos | 时钟快进/回拨 | Token 过期、证书校验、定时任务 |
| DNS 故障 | DNSChaos | 域名解析错误/随机 IP | 服务发现依赖、DNS 缓存 |
3. 实验编排——ChaosWorkflow
# 先杀 Pod,等 10 秒,再注入网络延迟,观察恢复
apiVersion: chaos-mesh.org/v1alpha1
kind: Workflow
metadata:
name: api-resilience-test
spec:
entry: api-chaos
templates:
- name: api-chaos
templateType: Serial
children:
- step-kill-pod
- step-network-delay
- step-observe
- name: step-kill-pod
templateType: PodChaos
embed:
apiVersion: chaos-mesh.org/v1alpha1
kind: PodChaos
metadata:
name: step-kill-pod
spec:
action: pod-kill
mode: one
selector:
labelSelectors:
app: api
- name: step-network-delay
templateType: Schedule
embed:
apiVersion: chaos-mesh.org/v1alpha1
kind: Schedule
metadata:
name: step-delay
spec:
schedule: "@every 1m"
concurrencyPolicy: Forbid
type: NetworkChaos
networkChaos:
action: delay
mode: all
selector:
labelSelectors:
app: api
delay:
latency: "500ms"
Workflow 进阶:并行阶段与恢复模板
# Parallel:同一时刻对多个目标注入(验证链路同时故障)
- name: multi-target-parallel
templateType: Parallel
children:
- step-backend-delay
- step-db-loss
# 注意:Parallel 内每个子步骤的 deadline 要一致,
# 否则先结束的故障恢复、后结束的还在注入,无法对比
# Suspend:在步骤之间暂停固定时长(等待系统收敛再注入下一步)
- name: wait-settle
templateType: Suspend
duration: 30s
# 失败处理:Serial 中某一步失败时后续步骤是否继续
# 默认失败即中断——推荐保持该行为:注入失败说明环境已异常,
# 继续注入只会把问题搅浑,先止损再排查
4. 混沌实验 Checklist
| 实验 | 故障类型 | 验证指标 |
|---|---|---|
| Pod 故障恢复 | 随机杀 Pod | Deployment 自动恢复,服务可用 |
| 节点故障 | 模拟节点 NotReady | Pod 被驱逐到其他节点 |
| 网络分区 | 跨可用区网络中断 | 服务降级但不崩溃 |
| 数据库故障 | 主库宕机 | 自动切换到从库 |
| 磁盘满 | 填满磁盘空间 | 日志不丢失,服务优雅降级 |
| 时钟偏移 | NTP 同步异常 | 证书/Token 不过期 |
5. 安全边界
- 先在 staging 执行,观察一段时间后再上生产
- 设置爆炸半径:通过
mode: one或mode: fixed(1)限制影响范围 - 配置自动恢复:Chaos Mesh 实验默认在 deadline 后自动停止
- 禁止在无备份时注入数据库故障
- 监控告警:实验期间保持 Prometheus + Grafana 监控面板打开
6. Litmus 完整案例
Litmus 的核心概念是 ChaosExperiment(故障定义模板,多来自 ChaosHub)+ ChaosEngine(实验实例,绑定目标应用)。实验由 chaos-operator 调度,通过 RBAC 限定的 ServiceAccount 执行。
# 1. 安装 Litmus(Helm 方式)
helm repo add litmuschaos https://litmuschaos.github.io/litmus-helm/
helm install litmus litmuschaos/litmus --namespace litmus --create-namespace
# 2. 从 ChaosHub 安装 pod-delete 实验模板
kubectl apply -f https://hub.litmuschaos.io/api/chaos/2.16.0?file=charts/generic/pod-delete/experiment.yaml
# 3. 定义 ChaosEngine:只对 payment 应用的 Pod 生效
apiVersion: litmuschaos.io/v1alpha1
kind: ChaosEngine
metadata:
name: payment-pod-delete
namespace: default
spec:
appinfo:
appns: default
applabel: "app=payment"
appkind: deployment
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"
# 只允许在 22:00-06:00 之外执行(安全边界)
annotationCheck: "true"
执行流程:kubectl apply -f engine.yaml 后,operator 监听 ChaosEngine 变更,创建 ChaosRun 与对应 Job;Job 内的 experiment pod 对目标 Deployment 注入 pod-delete;期间 Litmus 通过 litmus-check 观察应用恢复;实验结束生成 ChaosResult 记录结果。
# 4. 查看实验结果
kubectl get chaosresult payment-pod-delete-pod-delete -o jsonpath='{.status.experimentStatus.verdict}'
# 输出 Pass / Fail / Stopped
# 5. 查询实验事件详情
kubectl describe chaosengine payment-pod-delete
7. 实验编排与设计原则
混沌实验不是随机破坏,而是带着假设的验证工程。设计一个实验前先回答三个问题:系统正常时的稳态指标是什么?我要验证哪个韧性假设?最坏情况下爆炸半径多大?
- 单一变量:一次实验只注入一类故障,否则无法定位根因;多条链路要验证时拆成 Workflow 的串行步骤
- 从组件到链路:先验证单组件自愈(Pod 重启),再验证链路韧性(网络分区下的重试与降级),最后才做跨区域演练
- 最小爆炸半径:优先
mode: one/fixed(1),用 labelSelectors 精确圈定目标 Pod 而非整个 Deployment;生产环境先跑 1% 流量再逐步放大 - 预设终止条件:每个实验必须携带
duration或deadline,并设定自动熔断指标(如错误率 > 5% 立即停止)
一个完整实验的前中后步骤:
# 实验前(T-30min)
# 1. 检查变更窗口:确认无发布、无迁移任务在跑
# 2. 备份稳态基线:Prometheus 导出最近 24h 的 P99 延迟与错误率
# 3. 通知干系人,准备好停止开关(kill ChaosResource / 撤销 CRD)
# 实验中
# 4. 按预设顺序注入,每个阶段停留 2-3 个观察周期
# 5. 观察指标偏离,记录现象时间线与 SLO 违反情况
# 实验后
# 6. 等待 deadline 自动恢复,或手动删除 ChaosResource
# 7. 确认指标回归基线,输出实验报告并决定是否修复代码
8. 安全边界与演练隔离
- 白名单机制:用
--set controllerManager.allowedNamespaces(Chaos Mesh)限定故障注入只允许在指定 Namespace 执行;Litmus 通过 RBAC 的chaosServiceAccount只授予目标应用的权限 - 演练环境隔离:生产演练优先选择独立 Namespace、独立 Service 与独立的降级链路(如影子流量副本);数据面与应用面都连接到演练专用实例,避免误伤真实用户
- 明确终止条件:除 CRD 自带的
duration外,设置人工熔断开关——kubectl delete podchaos --all -n prod应能在 30 秒内清空所有注入;演练前把该命令写进 runbook - 审计与回溯:Chaos Mesh Dashboard 的审计日志与 K8s Event 保留注入历史,出事故时可快速定位"谁在何时注入了什么故障"
- 禁止注入清单:有真实备份任务的节点、承载 DB 主库的 Pod、未做演练审批的敏感系统,列入 Namespace/标签级禁止名单
熔断开关与演练审批
# 演练前把这三条命令写进 runbook,任何异常 30 秒内止损:
kubectl delete podchaos -A --all # 立即删除所有 Pod 注入
kubectl delete networkchaos -A --all # 立即删除所有网络注入
kubectl delete workflow -n prod --all # 停止所有编排中的实验
# 演练审批单(git issue 模板)至少包含:
# 目标组件 / 注入类型与参数 / 计划窗口(避开发布与备份)
# 稳态基线(P99、错误率)/ 熔断指标(如错误率 > 5% 即停)
# 干系人通知列表 / 恢复验证人
审批不是形式主义——它强迫演练负责人提前回答「出了事怎么办」,这本身就是演练的一部分:如果熔断命令写不出来,说明这个实验还没准备好执行。
9. 指标验证与结果分析
混沌实验的价值在于可量化的对比。以"backend → database 网络延迟 +200ms"实验为例,注入前后采集同一组指标:
| 指标 | 注入前基线 | 注入期间 | 恢复后 | 结论 |
|---|---|---|---|---|
| P99 延迟 | 120ms | 380ms | 125ms | 未超 SLO 500ms,通过 |
| 错误率 | 0.2% | 1.8% | 0.3% | 重试机制生效,未级联 |
| 可用 Pod 数 | 6 | 6 | 6 | 副本无驱逐,通过 |
| 数据库连接池占用 | 40% | 92% | 45% | 接近上限,需调优连接池 |
# 用 PromQL 拉取注入前后对比(注入时间为 15:00-15:05)
# 延迟:avg by (pod) (rate(http_request_duration_seconds_sum[1m])) /
# avg by (pod) (rate(http_request_duration_seconds_count[1m]))
# 错误率:sum(rate(http_requests_total{status=~"5.."}[1m])) /
# sum(rate(http_requests_total[1m]))
# 生成对比报告的关键字段:实验 ID、注入类型、目标、起止时间、
# 基线值、峰值、恢复时长、SLO 是否违反、遗留问题、修复建议
结果分析的原则:故障恢复不等于实验通过——若恢复依赖人工介入或超时时间过长,说明韧性假设不成立;把每次实验的"峰值 / 恢复时长 / 违反的 SLO"沉淀进韧性手册,作为下一次实验的基线。
实验报告与韧性指标沉淀
# 每次实验固定输出一份报告,字段固定,便于横向对比:
# 实验 ID / 日期 / 注入类型与参数 / 目标组件 / 起止时间
# 基线指标(P99、错误率、可用副本数)
# 峰值指标 / 恢复时长(注入结束 → 指标回归基线)
# SLO 是否违反 / 是否需人工介入 / 遗留问题 / 修复负责人与截止日期
| 韧性指标 | 定义 | 参考目标 |
|---|---|---|
| 恢复时长(MTTR) | 故障注入结束到指标回归基线的时间 | < 5 分钟(自动恢复) |
| 自愈率 | 无需人工介入即可恢复的实验占比 | > 90% |
| SLO 违反次数 | 实验期间违反既定 SLO 的次数 | 0(或记录为待修复项) |
| 回归缺口 | 恢复后指标未回到基线 ±10% 内的项 | 0 项 |
季度性把这些指标汇总成「韧性趋势图」:恢复时长逐季缩短、自愈率逐季上升,说明混沌演练在真实地提升系统韧性——这是向管理层证明混沌工程价值的核心数据。
常见错误
- Chaos Mesh Dashboard 无法访问:检查
port-forward是否正常,确认chaos-dashboardPod 状态为 Running,查看 Pod 日志排查 TLS 证书或端口冲突问题。 - 故障注入后 Pod 未被杀掉:
selector.labelSelectors与目标 Pod 标签不匹配。用kubectl get pods --show-labels确认标签,注意 YAML 缩进导致的 YAML 解析错误。 - NetworkChaos 不生效:集群网络插件不兼容(如某些 CNI 不支持 tc)。确认 Chaos Mesh 的
chaosDaemon.runtime和socketPath与节点容器运行时一致。 - 实验超时后状态未恢复:手动删除残留的 ChaosResource:
kubectl get podchaos -A检查并删除未结束的实验。 - Litmus ChaosEngine 报错 " ChaosResult not found":ChaosEngine 所在命名空间需要与 Litmus 组件在同一集群,检查 RBAC 权限和 ServiceAccount 绑定。
- Parallel 步骤 deadline 不一致导致对比失真:Parallel 内各子步骤的
duration/deadline必须一致,否则先恢复的故障会掩盖叠加效应的观测。 - 实验报告只记「通过/失败」:没有记录峰值、恢复时长与 SLO 对比的实验结果不可复用——后续实验缺少基线,无法判断韧性是否在改善。
最佳实践
- 从小范围开始:首次实验使用
mode: one只影响单个 Pod,确认恢复机制正常后再扩大爆炸半径。 - 先在 staging 验证:所有混沌实验在预发布环境跑通后再应用到生产,避免未知风险。
- 配合监控观察:实验前打开 Prometheus + Grafana 面板,实时观察延迟、错误率、CPU 等指标变化。
- 设置合理超时:为每个实验配置
deadline,防止故障无限注入导致级联故障。 - 记录实验结果:每次实验记录故障类型、影响范围、系统反应和修复时间,逐步完善韧性手册。
- 固定报告模板:每次实验输出固定字段的报告并归档,季度汇总成韧性趋势(MTTR、自愈率、SLO 违反次数)。
- 混沌演练纳入发布流程:重大架构变更(引入缓存、换数据库驱动)后 24-48 小时内补一次针对性混沌实验,验证新组件没有悄悄降低韧性。
- 演练后 48 小时内复盘:实验结束后 48 小时内完成报告归档与问题指派——超过一周的复盘结论基本不会落地。
练习题
- 使用 Chaos Mesh 注入一个网络延迟实验,让 backend 服务到 database 的延迟增加 300ms,持续 5 分钟,观察服务的错误率变化。
- 编写一个 ChaosWorkflow:先执行 Pod 残杀(pod-kill),等待 30 秒后注入 CPU 压力(2 核),观察 Deployment 是否能自动恢复。
- 对比 Chaos Mesh 和 Litmus 的实验编排能力:在 Litmus 中创建一个等效于上述 Workflow 的 ChaosEngine,记录两者的配置差异。
- 用 ChaosWorkflow 的 Parallel + Suspend 编排一次「磁盘满 + 网络抖动」叠加实验,观察两个故障同时发生时系统的表现,并写一份带指标对比的报告。
- 连续三个月每月跑一次同一实验(Pod 随机杀死),汇总 MTTR 与自愈率的变化趋势,分析韧性是否改善。
- 设计并演练一次「节点断网」实验:封禁一台节点的外部流量 10 分钟,验证集群的 NotReady 处理与 Pod 驱逐逻辑。
学习检查点
学完本章后,请检验自己是否掌握以下内容:
| 检查项 | 自测问题 | 验证方法 |
|---|---|---|
| 概念理解 | 能用自己的话解释混沌工程的核心原则和稳态假设 | 尝试向他人讲解 |
| 命令操作 | 能不查文档完成 Chaos Mesh/Litmus 安装和实验创建 | 在终端实际执行 |
| 原理掌握 | 能说出混沌实验的爆炸半径控制和观测原理 | 画出流程图 |
| 故障排查 | 能独立排查混沌实验无法执行或影响范围过大的问题 | 模拟故障并修复 |
| 最佳实践 | 能说明为什么混沌实验需要从 staging 环境开始 | 对比不同方案 |
本章总结
混沌工程的核心方法论是主动而非被动——在受控环境中有意识地注入真实故障,验证系统的稳态假设是否成立。工具层面,Chaos Mesh 的 CRD 声明式与 Litmus 的 ChaosHub 各有所长,但真正的价值来自纪律:staging 先行、限制爆炸半径、设置 deadline、全程监控,并把每次实验沉淀为韧性手册,让故障恢复能力成为可验证的系统属性。
延伸阅读
- 6.2:Kubernetes 入门 Kubernetes 入门
- 6.15:HA 集群 Linux 高可用集群
- 4.7:压力测试实战 压力测试实战
- k8s04 K8s NetworkPolicy 网络策略
- container_sec02 Falco 运行时安全监控
- 6.7:GitOps 入门 GitOps 入门——ArgoCD 与 GitOps 发布