专业书籍精读 · SRE · 第 15 章
Site Reliability Engineering · Ch 15 · John Lunney & Sue Lueder · Google · 2016
网站崩了,两个小时后修好了。然后呢?大多数公司的答案是「然后就没有然后了」——大家松一口气,各回各的活;三个月后同一个毛病原样再来。Google 这本书的第 15 章讲的就是那个「然后」该怎么做:把这次事故写下来、讲清楚、变成一串必须做完的改动。这份文档叫复盘。
民航业有个老规矩:空难之后调查飞机,不调查飞行员。不是因为飞行员没犯错——很多时候他确实按错了钮。而是因为调查组真正想知道的是:那个钮为什么长得跟旁边那个一模一样?为什么手册写的和座舱里显示的不一致?为什么那个错误的动作,在他眼里看起来是对的?找出这些,下一个飞行员就不会再按错。如果结论是「飞行员失职」,钮还在那儿,下一个人照样按。
整章最值钱的一句就是这个道理的技术版:你没法「修好」人,但你可以修系统和流程。
难在一件很人性的事:一旦开始追人,大家就不说了。
你要的信息偏偏只在当事人脑子里——他当时看到了什么、为什么那一步在当时是合理的。没有任何监控能替他说出来。可只要他知道说出来会被记一笔,他就只讲那些瞒不住的部分;下一次,小毛病他干脆不报了。于是你的事故记录本上全是大事故,看起来风平浪静,其实小问题在暗处一直攒着。
① 先把「谁干的」这个问题从会议室里删掉。前提是一句假设:参与这件事的每个人,都是本着好意、用他当时手上的信息做了他认为对的事。于是问题从「谁做错了」换成「为什么当时那个选择看起来是对的」。这不是对人温柔,是一门交易:你放弃追究一个名字,换来那部分只有他知道的信息。
② 什么情况必须写,要提前说好:用户感觉到的故障超过多久、丢了任何数据、值班的人半夜爬起来手工干预过、修了太久、以及——监控没报出来、是用户先发现的。为什么要提前?因为等出了事再讨论「这算不算大事」,答案会随当天谁在场、谁不高兴而变。
③ 产出不是文档,是一串待办。复盘写得再动人,如果没有几条「改哪个系统、谁改、什么时候改完」,它就只是一篇作文。而且写完必须有人认真看——书里的原话很不客气:一份没被评审过的复盘,跟从没存在过一样。
④ 老复盘是最好的教材。Google 的做法有点好玩:把几个月甚至几年前的旧复盘翻出来,叫上当年的当事人、旁观者和新人一起读;还有一种叫「灾难轮盘」的排练——挑一份旧复盘,让新人扮演当时的角色,把那场事故从头演一遍。
事故的学费已经交了,复盘是唯一能把它换成东西的动作:不追人、只追「为什么当时那么做是合理的」,因为你要的信息只在当事人嘴里;提前说好什么情况必须写,写完必须有人看,而真正的产出是一串被跟到关掉的改动。诚实说一句代价:无指责很容易做成表演——嘴上不追责,年底考评照样记着这一笔;只要人能闻出这个味道,整套东西就白搭。
想进到完整流程、对比表和真实公司的公开复盘? → 切到精读版
SRE 第 15 章要立的规矩只有一条:事故的学费已经付过了,复盘(postmortem)是唯一能把它换成资产的动作。而这个动作成不成,取决于一个反直觉的设计——把「谁做错了」这个问题从流程里彻底删掉。删它不是出于对人温柔,是因为你最需要的那部分信息只存在当事人脑子里:他当时看到了什么、为什么那个选择在当时看起来是对的。你一开始追人,这部分信息就永远拿不到了。本章的原话是:「你没法『修好』人,但你可以修系统和流程,让人在设计和维护复杂系统时更容易做出正确的选择。」还有一句更硬的判据:一份没被评审过的复盘,跟从没存在过一样。
作者 John Lunney 与 Sue Lueder。本章在 SRE 书 Part III「实践」里紧接着事故处置那一串:上承第 12 章「有效排障」(怎么把毛病找出来)、第 13 章「紧急响应」与第 14 章「事故管理」(事故当中怎么组织人),而本章管的是事故结束之后的那 48 小时。它同时是第 3 章「拥抱风险」的还款通道——错误预算被烧掉的那一截,只有靠复盘产出的行动项才换得回来。落到现实里,各家的名字不同(incident review、post-incident review / PIR、correction of errors / COE),做的是同一件事;它也对应一道几乎必问的面试题:「讲一次你参与过的线上事故,后来你们改了什么。」
本章的题词就是整章的经济学:「失败的代价是教育。」(The cost of failure is education. —— Devin Carraway)翻译成工程语言:事故已经把钱花掉了——用户疼过了、错误预算烧掉了、几个人的周末没了。这笔支出唯一可能的回报,是你从中学到点什么并落地。不写复盘,等于交了学费不去上课。
把账算给 SLO 看最直白:一条 99.9% 的月度可用性目标,整月只允许约 43 分钟不可用(第 3、4 章的口径)。一次 30 分钟的事故就吃掉七成;同一类事故一个季度回来三次,全年余额基本就没了。所以「防止它再来」不是道德要求,是预算算术。
本章要治的病有三个。一是教训不落地:修好了、群里说声「好了」,教训留在两三个人脑子里,其中一个半年后换了组,同类事故原样重演。二是指责导致的瞒报——这是本章最坚决的主张:把指责从复盘里拿掉,能让人有底气毫无顾虑地把问题捅上去;反过来,弥漫着指责的空气会造就一种把事故扫到地毯底下的文化,而这让组织承担的风险更大。三是没有判据:没人事先规定什么情况必须写,实际结果就是「大到瞒不住才写」,于是事故库里只剩 P0,看着风平浪静。
本章给的定义是五件东西的合集:一次事故的书面记录,包含它的影响、为缓解或解决它所采取的动作、根因,以及为防止它再次发生的后续行动。五样里前四样都是「回顾」,只有最后一样是「未来」——而全章的重量正压在最后一样上。
三个主要目标的排序也很讲究:① 确保事故被记录下来;② 确保所有促成它的根因都被弄明白;③ 尤其是,确保有效的预防措施被真正落实,以降低它再次发生的概率与影响。那个「尤其是」不是修辞——它意味着一份写得漂亮、时间线精确、却没产出任何行动项的复盘,在本章的判据下是失败的。所以判断一个团队的复盘文化是真是假,最快的办法不是读文档,是去看那些行动项有没有被关掉:每条都该有负责人、有合理优先级、直接落成缺陷跟踪系统里的一张单子,否则「以后我们会注意」会大量出现,而它不改变任何东西。
还有一个容易被忽略的判据:复盘的读者不是你的主管,是六个月后那个不认识你的值班工程师。他会在凌晨三点搜到这份文档,因为他遇到了很像的症状。为他写,时间线和术语解释就自然会写对。
本章明确要求:复盘的触发判据要在事故发生之前就商定好、公之于众。它给的常见判据清单是——
清单里最容易被跳过的是最后一条:监控没报出来、靠人工发现。它意味着这次事故是用户先告诉你的——即便影响很小、几分钟就好了,你的监控有个洞本身就值得一份复盘(呼应第 6 章)。同理,「值班工程师人工介入过」的隐含意思是:需要人半夜爬起来敲命令,本身就是一个待修的缺陷。
本章还配了一句用来对冲判据压力的话:写复盘不是惩罚,而是全公司的一次学习机会。如果团队把「要写复盘了」听成「要挨罚了」,判据越严格、瞒报越严重——这两件事必须一起立。
本章对「无指责」的定义是一句可操作的假设:一份无指责地写成的复盘,假定所有卷入事故的人都是本着好意、并且用他们当时掌握的信息做了正确的事。请注意这句话里的时态——「他们当时掌握的信息」。这是整个机制的支点:读复盘的人已经知道结局,天然带着事后偏见,会觉得线索「明明很明显」;而当事人在那一刻并不知道。
于是提问方式必须换。不问「你为什么按了那个钮」,问「为什么按那个钮在当时看起来是对的」;不问「谁改的配置」,问「什么条件让这次改动看起来是安全的」。本章把这类调查描述为:从分配责任,转向调查是什么系统性的原因让某个人或某个团队掌握了不完整或不正确的信息——只有转过来,有效的预防方案才做得出来。
这套东西不是软件业发明的。本章明说它源自医疗与航空业:在那些行业里失误会死人,所以它们培育出一种氛围,把每一次「失误」都当成加固系统的机会。落到最有名的一句结论就是:「你没法『修好』人,但你可以修系统和流程,让人在设计和维护复杂系统时更容易做出正确的选择。」本章还给了正反措辞的对照:同一个技术判断(这个后端烂到每周出问题、该重写),可以写成情绪化的抱怨兼暗指某人无能,也可以写成面向未来的主张(重写确实能止住这些告警,而且维护手册长到没人学得完,未来值班的人会感谢我们)——只有第二种写法能被别人接着往下推。
上一节是价值观,这一节是机制。本章讲得非常直接:把指责从复盘里拿掉,能让人有底气毫无顾虑地把问题往上捅;反过来,一种弥漫着指责的空气,冒的风险是造就一种把事故和问题扫到地毯底下的文化,而这会让组织承担更大的风险。
为什么这条比听上去更严重?因为它污染的不是士气,是数据。可靠性工程的所有决策都建立在「我们知道自己出过什么事」之上:错误预算怎么花、下季度修哪块、告警加在哪。一旦小事故不再被上报,你的事故库就只剩下瞒不住的那些——这是一份幸存者偏差的样本。你会得出「系统很稳」的结论,而真实情况只是没人说。这也是为什么「无指责」必须写成制度而不是态度:态度会随着人换、随着考评变,制度不会。
本章强调复盘从头到尾是协作产物,并点名几个关键能力:实时协作(多人同时编辑,事故刚结束、信息还热着就一起补)、开放的评论与批注(任何人都能就某一行提问)、邮件通知(相关方不用主动来捞)。听着像工具功能,但它们决定了一份复盘能不能在信息蒸发之前被写完。
写完之后是评审,由没有直接卷入这次事故的资深工程师把关,逐条检查:关键的事故数据有没有为了以后被完整记录下来?影响评估做全了吗?根因挖得够深吗?行动计划是否恰当、由此产生的缺陷单优先级是否合理?结果有没有分享给相关方?
并且立了一条措辞毫不客气的最佳实践:没有一份复盘可以不被评审——一份没被评审过的复盘,跟从没存在过一样。理由很实在:写作是私人的,评审才是把这份知识并入组织的那一步。
本章清楚地知道,光有流程没用——它花了一整节讲怎么把复盘从制度变成习惯,给的都是具体到可以照抄的做法:
还有一条最佳实践值得单独拎出来:当着大家的面奖励那些做对了事的人——尤其是主动承认自己失误的人。这是无指责文化最便宜也最有效的燃料。此外本章主张这件事应该被度量而非凭感觉:既看写了多少份、行动项关闭得怎么样,也直接收集工程师的反馈,看他们是否真的觉得这里的复盘是无指责的——因为这套东西唯一会失效的方式,就是大家嘴上认同、心里不信。
本章结尾谈持续改进,方向是工具化:统一模板、把事故数据(时间线、监控图、告警记录)尽可能自动填进去。逻辑很朴素——摩擦越大,写的人越少;一份要从零手工拼三小时的文档,注定只有大事故才会有人写。Google 也把模板和一份完整的示例复盘(附录 D,一次服务过载事故)公开了出来,可以直接拿去改。
表 1 · 追人 vs 追条件:同一次事故,两种问法各买到什么
| 指责式(谁的锅) | 无指责式(为什么当时是合理的) | |
|---|---|---|
| 核心问题 | 谁做了这件事、他为什么违规 | 什么条件让这个动作在当时看起来是对的 |
| 拿到的产出 | 一个名字 + 一句「以后会更小心」 | 一组只有当事人知道的条件 → 可改的行动项 |
| 对下一次的影响 | 坑还在,换个人照样踩 | 路被堵上了,同一条走不通 |
| 对上报的影响 | 小事故不再上报,事故数据变成幸存者偏差 | 人有底气把问题往上捅 |
| 看起来的好处 | 「严格」「有交代」,对外好写 | 像是「没人负责」——需要向管理层解释 |
| 真实代价 | 组织风险被推到暗处继续累积 | 做成表演就一文不值;见 §7 的「无指责 ≠ 无问责」 |
表 2 · 什么时候必须写:判据、阈值示例、跳过的代价(阈值是示例,本章只说「某个阈值」,具体数字由你的 SLO 决定)
| 触发判据 | 阈值示例 | 跳过它的代价 |
|---|---|---|
| 用户可见的宕机 / 降级 | 影响 > 1% 请求且持续 > 5 分钟;或吃掉 > 10% 月度错误预算 | 预算被慢慢啃光,年底才发现没得花 |
| 任何形式的数据丢失 | 无阈值——丢一条也写 | 数据问题几乎必然复发,且第二次通常更大 |
| 值班工程师人工介入 | 回滚、切流量、手工重启等任何一次人工动作 | 「需要人半夜起来敲命令」这个缺陷永远不会被修(琐务,见第 5 章) |
| 解决耗时过长 | 从告警到恢复 > 60 分钟 | MTTR 长的真实原因(手册过期、权限缺失、找不到人)没人去查 |
| 监控没报出来 | 由用户 / 客服 / 人工巡检先发现 | 监控的洞留着,下一次同类问题依然靠用户告诉你 |
| 任何利益相关方要求 | 不需要理由 | 把「值不值得写」的判断权收拢到少数人手里 |
表 3 · 复盘的四种失败模式:怎么认出来、怎么治
| 失败模式 | 症状 | 解药 |
|---|---|---|
| 作文式 | 时间线精美、行动项一条没有,或全是「以后要注意」 | 每条行动项必须落成有 owner、有优先级的缺陷单;定期查关闭率 |
| 追人式 | 文档里出现具体人名的过失、评审时氛围紧张 | 把提问模板换成「为什么当时看起来是对的」;当众奖励主动认错的人 |
| 只在大事故写 | 事故库里只有 P0,小问题查无实据 | 事先写死触发判据(表 2),尤其是「监控没报出来」那条 |
| 写完没人看 | 文档躺在某个目录里,半年后没人搜得到 | 强制评审 + 广播给相关方;靠读书会与角色扮演把老复盘反复用起来 |
表 4 · 行动项分三类:一份复盘里最好三类都有
| 类型 | 目标 | 典型例子 | 见效速度 / 成本 |
|---|---|---|---|
| 防再发 | 让同样的原因不再产生同样的结果 | 推送前预检加目标集群健康检查;给危险命令加二次确认 | 见效慢、价值最高;常需改代码 |
| 减影响 | 它再发生时,伤害小一个量级 | 加降级开关、限流、把爆炸半径切成分区 | 中等成本;对还没想到的故障同样有效 |
| 缩短发现与恢复 | 把 MTTR 压下去 | 补一条症状告警、更新值班手册、把回滚做成一条命令 | 最便宜、最快见效;常常是当周就能做完的那几条 |
选型建议一句话:如果时间只够做一类,先做第三类。「防再发」经常要等一个季度的排期,而「补一条告警、把回滚做成一条命令」通常当周就能落地,并且对下一次完全不同的事故也管用。
这一章在日常里的落点非常密集:值班交接时先看有没有未关闭的行动项;事故评审会的第一句话该是「我们不找人,我们找条件」;面试里被问到「讲一次你搞砸的事故」,标准答卷不是自责,而是——影响多大、当时为什么那个判断是合理的、后来改了什么、那条改动有没有被验证过。工程之外还有一个很实际的用途:对外的公开复盘已经是大厂事故公关的默认形态,写得好坏直接影响客户信任。
① 一句话:事故的学费已经付了,复盘是唯一能把它换成资产的动作——题词「失败的代价是教育」是整章的经济学。
② 复盘 = 五件东西:事故记录、影响、缓解 / 解决的动作、根因、防止再发的后续行动;三个目标里本章特别强调最后一个(「尤其是」有效的预防措施被落实)。
③ 判断真假的最快办法:不读文档,去看行动项有没有被关掉。没有 owner、没有优先级、没进缺陷系统的行动项等于不存在。
④ 无指责的定义是一句假设:所有人都是好意,且用当时手上的信息做了他认为对的事。所以提问要换成「为什么这在当时看起来是对的」。这套做法源自医疗与航空,落成最该背下来的一句:「你没法『修好』人,但你可以修系统和流程。」
⑤ 指责的代价是瞒报。它污染的不是士气,是你赖以决策的事故数据——只剩 P0 的事故库是一份幸存者偏差样本。
⑥ 触发判据必须事先写死:用户可见的宕机 / 降级超阈值、任何数据丢失、值班人工介入、解决耗时超阈值、监控没报出来,外加「任何利益相关方都可以要求写」。最后两条最容易被跳过,也最值钱。
⑦ 复盘是协作产物;写完必须由没卷进这次事故的资深工程师按五问评审(数据留了吗 / 影响全吗 / 根因深吗 / 行动计划与优先级对吗 / 分享出去了吗)。没被评审的复盘跟从没存在过一样。
⑧ 让它变成文化的具体做法:月度精选、内部复盘小组、复盘读书会(常读几年前的老复盘)、Wheel of Misfortune 角色扮演演练,以及当众奖励主动认错的人;并且要度量——不只数篇数,还要直接问工程师「你真的觉得这里无指责吗」。最后,把写复盘的摩擦降下来(模板 + 自动填入事故数据):写的人越少,组织记忆越短。