专业书籍精读 · Chaos Engineering · 第 6 章
Chaos Engineering: System Resiliency in Practice · Ch 6 · Microsoft · 2020
前两章讲的是「怎么演一场」——Slack 给老系统写剧本,Google 把演练放大到整个公司。这一章退后一步,问一个更基本的问题:该先摇哪一个?
一个稍复杂的线上系统,可能出错的地方多到数不完;而你一年只做得好几十次实验。这几十次该花在哪?
医院能查的项目有上千种,你只有一个上午。
聪明的做法不是随机抽几项,也不是把最贵的仪器都用一遍,而是看两件事:这个毛病在你这个年纪有多可能发生,以及万一真有,后果有多重。心脏和血压排前面,热带寄生虫排后面——不是后者不要紧,是它在你身上概率太低。混沌实验的排序用的是同一把尺子。
很多团队做混沌的第一步是装个工具,然后把工具会做的事当清单:它能随机关机器,于是就一直关机器。
关了两百次,每次都没事。这令人安心,但也可能只说明:你早就知道会没事。而真能把系统打倒的那几件事,一次都没摇过。
更尴尬的是另一半:真要列个完整清单,又列不完。依赖有二三十个,每个出问题的方式又有好几种,相乘就是几千几万种。
好处是把有限的次数花在真会有收获的地方。还有一条很省力的规矩:结果你已经知道的实验,不要做。清楚它一摇就出事,那就别摇,直接去修——摇它只是给自己安排一次真事故。诚实说一句代价:排序靠的是对概率和影响的估计,而估计会错,排在后面的故障照样会找上门。
能出错的地方无穷多,你的实验次数有限——所以混沌工程的真功夫不在怎么注入,而在把「先摇哪个」排对:从依赖清单出发,优先那些「变慢」和「给错数据」的形态,用「多可能 × 多疼」排序,并且跳过你已经知道答案的那些。
想进到具体机制、量级和示意图? → 切到精读版
你以为混沌工程的门槛是注入能力——能不能杀进程、断网络、注延迟。这一章把门槛挪了位:注入从来不是瓶颈,选择才是。故障空间随依赖数与失效形态组合爆炸,而实验预算在人力、风险、时间窗三头都是有限的。于是真正的工程问题是实验的变化(variation)与优先级:从一份完整的依赖清单出发,沿失效形态的梯度(挂掉 → 变慢 → 返回错数据)铺开变化,按 概率 × 影响 ÷ 成本 排序,并且不跑答案已知的实验——实验的产出是消除不确定性,不是制造一次事故。
全书第 6 章,Part II「原则落地」的第三篇,视角来自微软。它和前两篇是「怎么演」与「演哪场」的分工:Ch4 Slack 给存量系统写剧本、Ch5 Google 把演练放大到公司规模,这一篇退一步问预算该花在哪;往后 Ch7 LinkedIn 管实验的伦理上限,Ch12 用血缘驱动故障注入(LDFI)把本章从人的判断升级成算法。现实里它对着工具已经就位、但实验清单上只有「随机杀实例」一条的团队。
先把规模摆出来。一个中等规模的线上服务,一次请求路径上的依赖:显式的十来个(数据库、缓存、鉴权、几个下游 RPC、消息队列、对象存储),隐式的还有一打(DNS、TLS 证书、服务发现、配置与开关下发、时钟、指标后端、镜像仓库)。按二三十个算、每个至少 4 种失效形态,单依赖单形态就上百个候选;允许两个依赖同时出问题是上万;再叠负载、区域、失败比例,继续往上。
而实验预算三头都有限:人力(一个像样的实验要写假设、定指标、备中止方案,Ch4 示范过这有多重)、风险(每次生产注入都可能变成真事故)、时间窗(要挑低峰、要人在回路)。一个认真做的团队一年做得好的实验在几十次量级,不是几千次。
不解决会怎样?三种典型退化:
所以这章的问题是:在枚举不完的故障空间里,用什么可解释的规则选出接下来那十个实验?
一句数量关系就够:候选实验数 ≈ 依赖数 × 失效形态数 × 其他变化维度。三个因子各自只是十位量级,乘起来就到四五位。这决定了混沌工程不可能像单元测试那样追覆盖率——它天生是抽样,而全部质量取决于抽样规则。
这也解释了为什么注入能力不是瓶颈:手上的工具早已能造出远超你跑得完的实验。稀缺的是判断力。
清单从哪来决定了你的上限:从工具能做什么起步,清单长得像工具的功能列表;从依赖起步,它才长得像你的系统。
判据一句话:它坏了你的请求会变慢或失败,它就是依赖。按这条过一遍,通常能捞出一批从没被测过的东西:DNS、TLS 证书过期与吊销检查、服务发现、配置与开关下发、NTP、指标后端(写不进去时线程会不会被阻塞?)、镜像仓库(只在扩容时用到,于是只在你最需要扩容的那一刻失效)。
最值钱的发现藏在这里:被标为「非关键」的依赖。「埋点失败不影响下单」只是一句假设;真注一次常发现它是同步调用、没设超时、还和主链路共用一个线程池。关键性是实验结论,不是设计文档里的声明——「大家都说它不重要」反而是高优先级信号。
这是本章最该带走的东西。同一个依赖,按「容错代码接得住的难度」排有四档:
第③档为什么最危险,可以算出来:上游 200 QPS、下游 p99 50ms,按 并发 ≈ QPS × 延迟 稳态只需约 10 个槽;劣化到 3s 就需要 600 个槽,而连接池只有 50——几秒内池子占满,连不经过这个下游的请求也开始失败。一个「非关键」依赖就这样升级成全站故障,而它从头到尾没返回过一个错误。
所以变化的第一刀该沿这条梯度切:任何一个你已摇过「杀掉」的依赖,都还欠两个实验——注延迟、注错数据。这比再找一个新依赖杀掉便宜得多,收获却大得多。
优先级 = 概率 × 影响 ÷ 成本。三项都要落到能说清的输入:
排序之后还有两条过滤规则,比排序本身更省预算:
一、跳过答案已知的。确信它会失败(没超时、没降级)→ 直接提单去修,别注入;确信它会通过而且验过 → 也不必再占一个窗口。实验的产出是消除不确定性,所以该跑的恰好是「你有信心会过、却从没验过」的那一批——惊喜全藏在那里。
二、一次注入收三样东西。同一次实验顺带能验:系统行为、告警是否触发、人与预案跟不跟得上。挂上监控验证的边际成本近乎为零,却最常被忘。这也抬高了某些「我有信心」的实验的优先级——摇它不为看系统,是为看告警会不会响。
第一张表是梯度的落地:同一个依赖,四种摇法各验到什么、为什么常被漏。
| 失效形态 | 验到的是 | 常用容错手段 | 为什么常被漏测 | 优先级 |
|---|---|---|---|---|
| ① 拒连 / 快速失败 | 熔断与降级路径走不走得通 | 熔断器、重试、兜底值 | 不漏——几乎所有人从这里开始 | 低(多半已验过) |
| ② 超时 | 超时值是否存在、是否算过 | 显式超时 + 预算传递 | 默认值藏在客户端库里,没人看过 | 中高 |
| ③ 劣化(注延迟) | 池饱和会不会把故障扩散到无关请求 | 池隔离(舱壁)、负载脱落、自适应并发 | 没有错误码,可用率仪表盘全绿 | 最高 |
| ④ 错 / 部分数据 | 校验、对账与缓存污染的防线 | schema 校验、校验和、对账任务 | 注入成本高,且需要正确性 oracle | 高(值钱但难做) |
误区是把预算全花在第①档。它恰恰是唯一一档你大概率已经接住了的。
第二张表按本章的尺子排了一遍,给出「先做哪几个」的样子。
| 候选实验 | 概率来源 | 影响 | 成本 | 结论 |
|---|---|---|---|---|
| 给「非关键」依赖注 3s 延迟 | 关键性只是声明,从未验证 | 可能池饱和 → 全站 | 低,单实例即可 | 排第一 |
| 复现上次事故的那类故障 | 实测——已经发生过 | 已知且量化过 | 中,但假设最清楚 | 排前列,兼做回归 |
| 给刚改过的依赖注失败 | 变更频率是最好的先验 | 随路径流量而定 | 低 | 常态化,挂进流水线 |
| 随机杀实例 | 高,但后果已被验证多次 | 通常无感 | 极低 | 保留为持续跑的底噪 |
| 时钟回拨 / 脑裂 | 在多数系统上低两个数量级 | 可能很重 | 高,难编排 | 有状态存储才值得 |
| 注入你确信会挂的那个调用 | — | — | — | 不做:直接提单去修 |
落到具体选择:
三句能直接改掉评审结论的话。一:混沌工程的质量不等于注入能力,等于抽样规则的质量。二:「被说成非关键」是高优先级信号。三:已摇过「杀掉」的依赖,都还欠「注延迟」和「注错数据」。
面试被问「你们怎么做混沌工程」,答「每周随机杀实例」是工具层;答「先列全依赖清单(含 DNS、证书、配置下发),按概率 × 影响排序,优先给被认为非关键的依赖注延迟,并用同一次注入验证告警」是另一个层级——它显示你知道瓶颈在选择而不在注入。
大厂实证
① 核心命题:注入不是瓶颈,选择才是。候选实验数 ≈ 依赖数 × 失效形态数 × 其他变化维度,三个十位量级因子相乘就到 10³–10⁴,而预算只有几十次量级——混沌工程天生是抽样,质量全在抽样规则。
③ 清单要从依赖起步而不是从工具起步。判据:它坏了你的请求会变慢或失败,它就是依赖——DNS、证书、服务发现、配置下发都算。
④ 失效形态有四档梯度:拒连 → 超时 → 劣化 → 错数据。容错代码基本只测过第①档,大事故出在③④档。
⑤ 第③档为什么致命,一算就清楚:200 QPS × 50ms 只需约 10 个槽,劣化到 3s 要 600 槽而池子只有 50——池饱和把一个没报错的依赖变成全站故障。
⑥ 变化(variation)的第一刀:已摇过「杀掉」的依赖,都还欠「注延迟」与「注错数据」,比再找新依赖杀掉便宜得多。
⑦ 排序用 概率 × 影响 ÷ 成本:概率最硬的输入是事故历史,影响按 QPS 与金额算。被说成「非关键」的依赖是高优先级信号。
⑧ 两条过滤规则:答案已知的不跑(确信会挂就直接去修),以及一次注入顺带验告警——边际成本近乎为零,却最常被忘。