不把「正常」写成数字,那就只是事故
一句话总结
在破坏之前,“正常”必须已经用数字写下来。没有这个数字,实验就和事故无法区分。
为什么需要它
运维复盘中最常听到的一句话是“当时有点慢”。有点慢是慢多少,平时是多少,没人说得出来。在这种状态下注入一个故障,会发生什么?屏幕变红,人们蜂拥而来,有人按下回滚,最后谁也说不清学到了什么。这不是实验,只是一次事故。
混沌工程的不同之处不在于“破坏”。而在于在破坏之前,先用文字写下要测量什么、相信会发生什么、何时停止。混沌工程原则把“把系统的正常行为定义为可测量的输出”列为第一条,原因就在这里。它明确要求,不用内部状态(CPU 使用率、堆大小),而是用从外部看得见的输出——请求成功的比例、响应返回的时间——来定义。因为内部指标在没有故障时也会波动,而真正出了故障时又可能看起来毫无异常。
工作原理
稳态通常用两条轴来写:可用性和延迟。这两条轴互不相同,必须一起写。即使 99.9% 的请求成功了,p95 响应如果是 3 秒,用户仍会觉得它坏了;反过来,响应只要 20 毫秒,但每二十次失败一次,同样也是坏了。只看一条轴,就会错过一半的实验结果。
延迟不用平均值,而用分位数来写。平均值会稀释缓慢的尾部。一百个请求中有五个耗时 2 秒,其余耗时 20 毫秒,平均值是 120 毫秒,看起来很正常,但 p95 是 2 秒,问题就会原样暴露。用户感受到的不是平均值,而是自己的那一个请求。
정상 상태 기술의 예 (이 코스의 실습에서 실제로 쓰는 형식)
availability_ratio_min : 0.98 20초 동안 정적 경로 200회 중 196회 이상 성공
p95_ms_max : 70 CPU 를 쓰는 경로의 95분위 응답이 70밀리초 이하
此外还要一并写明中止条件。比如“成功率降到一半以下就立即恢复”这样的句子。实验进行中,判断力会变得模糊。总觉得再看一会儿就能发现原因,而且恢复之后重新设置又很麻烦。为应对这一刻,需要一句在冷静时写下的话。
最后确定爆炸半径(blast radius)。只动一个副本,只在一个命名空间内,只占 1% 的流量。实验只需大到足以验证假设即可,再大就只会增加风险而没有收获。
测量窗口的长度和样本数也要提前定好。窗口太短,会把恢复很快的故障整个漏掉;样本太少,一次失败就会让成功率波动 5%,无论看到什么都显得有意义。本课程的实验在 20 秒窗口内每秒发 10 次可用性请求,共收集 200 个。一次失败相当于 0.5%,因此 0.98 这条界线就意味着“最多允许失败四次”,解读就变得明确。比起边界的数字,更重要的是知道这个数字对应多少个请求,这才是读懂实验的感觉。
在现场相遇的样子
在 Kubernetes 中,“正常”分成好几层,所以更要小心。Pod 处于 Running 和处于 Ready 是两回事,处于 Ready 与被挂入 Service 的端点并接收流量,又是另一回事。容器是否就绪由 readinessProbe 决定,其结果通过 Pod 的状况(conditions)体现出来。即使三个绿灯全亮,用户的请求仍然可能失败。
所以在实战中,不要把集群状态当作“稳态”。应当把从外部实际发送请求得到的结果当作稳态,把集群状态作为解释它的辅助证据。把这个顺序颠倒过来,就会出现那种常见的场景:仪表板全是绿色,只有客服中心忙得不可开交。
测量基线时还有一个常犯的错误:把尚未稳定的状态当作基线。刚部署完,缓存是空的,镜像也刚拉取下来,响应比平时慢。把这个数字当作基线,之后所有的比较都会变得松散。要做到两点:等滚动更新结束、探针稳定之后再测量;测量基线期间什么都不要动。本实验的记录工具之所以在基线测量窗口内出现新 Pod 时拒绝记录,理由也是如此。
再补充一点,稳态应当是团队达成共识的一句话。一个人定的标准,出了事件时一定会动摇。必须能说明“p95 70 毫秒”这个数字从哪里来,为什么既不是 60 也不是 100;这个说明通常是实测与判断的结合,比如“平时测得 33 毫秒,而两倍以内用户感觉不到”。只有数字而没有依据的标准,到下个季度会毫无理由地被改掉。
下一项测验要确认什么
确认为什么要把稳态定义为从外部可见的输出,为什么延迟要用分位数而不是平均值来写,以及中止条件和爆炸半径在实验中各起什么作用。