“我们的系统高可用 99.9%”——这句话在依赖挂掉之前都是信仰。混沌工程的本质:主动把依赖杀了,看系统表现和宣称的是不是一回事。这篇讲测试团队怎么落地混沌工程——不是造 chaos monkey 那种平台,而是把故障注入变成可重复的测试场景。
一、混沌测试 vs 传统故障测试:区别在"假设驱动"
1 | 传统故障测试: 我们知道 DB 会挂 → 测"DB 挂了系统怎么办" |
混沌实验三要素(照搬业界标准,落地时一个都不能少):
- 稳态定义:实验前明确"正常是什么样"(核心接口 P99 < 200ms、错误率 < 0.1%、下单成功率 > 99.9%)——没有稳态定义,"系统挂没挂"就是吵架;
- 爆炸半径控制:注入范围最小化(一个 pod/一个节点/一个依赖),从 staging 单实例开始,逐步放大;
- 停止条件:提前写死"什么指标越界就立刻停止注入并恢复"——混沌实验本身也是风险,必须有一键刹车。
二、故障场景库:按"失效模式"分类,不按"服务"分类
场景按失效模式组织(跨服务复用),每个场景 = 注入参数 + 稳态观测 + 预期行为:
1 | # chaos/scenarios/dependency_down.yaml |
场景库的完整清单(起步 12 个就够):
| 类别 | 场景 |
|---|---|
| 依赖失效 | 单依赖 kill / 依赖超时(延迟 5s)/ 依赖返回 5xx |
| 网络 | 分区(节点间丢包 100%)/ 延迟 300ms / 丢包 20% |
| 基础设施 | 单 pod kill / 节点 kill / 磁盘满(写满到 95%) |
| 数据 | 主从切换(DB failover)/ 缓存全失效(清空 Redis) |
| 时间 | 时钟偏移 5 分钟 / 证书过期 |
| 客户端 | 弱网(复用 mitmproxy 场景)/ 断网重连 |
三、注入工具选型:能进程级就别网络级
1 | 注入粒度由细到粗(能细就别粗): |
我们的实践:ChaosBlade 做进程级和网络级(K8s 环境原生支持),toxiproxy 做接口级(依赖间插代理,按接口精确注入),DB failover 走 DBA 的演练流程。工具不是重点,场景库 + 停止条件 + 稳态观测才是。
四、和测试体系的关系:混沌是"压测/长稳/回归"的补充维度
| 测试维度 | 回答的问题 | 触发条件 |
|---|---|---|
| 回归 | 功能对不对 | 代码改动 |
| 压测 | 容量够不够 | 流量增长 |
| 长稳 | 时间久不久得住 | 泄漏类 bug |
| 混沌 | 坏的时候坏得体不得体 | 架构变更/依赖变更后 |
混沌实验的触发时机(不是天天跑):
- 架构变更后必跑:加了新依赖、换了中间件、调整了超时配置——"新依赖"就是新的失效模式;
- 重大版本前:和发版回归并行;
- 季度全量:12 个场景全过一遍,出"韧性报告"(哪些依赖失效是"优雅降级",哪些是"雪崩")。
五、一次真实实验的复盘格式
1 | 实验:dependency_down / user-service(单实例 kill,300s) |
复盘的产出不是"系统挂了",是三个精确的改进项——混沌实验的价值密度取决于复盘质量。
六、落地节奏(6 个月路线)
1 | M1-2:稳态定义 + 场景库前 4 个(依赖 kill/超时)+ staging 单实例 |
七、小结
混沌工程落地的四个关键:稳态定义(没有它实验就是吵架)、场景库按失效模式组织(可复用可累积)、爆炸半径 + 停止条件(实验本身是风险)、复盘出改进项(价值在产出不在过程)。它不替代回归/压测/长稳,回答的是它们都不回答的问题:"系统坏的时候,坏得体面吗?"