TT Lab
博客 开始
学习 学习路径 课程

故障指挥与复盘

时间线重建

在 TT Lab 中继续学习

一句话总结

时间线要用证据而不是记忆来建立。只有把不同的时区和偏差的时钟对齐到同一个基准时钟后,才能说出“什么在先”, 而且要知道同一症状可能来自不同原因,才不会回滚错东西。

为什么需要它

事故结束后,大家都记得“部署之后马上就出错了”。可这个“马上”,是人用眼睛对齐服务器时钟记录的日志 得出的结果。如果一台服务器的时钟慢了 3 分钟,那台服务器的错误看起来就像发生在部署之前;另一台服务器是 KST, 部署记录是提交者的时区(+0900 和 -0800 混在一起),开关记录又是另一个时区。在这种状态下排定顺序, 就会把无辜的部署当成元凶,或者把真正的原因挤到窗口之外。

而且“从部署前后开始出错”这同一症状来自不同的原因:新版本的 bug、同一时间加入的配置变更、下游依赖 服务的延迟——三者在图表上都同样是 5xx 飙升。可正确的行动分别是回滚应用、回滚配置、什么都不回滚。

它如何工作

第一,选定一个基准时钟。 通常以用 NTP 校准的负载均衡器或指标系统为基准。如果同一个请求同时记录在基准和 服务器两边(请求 id),两个时刻之差就是该服务器时钟的偏差。要用多对而不是一对的中位数,才不会被处理延迟 干扰。时区不是偏差——先读时区标记换算成同一时刻才是第一步。

第二,把变更排成一行。 把部署、配置、开关全部换算成 UTC 时刻排序,把对齐后时钟的首个错误之前一定窗口 (例如 30 分钟)内的变更列为嫌疑。

第三,用模式分辨原因。 滚动部署的 bug 会从装上新版本的服务器开始依次出错。配置通常会同时进入所有 服务器,所以错误也同时开始。依赖服务的问题会先出现该服务的延迟指标飙升,错误的形态 (超时代码、耗时数秒的响应)也不同。

第四,给区间命名。 把检测延迟(首个错误 → 告警)、缓解时刻(错误率降到基准以下并保持的第一个时点) 定为数据定义,就不会出现每个人数字不同的情况。

测量偏差的计算是这样的。如果同一请求在两边都有记录,就收集多个差值取中位数。

req=r0700123  기준(LB) 05:21:40.112Z   web-2 가 찍은 시각 05:18:12.125Z   차이 -207.987
req=r0700131  기준(LB) 05:21:44.530Z   web-2 가 찍은 시각 05:18:16.541Z   차이 -207.989
…                                                         가운데 값 ≈ -208초 (web-2 가 208초 뒤처짐)
맞춘 시각 = 서버가 찍은 시각 − 어긋남  →  05:18:12 − (−208) = 05:21:40

而同一症状的三种原因,信号和行动都不同。

原因 错误开始的形态 一并出现的信号 要做的事
新应用版本的缺陷 从装上新版本的服务器开始依次出现 各服务器的开始时间按滚动间隔错开 把应用回到上一个版本
配置部署 所有服务器在同一秒 紧随配置部署之后,早于应用部署 把配置回到上一个修订
依赖服务延迟 所有服务器一起,超时代码 依赖服务的 p99 先飙升 不回滚,处理依赖一侧

现场遇到的情形

常见的场面是“三个都是部署的锅,全部回滚吧”。可是因依赖服务而出的错误,回滚了也照旧,而且回滚本身 又是一次变更,会带来新的风险。另一个常见场面是一台时钟偏差的服务器制造出“部署前就有错误”的错觉, 让部署变得无辜。这两种情况,只要对齐时钟、看清模式,几分钟内就能分辨。

git 记录也是陷阱。默认的 git log 用提交者的时区打印日期,并且最新的在上面。在美国晚上 9 点做的提交 显示为 Tue Nov 3 21:14 -0800,按 UTC 却是 11 月 4 日凌晨。按日期字面排序,这次部署就会被挤到前一天, 从“事故前 30 分钟内的变更”列表中漏掉。收集变更记录时,一定要先换算成时刻(UTC)再排序。

检测延迟由告警规则决定。“每分钟错误率超过 5% 连续 3 分钟”这条规则,再快也要等第三分钟结束才响, 错误缓慢上升时还会更晚。在时间线中把检测延迟提取成数字,复盘时“告警晚了”的感觉 就会变成“规则决定了最少 3 分钟,实际 4 分 12 秒”这一事实,要修的地方(规则的阈值或持续时间)也就清楚了。

接下来的实验要做什么

对照两台服务器的日志(时区和时钟各不相同)、负载均衡器样本、git 部署记录、开关记录、告警和指标,测量时钟偏差, 求出基准时钟下的首个错误和嫌疑变更、检测延迟和缓解时刻。最后,把三个服务的同一症状按原因区分, 只回滚该回滚的。