On-Call:事故响应的完整流程
这一章讲三件事: 值班(On-Call)这项工作怎么排班、靠什么技能; 事故来了按什么顺序处理——以及为什么顺序和你想的相反; 事后怎么把一次事故变成团队的知识。 读完你会有一张事故处理流程卡,和一份「不要成为救火英雄」的提醒。
1. 这一章讲什么
即使你读完前七章、代码写得无懈可击,东西还是会坏——这是第 03 章说过的物理事实。On-Call(值班)就是那个「东西坏了时第一个被找来的人」的正式制度:On-Call 工程师是应对计划外工作的第一道防线,无论是生产环境问题还是临时支持请求1。原书甚至要求不参与值班的人也读这一章——On-Call 技能适用于任何紧急状况2。
本章在全书链条里的位置:第 07 章的展开监控盯着指标异动;指标响铃之后的世界,就是本章。
2. 顶层全景
警报响起 ──→ ① 分流 确认问题、定优先级(不排障!)
──→ ② 协同 找到负责人,通知该通知的人
──→ ③ 应急方案 "止血":回滚/切流/关特性(不是修复)
──→ ④ 解决方案 有空喘气了,科学方法找根因
──→ ⑤ 后续行动 回顾文档 + 5 Why + 防复发任务票
(未完成 ⑤,事故不算结束)
图说:顺序是本章的命门——普通人的直觉是直奔 ④,原书说那是
在用户还在受苦的时候玩解谜。主走查:一次真实事故全程。
3. 核心原理
3.1 值班怎么排班、靠什么
排班通常一周或两周一轮,新人先「跟随」几轮;常见配置是一名主要责任人加一名辅助,有的公司还有分层升级(支持→运维→开发)3。每轮以交接开始和结束:上一班总结未解决的事故、给下一班留背景4。
原书给值班工程师列了五项技能,按「对事」和「对人」分两半:
对事的三件。 一是随时响应——原书引用一句老话「你最好的能力是随时响应」:值班时要预期被打断,放弃做深度工作的幻想;「随时响应」也不等于立刻解决,完全可以先回复「我可以在 15 分钟内给您答复吗」——人们期待快速反应,不必然期待快速解决5。二是保持专注——把值班要用的东西提前备好:关键仪表盘、运行手册、日志入口,攒成一个「On-Call 书签文件夹」;平时多看正常状态的图表,建立正常行为的基础线,事故来时你才知道哪张图不对劲6。三是定优先级——多数公司用 P0、P1、P2 这样的分级,原书抄了谷歌 云的定义:P1 是「服务在生产环境中无法使用」7。分级的锚点是三个行业缩写:SLI(服务水平指标,如错误率、延迟——判断健康与否的原始数据)、SLO(服务水平目标,给 SLI 划及格线,如「错误率低于 0.001%」)、SLA(服务水平协议,写明越过 SLO 的后果,通常是赔钱甚至解约)8。
对人的两件。 清晰沟通——事情发生得很快,沟通不畅会酿成大问题;要礼貌、直接、响应快,不知道就说不知道。原书给了一条值得背下来的句式:复述对方的问题来确认理解——「确认一下:登录服务是从配置文件服务中接收到 503 响应码的吗?您说的不是认证,对吗?」(认证和授权是两个容易混的服务名)9。以及定期发布状态更新——每条更新带上新发现、下一步计划和一个新的时间预估10。跟踪你的工作——每件事都记进任务票,笔记里永远带时间戳:当用户在下午 1:05 开始报告延迟时,「某项服务在下午 1 点被重启过」这条信息值一场排查11。
3.2 事故五阶段:顺序就是纪律
生产软件的关键问题就是事故,由自动监控的警报或支持工程师的报告触发。原书把响应拆成五个阶段,并立刻点破常见的顺序错误:大多数人以为处理事故是为了解决问题——解决问题确实重要,但只是第三个目标;第一个目标是减轻影响恢复服务,第二个目标是抓住现场信息供日后分析12。
① 分流 triage 确认问题、评估影响、定优先级 —— 明确「不排障」
② 协同 coordination 找到负责人,通知所有相关方与受影响用户
③ 应急方案 mitigation "止血":回滚、切流、关特性、加机器 —— 不是修复
④ 解决方案 resolution 有喘息空间了,科学方法找根因、真正修掉
⑤ 后续行动 follow-up 回顾文档、5 Why、防复发的任务票
两条阶段纪律要划出来。分流阶段明确禁止排障:「分流也不是排除故障的时候。在你排除故障的期间,你的用户将会继续受苦」——排障是③④阶段的事,分流唯一要做的是给问题定价,好让组织把最贵的人力投给最贵的问题13。应急方案阶段要边止血边抓现场:一旦问题缓解,现场可能再也复现不出来——快速保存遥测数据、堆栈痕迹、堆转储、日志和仪表盘截图14。止血的标准动作是回到「最后已知良好」的版本,或把流量从问题处移开;理想情况下你有一份运行手册(runbook,针对常见问题的预定义分步指示)照着执行15。
3.3 主走查:一起真实事故的五个阶段
原书用一整起真实事故把五个阶段串成一条线,我们完整走一遍16。
背景: 公司的数据仓库(为报表和 机器学习提供分析查询的数据库)靠一个连接器从流式系统读消息、写入仓库。某天监控发现:流里的数据没有出现在数据仓库中,而且缺的表用于生成客户报告。
① 分流。 On-Call 工程师确认警报、看影响的表——客户报表用的表缺了数据,定高优先级。分流到此为止:确认了警报、定了级,「没有试图解决问题」,只是看了看哪些表受影响17。
② 协同。 转入协同:在运维频道发公告(面向客户的数据表出现数据缺口);初查显示连接器在跑、日志无异常——于是拉来连接器的开发者,又拉一位有经验的工程师;工程经理出任事故经理;给全公司发邮件,在面向客户的状态页上挂出公告18。这一阶段的机制课:小事故由 On-Call 自己协调;大事故由事故指挥官负责——他跟踪谁在做什么、调查到哪一步;相关方全部进作战室(专门协调事故响应的虚拟或物理空间);所有交流落在书面(任务票或频道),因为「详细的记录将有助于事后重建时间线」19。
③ 应急方案。 止血。工程师先重启连接器——无效;堆栈显示它在读取并反序列化(把传输格式解码回内存对象)消息,CPU 打满 100%,猜测是被一条庞大或损坏的消息卡住。已知完好的数据流(并行传输消息的通道)有 30 个,不知道哪个 藏着坏消息——用二分法:先启用一半,看连接器的行为,再对半缩小。最终找到坏流,连接器带着 30 减坏流重启,表数据追平,影响面缩小到「一个流、一张表」20。注意这一步做了什么、没做什么:影响从「客户报表」缩到「一张表」——止血完成,根因未明。
④ 解决方案。 现在有空喘气了。工程师把健康流全部从连接器移走,单独复现问题;用命令行工具手动读那条消息——一切正常。然后是全案的转折点,原书称之为「一番顿悟」:为什么命令行工具读得了,连接器读不了? 差异在连接器多了一套消息头解析逻辑。重新运行工具、打开消息头输出,真相出现:坏消息的头信息里有一个单键、值是空的21。顺藤摸瓜:消息头是一个 APM(应用性能管理,埋在应用内部报告运行状态的监控工具)守护进程默认注入的,没人知情;再联系外部支持,证实命令行工具自己有个 bug——它不输出含空尾字节字符串的消息头22。验证理论:在连接器配置里禁用头解码——表数据立刻加载,全部数据质量检查转绿23。
原书把这里的思维方式提炼成科学方法:检查问题→做出诊断→测试和「治疗」;治疗成功就 治愈,不成功就回头重提假设。团队当时的假设链是「连接器反序列化有问题、数据没被真丢」→ 用指标和二分法找坏流 → 假设细化到「头信息解析」→ 用「禁用头解码」这个实验验证24。诊断本质上是一种搜索:小问题用线性(顺着从头查到尾)搜索,大系统用二分——在调用堆栈的中点设断点,判断问题在上游还是下游,再对半缩小25。
⑤ 后续行动。 事故解了,事还没完。工程经理安排回顾(原书先用了「尸检」这个词,医学借来的比喻——病人死了才验尸; softer 的替代词是「回顾」),On-Call 工程师起草文档;「尸检」过程新开 3 张任务票,分别查:为什么 APM 要用消息头、为什么连接器不能反序列化、为什么命令行工具不输出空字符串的头26。
文档的核心是根本原因分析(RCA),工具是 5 个「Why」——对着问题连问五次为什么。原书把本案的链条整个抄了出来27:
问题:数据仓库中的数据缺失。
1. Why? 连接器没有加载数据到数据仓库。
2. Why? 连接器不能反序列化传入的消息。
3. Why? 传入的消息有糟糕的头信息。
4. Why? APM 在消息中插入了头信息。
5. Why? APM 在开发者不知情的情况下默认了这种行为。
→ 根本原因:APM 的意外消息头配置。
原书同时给了这个流行工具的两条安全须知。其一,「根本原因分析是一个流行但具有误导性的术语」——事故很少只有一个原因,5 个 Why 常常引出多个分支,都记下来28。其二,回顾会对事不对人:「彼得没有禁用消息头」是指责,「消息头配置的改变没有经过代码评审」是需要改进的问题;良好的回顾会还把「找出解决方案」与评审会议分开——解决方案(比如「把坏消息放进死信队列」)应该作为后续任务去跟进,不该在会上现场拍板29。
3.4 支持请求:值班另一半的流水账
不处理事故时,值班工程师在处理支持请求——「这个怎么用」到疑难排查都有。原书给了一套同样可套用的节奏,并用一段带时间戳的真实对话演示30:
3:48 PM 萨米特:有用户报告页面加载很慢。
4:12 PM 珍妮特:谢谢报告。能给我一两个用户 ID 和具体页面吗?
我们的仪表盘没显示大面积延迟。 ← 先确认现象和影响
5:15 PM 萨米特:用户 1934 和 12305,/ops 页,加载大于 5 秒。
5:32 PM 珍妮特:收到,明早 10 点前给你答复。 ← 给 ETA
8:15 AM 珍妮特:查到了。昨天下午我们对 APM 仪表盘的数据库做了
维护,影响到运维主页上的汇总信息;昨晚 8 点左右结束。
能确认用户那边恢复了吗? ← 复述根因,请对方确认
9:34 AM 萨米特:确认,好多了。 ← 关单前必须拿到确认
半小时内首次响应、主动要信息定级、给 ETA、复述来龙去脉、请请求者确认后才关单——3.1 的五项技能在这十行对话里全部落地。原书还提醒:把支持当分心是亏本的——它是你看到「自己的软件在真实世界怎么被使用、怎么失败」的窗口,而且快速高质量的支持响应会给你积累声誉31。
3.5 不要逞英雄
本章的最后一节是刹车。值班和支持会带来真实的满足感——被感谢、被称赞——于是有些工程师随着经验增长,「跳入救火模式成为一种条件反射」,变成团队的「救火队员」:大家默认出了事找你32。原书指出这条路的四重代价:你实质上成了长期的 On-Call,长时间高风险导致倦怠;你不断被打断,编程和设计的本职反而「步履蹒跚」;依赖你的团队永远不会长出自己的排障能力;而你修修补补的次数越多,修严重潜在问题 的工作越被排在后面——火越救越多33。
自查信号:如果你觉得「只有我能解决这个问题」,或者你不在班上也频繁救火,你可能正在成为英雄。解法是双向的:和你的管理者重新谈分工,让更多人练到能顶上;如果你依赖某位英雄,主动从他手里接过一些——原书给了句可复制的台词:「谢谢,珍。实际上,我想自己想办法解决一下,这样我就能掌握技能了……如果这仍然是个谜,我可以在 30 分钟内请求你的帮助吗?」34——这句话同时是第 01 章「提问三步」和本章「随时响应」的合流点:先给自己 30 分钟,再开口。
4. 作者的判断与证据
- 「先止血后查因」:作者判断,论据是顺序推演(用户在受苦、现场会消失);与谷歌 SRE 一脉相承,原书在「升级加油站」里承认五阶段改编自 Increment 网站的文章。
- 数据仓库事故:真实事故,细节具体(30 个数据流、CPU 100%、单键空值头、APM 注入、命令行工具 bug),是本章全部机制的载体。
- 「根本原因分析是误导性的术语」:作者判断,论据是「事故很少由单一问题引起」的行业经验。
- 「不要逞英雄」:作者立场,论据是四重代价的机制推演;没有数据,但与行业 对倦怠的普遍认知一致。
5. 边界与局限
- 组织形态依赖:事故指挥官、作战室、状态页这些机制假设组织有基本的运维基建;小团队没有专职 SRE 时,这些角色全由开发兼任,书的建议仍适用但压力更大。
- SLA/赔偿:只讲了「违反要赔钱」,没讲 SLI/SLO 怎么科学设定(原书指路谷歌 SRE 书第 4 章)。
- 心理侧只开了头:倦怠的机制、值班后的恢复,本章只有「不要逞英雄」一节;第 14 章的睡眠与休假是它的续篇。
- 案例中的工具名词(APM、死信队列)是 2010-2020 年代主流栈;今天的可观测性工具形态已变,流程不变。
6. 可带走的
- 值班五技能:随时响应、先记住「正常长什么样」、按 P 级与 SLI/SLO 定优先、复述确认、带时间戳的记录;
- 事故顺序背下来:分流(不排障)→ 协同(找指挥官、告知相关方)→ 止血(不是修复)→ 找根因 → 回顾防复发;
- 止血的同时抓现场:遥测、堆栈、堆转储、截图——缓解之后可能再也复现不了;
- 「命令行能读、程序不能读」这类差异处就是根因的入口——差异比较是最便宜的诊断;
- 找根因用二分搜索:在调用链中点断开,判断上下游,对半缩;
- 回顾文档写「哪个环节缺了制度」(「改动没经过评审」),不写「谁没 做什么」;
- 5 Why 常常引出多个根因——都记下,别强行归一;
- 支持请求四步:确认现象→给 ETA→复述根因→请对方确认再关单;
- 警惕自己成为救火英雄:打断深耕、掩盖系统问题、拖垮你自己;把「30 分钟内再来找你」变成口头禅;
- 所有后续任务完成之前,事故不许关单。
7. 原文地图
| 主题 | 原书章 | 原文位置 |
|---|---|---|
| 第一道防线 | 第9章 On-Call | text/18-ch09-9-on-call.txt:4(搜「第一道防线」) |
| 轮换与交接 | 第9章 On-Call | text/18-ch09-9-on-call.txt:23(搜「根据时间表进行轮换」) · text/18-ch09-9-on-call.txt:46(搜「以交接开始」) |
| 随时响应 | 第9章 On-Call | text/18-ch09-9-on-call.txt:61(搜「你最好的能力是随时响应」) · text/18-ch09-9-on-call.txt:75(搜「15 分钟内给您答复」) · text/18-ch09-9-on-call.txt:76(搜「不一定需要快速解决」) |
| 基线与书签 | 第9章 On-Call | text/18-ch09-9-on-call.txt:91(搜「正常行为的基础线」) · text/18-ch09-9-on-call.txt:95(搜「书签」) |
| P0-P4 | 第9章 On-Call | text/18-ch09-9-on-call.txt:112(搜「P0、P1、P2」) · text/18-ch09-9-on-call.txt:117(搜「P1:严重影响」) |
| SLI/SLO/SLA | 第9章 On-Call | text/18-ch09-9-on-call.txt:124(搜「服务水平指标(SLI)」) · text/18-ch09-9-on-call.txt:128(搜「0.001%」) · text/18-ch09-9-on-call.txt:130(搜「返还资金」) |
| 503 确认 | 第9章 On-Call | text/18-ch09-9-on-call.txt:153(搜「503 响应码」) |
| 状态更新 | 第9章 On-Call | text/18-ch09-9-on-call.txt:156(搜「定期发布状态更新」) |
| 时间戳 | 第9章 On-Call | text/18-ch09-9-on-call.txt:188(搜「包含时间戳」) · text/18-ch09-9-on-call.txt:189(搜「下午 1:05」) |
| 事故三目标 | 第9章 On-Call | text/18-ch09-9-on-call.txt:197(搜「第一个目标」) · text/18-ch09-9-on-call.txt:200(搜「第三个目标」) |
| 五阶段 | 第9章 On-Call | text/18-ch09-9-on-call.txt:202(搜「分流(triage)」) · text/18-ch09-9-on-call.txt:207(搜「应急方案(mitigation)」) |
| 止血 | 第9章 On-Call | text/18-ch09-9-on-call.txt:208(搜「止血」) |
| 分流不排障 | 第9章 On-Call | text/18-ch09-9-on-call.txt:250(搜「排除故障的时候」) |
| 主走查:事故起点 | 第9章 On-Call | text/18-ch09-9-on-call.txt:223(搜「数据无法加载到数据仓库」) |
| 分流段结束 | 第9章 On-Call | text/18-ch09-9-on-call.txt:244(搜「分流阶段已经结束」) |
| 协同与指挥官 | 第9章 On-Call | text/18-ch09-9-on-call.txt:270(搜「事故指挥」) · text/18-ch09-9-on-call.txt:278(搜「作战室」) · text/18-ch09-9-on-call.txt:286(搜「重建时间线」) |
| 主走查:二分法 | 第9章 On-Call | text/18-ch09-9-on-call.txt:300(搜「目前共有 30」) · text/18-ch09-9-on-call.txt:302(搜「二分法搜索」) |
| 运行手册 | 第9章 On-Call | text/18-ch09-9-on-call.txt:316(搜「运行手册是预定义好」) |
| 保存遥测 | 第9章 On-Call | text/18-ch09-9-on-call.txt:320(搜「快速保存遥测数据」) |
| 主走查:顿悟 | 第9章 On-Call | text/18-ch09-9-on-call.txt:339(搜「一番顿悟」) · text/18-ch09-9-on-call.txt:346(搜「一个单键」) · text/18-ch09-9-on-call.txt:347(搜「应用性能管理」) · text/18-ch09-9-on-call.txt:353(搜「空尾字节」) |
| 禁用头解码 | 第9章 On-Call | text/18-ch09-9-on-call.txt:356(搜「禁用头解码」) |
| 科学方法 | 第9章 On-Call | text/18-ch09-9-on-call.txt:369(搜「科学方法」) · text/18-ch09-9-on-call.txt:373(搜「假设」) |
| 二分诊断 | 第9章 On-Call | text/18-ch09-9-on-call.txt:385(搜「线性搜索」) |
| 尸检与任务票 | 第9章 On-Call | text/18-ch09-9-on-call.txt:410(搜「尸检」) · text/18-ch09-9-on-call.txt:410(搜「3 张任务票」) |
| 5 Why 全链 | 第9章 On-Call | text/18-ch09-9-on-call.txt:434(搜「利用 5 个」) · text/18-ch09-9-on-call.txt:444(搜「APM 在开发者不知情」) |
| RCA 是误导术语 | 第9章 On-Call | text/18-ch09-9-on-call.txt:448(搜「误导性的术语」) |
| 对事不对人 | 第9章 On-Call | text/18-ch09-9-on-call.txt:460(搜「是一种指责」) · text/18-ch09-9-on-call.txt:463(搜「评审会议分开」) |
| 支持对话 | 第9章 On-Call | text/18-ch09-9-on-call.txt:495(搜「萨米特」) · text/18-ch09-9-on-call.txt:521(搜「estimated time of arrival」) |
| 支持是学习 | 第9章 On-Call | text/18-ch09-9-on-call.txt:525(搜「一次学习的机会」) |
| 救火队员 | 第9章 On-Call | text/18-ch09-9-on-call.txt:540(搜「救火」) · text/18-ch09-9-on-call.txt:542(搜「万金油」) |
| 30 分钟台词 | 第9章 On-Call | text/18-ch09-9-on-call.txt:561(搜「掌握技能」) |