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

可观测性

换个分母,可用性就变了

在 TT Lab 中继续学习

一句话总结

SLI 不是查询,而是定义。决定把什么算作良好事件、把什么算作有效事件,决定了数字的大部分。

为什么需要它

在故障会议上最常听到的一句话是“我们的可用性是百分之多少”。然而,面对同样 12 小时的数据,三个人分别拿出了 98.9%、97.7%、97.2%。三个人的计算都没有错,只是回答了不同的问题。

第一个人只把 5xx 响应算作失败。第二个人把 400 系列的响应也算作失败。第三个人统计的不是请求,而是分钟——在 1 分钟内,只要错误比例超过标准,就把这 1 分钟整个看作坏的分钟。

这个差异不是文字游戏,而是牵涉金钱和人力时间的差异。定义宽松,错误预算看起来还有剩余,危险的发布就会通过;定义过于严苛,在什么事也没有的夜里,人就会被叫醒。所以比起把目标(SLO)定为百分之多少,更应该先就要统计什么达成一致。

工作原理

Google SRE Workbook 用一句话描述 SLI——有效事件中良好事件的比例。所以写定义时,必须填好三个部分。

栏 要确定的内容 常见错误
事件(event) 把什么算作一个 把请求和时间窗口混在一起使用
良好事件(good) 把什么视为成功 只看状态码,把缓慢的成功算作成功
有效事件(valid) 把什么放进分母 连健康检查、机器人、内部调用也放进分母

三个部分中只要改动一个,数字就会变。尤其是分母最可怕。如果把每秒运行六次的健康检查放进分母,用户遭遇的失败就会被健康检查的成功稀释,故障会被挤到小数点之后。反过来,如果把分母收窄到一条用户旅程,同样的事故就会显得严重得多。

把事件的单位从请求改成时间,性质本身就变了。以请求为准,会对流量大的时段发生的事故给予更大的扣分。以时间为准,凌晨两点的 20 分钟故障和白天两点的 20 分钟故障,都同样算作 20 分钟。哪一种是对的,取决于服务对用户承诺了什么。

把延迟放进成功条件时,必须了解直方图的局限。请求数计数器有状态码标签,但延迟直方图通常没有。这样就无法用一个查询统计“状态码为 200 并且在 100 毫秒内完成的请求”。要把两个信号相乘使用,就必须修改埋点,让指标那样暴露——这就是定义决定埋点的时刻。

测量的位置也是定义的一部分。同一个请求,是在应用内部测量、在前端代理测量,还是在浏览器中测量,数字都不同。在应用内部测量,连接中断、响应没有到达用户的情况仍会被记为成功;在代理测量,代理自己宕机的那段时间会整个从记录中消失。无论在哪个位置测量,都要同时写明该位置看不到的失败是什么,以后才能相信这个数字。

还必须决定如何划分周期。按日历(每月 1 日重置预算)便于写进合同,却会让人在月底集中进行危险的发布。滚动窗口(最近 30 天)能防止这种临时突击,代价是过去的事故会纠缠一个月。并不存在两者都对的选择,而是要看哪一种能把团队的行为改变成想要的方向,再据此选择。

最后,不要把多个 SLI 平均成一个。可用性、延迟和正确性是不同的承诺,取平均之后,就会形成一种状态:没有一个承诺得到遵守,数字却看起来不错。分别测量、分别设定目标、分别消耗各自的预算,要容易处理得多。想把多个 SLI 合并成一个数字的需求,通常意味着想让报告变短,而一旦满足这个需求,就没有人能知道是哪一项承诺被打破了。

在现场相遇的样子

在一个支付服务中,曾经发生过这样的事。仪表板上的可用性超过了 99.9%,客户的咨询却不断进来。分母里包含了内部批处理调用,而这些调用占了整体的一半。当把分母收窄为只有用户请求时,同一时段的可用性下降到了 99.2%。不是数字变差了,而是到这时才得出了正确的数字。

反方向的事故也很常见。有个团队确立了“慢也是失败”的原则,并把阈值定为 100 毫秒。那一刻,可用性跌到了 85%,错误预算每天早上都被耗尽,发布被永远卡住。因为阈值不是出于用户的体感,而是出于负责人的偏好。修改定义时,要先用那个定义重新测量过去 30 天,确认能不能靠那个数字过日子。

下一项实验要做什么

用 Pod 的 Prometheus 中放入的 12 小时数据,亲手计算四种 SLI。以请求为准,只把 5xx 算作失败;把 400 系列也算作失败;把延迟 100 毫秒放进成功条件;最后把 1 分钟窗口当作事件来统计。看这四个数字相差多大,并换算成 30 天的允许停机时间,然后选出一个,以机器可读的格式进行声明,并就两种目标判断应该允许还是拦截发布。