排查要做的第一件事,是把时间钉死
一句话总结
调查时首先要做的,不是找原因,而是钉死时间。只有确定了开始和结束,才能统计影响范围,这样才能为每个假设写下“数据中应该看到什么”。
为什么需要它
有一次,接到支付错误的报告,四个人聚到了一起。一个人说是“今天早上的发布”,一个人说是“某个客户方的流量激增”,一个人说是“数据库连接耗尽”。三个说法都似是而非,而且每一个都要花 30 分钟去调查。两个小时后,原因是第四个候选项,而到那时为止,没有一个人用数据确认过事故是什么时候开始的。
如果先钉死开始时刻,当场就会排除两个。发布是在事故开始 40 分钟之后才上线的,而问题客户方的流量在整个事故期间与平时一样。这两件事都只需要查询一次就能得到。只要调换一下顺序,两个人的 30 分钟就能省下来。
这里重要的不是记忆,而是记录。人的记忆会说“好像是从 10 点左右开始的”,而数据会准确地说出“错误比例首次超过 0.05 的那一刻”。调查的第一行,永远应该是那个条件表达式和那个时刻。
工作原理
顺序是六个步骤。
一,钉死时刻。 先固定把什么视为“事故”的条件表达式。例如“在 5 分钟窗口中 5xx 比例超过 5%”。然后通过范围查询,找出该条件第一次为真的时刻和最后一次为真的时刻。此时不要写成绝对时刻——时区和夏令时会毁掉复盘。如果写成“距今多少分钟之前”,以后在任何人的屏幕上都能重新画出同样的区间。
二,统计范围。 有多少次失败,各个处理器各占多少。这里常见的陷阱是把窗口截得太短。如果对事故区间覆盖不足,失败次数就会整个缺失。只挑出条件为真的那些分钟,再把这些分钟的增加量相加,就不会被窗口长度左右。
三,先写下假设和预测。 如果只写假设,以后看到数据时,就会去迁就解释。先写下“如果这个假设成立,哪些指标应该呈现什么样子”,那么当数据不是那个样子时,就能放弃假设。
四,排除。 证伪比确证便宜。如果是某一个处理器的缺陷,就应该只有那个处理器的错误率突起,但如果四个处理器都以同样的比例突起,这个假设就完了。如果是流量激增,请求率就应该上升,但如果事故区间的请求率与前一小时只相差 6%,这个假设也完了。
五,用其他信号确证剩下的。 排除之后剩下的那一个,用与最初条件表达式不同的指标来确认。再看同一个指标,不是确证,而是同一句话的重复。如果是用错误率来定义事故,确证就应该从队列深度或饱和度这样与错误率独立变化的信号中去找,并且要以分钟为单位叠加查看,这个信号在同一区间内是否以同样的形态变化。
六,留下机器可读的时间线。 Google SRE Book 的事后分析文化一章所强调的,同样是“留下什么”。每个事件用一行写下相对时刻和依据指标。人读的句子以后无法做成统计,而表格可以。
在现场相遇的样子
高效故障排查一章反复说的,就是这个顺序。然而事后分析中最常缺失的一栏,不是原因,而是检测延迟。影响开始的时刻与告警响起的时刻之间的差,几乎总是没有记录。可是这个数字决定了下个季度要修复什么。如果在 5 分钟窗口上设置 10 分钟的持续条件,检测就要花 11 分钟,而如果事故只有 20 分钟,人是在过了一半之后才被叫醒的。
另一个是把不相关的事件放到同一条时间线上。在同样的 12 小时里,另有一段只有尾部延迟突起的区间,它与错误事故在时间上并不重叠,却常常被一起写进复盘文档。放上时间线时,要在每一行一并写下依据指标,以后才能留下这两者是不同事件的记录。
第三个是会议朝着增加假设数量的方向发展。候选项一旦有六个,人就会分散到六个方向,各自打开不同的指标,看着不同的时间轴。调查所需要的不是增加候选项的能力,而是便宜地排除的顺序。从预测最具体的假设开始确认,每查询一次就掉一个,当剩下的候选项不超过两个时,从那时起即使多人介入也不会重复。把用于排除的查询及其结果原样贴上,对后来的人来说,比结论更有价值。
最后是不留下调查过程本身。如果不写下被排除的假设和被排除的依据,下次事故中就会为同样的假设再花 30 分钟。被排除的记录,与原因记录一样有价值。
下一项实验要做什么
按顺序调查 Pod 的 Prometheus 12 小时数据中的一次事故。固定条件表达式,以相对时刻钉死开始和结束,只挑出条件为真的那些分钟来统计失败次数和各处理器的分布。先写下三个假设及各自的预测,再通过查询排除其中两个,并用其他指标确证剩下的那一个。然后制作机器可读的时间线和事后分析材料,最后写出比现在更快发现的检测规则,并用同样的 12 小时数据验证它能快多少,以及是否会因此出现无效呼叫。