故障复盘很容易开起来,很难做得有用。会开了,文档写了,四条改进项记下来了,六个月后同一个故障又发生一次,而某个人正好在找别的东西时翻出了那份旧文档。
在讨论这件事时,注意力几乎都落在"无指责"这个词上。这条原则确实重要,但失效的地方并不在那里。有大量组织把无指责执行得一丝不苟,复盘却什么也没有改变,原因很简单:他们把复盘本身当成了交付物,而不是当成产出交付物的手段。
判断你们的复盘是否有效,有一个简单到让人难堪的测试:过去六个月记录下来的改进项,实际完成的比例是多少? 如果答案是"大部分",那么无论流程长什么样,它都在起作用。如果低于一半,你们是在开会而不是在运行一个流程,换更漂亮的模板也救不了。少数几条带负责人和期限的事项,胜过一份没人有时间处理的详尽清单。
故障复盘到底为了什么
不是为了解释发生了什么。到故障收尾的时候,在场的人通常已经知道了。
它存在的意义,是找出让一个小问题长成大问题的那些条件,并且改掉其中几条。这和"什么坏了"是两个不同的问题,而这个区别在动笔的一刻就会显现出来。“数据库连接耗尽了"说的是什么坏了。“连接池饱和没有触发任何告警,而本该显示这一点的仪表盘并不在值班手册里"说的是一个条件,条件是可以动手处理的。
前一种说法能推出的行动,最多就是"这次重启后恢复了”。后一种说法能推出的是具体工作:加一条告警,在值班手册里补一行仪表盘链接。这个差别决定了半年后同样的事会不会再来一次。
第二个产物长期被低估,那就是一份关于这次事件的共同叙述。没有它,每个人都会留着自己那个版本,而这些版本会朝着各自原本就相信的方向漂移。一年之后,两个共同经历过同一次故障的团队,会讲出互相矛盾的两套说法。
无指责有非常具体的含义
它不是说没有人犯错。它是说,分析从这样一个前提出发:一个人依据当时能拿到的信息做出了合理判断,采取的行动事后被证明是错的,然后去问,这个行动在当时为什么看起来是对的。Google 的 SRE 手册系统讲述了这套做法,以及随之而来的失败模式。
这不只是出于善意,真正的原因是指责会摧毁信息。在一个故障会黏在个人身上的组织里,人们描述事件时会格外小心,会略去看起来不利的细节,并且把上报推迟到自己足够理解、能安全地讲清楚为止。这时复盘拿到的,是一份为了自保而修改过的叙述,这是所有可能的输入里最差的一种。
而且丢掉的往往正是最有价值的那部分:判断失误的那一刻屏幕上显示了什么,为什么当时觉得这些信息已经够了。恰恰是这一段最先被删掉。
在实践中,无指责是一种语言属性。“你为什么重启服务?“招来的是辩解。“你决定重启的时候,仪表盘上显示的是什么?“招来的是信息。第二个问题也更有用,因为它的答案是关于你们监控体系的事实,而不是关于某位同事的事实。
如果确实存在个人层面的绩效问题,请单独、直接地处理。放进故障复盘里处理会毁掉这次复盘,何况那也不是进行这种谈话的好场合。
写出能教会人的时间线
时间线是文档里最有价值的部分,同时也是最经常被写坏的部分:写成了一串抽掉了人的技术事件。
请写清楚人在什么时候知道了什么。不只是"14:32 重启了服务”,还要写这个人在 14:32 能看到什么、他相信正在发生的是什么。可以动手改的材料就在这里。因为一个由含混的仪表盘造成的十五分钟延迟是可以修复的问题,而一个只被记成空白的十五分钟延迟不是。
标出真正要紧的时刻:什么时候开始,什么时候第一次有人注意到,什么时候找对了人,什么时候搞清了原因,什么时候缓解,什么时候彻底解决。改进就住在这些时刻之间的间隔里,而检测所花的时间往往是其中最长的一段。
在想着怎么把恢复做得更快之前,先看这段检测间隔。在很多现场,没人察觉的时间比真正动手修复的时间还要长。
趁记忆还新鲜时就写,最好在一两天之内。一个人在某个具体时刻相信过什么,这种记忆消退的速度,远快于对事情本身经过的记忆。
很少只有一个根本原因
这个说法鼓励人们停在第一个说得通的解释上,而任何规模的故障都是链条,需要好几个条件同时成立才会发生。
有用的复盘会改为列出促成因素:触发故障的那次变更、本该拦住它却根本不存在的那个测试、发到了没人看的频道里的那条告警、对上一代架构才准确的那份值班手册。每一条都是独立的机会,你可以挑最便宜的两条来修,而不必去修链条上碰巧排在最前面的那一条。
“可以挑"这一点在实践中很关键。一旦写死只有一个原因,对策也就被锁定成一条,如果它恰好很贵,最后往往一条都不会落地。
这同时也减少了一种常见的扭曲。当一次复盘点名某个单一根本原因时,那个原因非常经常是"人为失误”,而这是描述不是解释,它恰好在调查本该开始的地方把调查关掉了。
让后续跟进真正发生
复盘的生死就在这里决定,而药方一点都不炫目。
减少条目。 两条能做完的,胜过八条做不完的。按它们究竟降低了检测时间、影响范围还是复发概率来排序,其余的砍掉。
写上具名负责人和日期,放进你们日常使用的工作跟踪系统,而不是放在文档里。只活在复盘文档里的条目,在做规划时是看不见的,而排期恰恰发生在那个时候。
在下一次复盘上检查未完成的条目。 单是这一个习惯,作用就超过任何模板。它让没做完的事变得可见,也把一串互不相干的会议变成一个真正的流程。
诚实地接受其中一些条目。 一个长期只完成一半承诺的团队,应该把承诺减半。记录一个没人会拨资源的意图,比记录一个"决定不做"更糟,因为后者至少是真的。
由此得到的改进,通常和我们的灾难恢复指南以及软件测试策略所讲的属于同一类:更快的检测、更小的影响范围,以及一套换个人也能运维的系统。
Mecanik 在服务器安全分析服务中,会对我们维护的系统做故障复盘。绝大多数情况下的模式都一样:技术原因很快就搞清楚了,真正昂贵的是那段没被发现的空白。
常见问题
做故障复盘的意义是什么? 是找出让小问题长成大问题的那些条件并改掉其中几条,这和"什么坏了"是两个问题。它还会产出一份关于事件的共同叙述,没有这份叙述,每个人都会留着自己的私人版本,而那个版本会朝他原本就相信的方向漂移。
无指责到底是什么意思? 是指分析假定一个人依据当时能拿到的信息做出了合理判断,行动事后被证明是错的,并追问它在当时为什么看起来是对的。它重要,是因为指责会摧毁信息:人们为了自保而修改叙述,复盘于是拿着一份被污染的输入在工作。
复盘应该找出唯一的根本原因吗? 不应该。任何规模的故障都是需要多个条件同时成立的链条,所以列出促成因素更有用。这样你可以修最便宜的两条,而不是链条上排第一的那条,也能避免以"人为失误"收尾,因为那是描述而不是解释。
事故时间线应该包含什么? 不只是系统做了什么,还要包含人在什么时候知道了什么。记下什么时候开始、什么时候第一次有人注意到、什么时候找对了人、什么时候搞清原因、什么时候缓解、什么时候解决。改进住在这些间隔里,而检测时间通常是最长的一段。
为什么复盘的改进项从来做不完? 因为它们活在文档里而不是工作跟踪系统里,排期的时候看不见。解决办法是减少条目、在日常系统里写上具名负责人和日期,并在下一次复盘开始时检查未完成条目,让没做完的事变得可见。
评论