关于证据,而非仪表盘
为什么 sigiro 的回答是一份排序列表、每一行都带一条可执行的查询,而不是一张图表。什么是基线,以及这个设计拒绝做什么。
问 sigiro 某个服务为什么不健康,它会返回一整块结构化的证据。这一块以一份排序列表开 头,列出值得关注的内容。每一项都带有一行含具体数字的摘要,以及一条可以运行来验证该 结论的 SQL 查询。
它不会返回图表,也永远不会。本页解释这个选择:sigiro 如何判定什么是异常、如何对发现 的内容排序、为什么查询要随答案一同交付,以及这个设计拒绝做什么。
读者看不见
对人来说,图表是个好答案。人扫一眼形状,注意到 14:10 处的台阶,然后继续往下走。几十 年来的可观测性工具都围绕这一刻构建,而且构建得很好。
但如今修复生产环境的是 agent,而 agent 看不见图表。它只能读文本。于是在修复之前,闭 环就断了:agent 能写补丁,却查不出哪里坏了,也判断不出自己的补丁是否奏效。
sigiro 就是弥合这个缺口的部分。它是仪表盘之下的一层,而不是仪表盘的替代品。如果你想 要面板,那就自带面板,让它们指向同一批表。
基线,而非阈值
这里没有什么需要配置,因为没有阈值需要配置。
每条序列都与它自身的近期历史作比较。sigiro 在 7 天窗口内把每个信号归入 5 分钟的桶, 然后对分桶后的序列运行贝叶斯在线变点检测。变点是序列统计特征发生变化的位置。整个检 测器唯一的结构性选择是一个方向:当变点之后的均值比之前更糟时,sigiro 才保留这次偏 移。错误更多、更慢、更吵、更贵。这是一次比较,而不是某个人随手挑的数字。
被检测的信号有五种:
| 信号 | 键 | 度量 |
|---|---|---|
error_rate |
服务与操作 | 每个桶中错误 span 的百分比 |
latency_p95 |
服务与操作 | 每个桶中 span 时长的 p95 |
log_volume |
服务 | 每个桶中的日志条数 |
error_log_rate |
服务 | 每个桶中 ERROR 级日志的百分比 |
profile_cost |
服务 | 每个桶中的性能剖析总开销 |
由此有两个推论,两个都会让人意外。
一直很慢的服务不是异常。如果 p95 整周都是两秒,那两秒对这个服务就是正常的,sigiro 什么都不会说。这是正确的,同时也是一个真实的局限:sigiro 告诉你的是什么_变了_,而长 期存在的故障并不会变。长期故障请用查询来找。
刚起来的服务器什么也查不出。检测器需要若干个桶,序列才有可供偏离的统计特征。常量序列 和极短序列在构造上都产生不了变点,这也是为什么这里同样没有“最少数据点”的选项。
5 分钟的桶也是刻意留的钝边。它让检测器只标记持续性的偏移,忽略一分钟的抖动。一分钟的 中断对这个检测器是不可见的,呼叫值班的告警系统才是应对它的合适工具。
从偏移到事件,再到排序列表
真正的故障会同时推动多个信号。错误上升、延迟上升、日志量上升,而这三者是一个事件,不 是三个。
因此在排序之前先做关联。落在同一个 5 分钟检测桶、或落在相邻桶中的两次偏移,会合并为 一个_事件_条目,该条目携带其成员偏移,以及在时间上邻近的可疑发布(如果有)。没有同伴 的偏移则保留为单信号条目。
然后对列表排序。第一排序键是严重程度。在此之内,异常类条目先按关联信号的数量排,再按 偏移幅度排,最后按原始差值排,每个键都从大到小。于是一个三信号事件会排在孤立的延迟偏 移之前——对于只有一个问题、注意力有限的读者来说,这才是正确的顺序。
幅度这一项值得单说一句,因为它天真的版本是错的。sigiro 使用对称相对变化
(after − before) / (after + before),而不是原始差值。原始差值无法把以微秒计的延迟
偏移与以百分比计的错误率偏移放在一起比较:不管微秒意味着什么,它每次都赢。相对形式是
无量纲的,因此不同信号的偏移之间能够诚实地排序。
不属于异常的条目排在最后:各类不一致,比如有错误却没有日志;你可以采取行动的覆盖缺 口;以及遥测管道自身的故障,比如重复 span 或孤儿 span。最后这一组回答的是读者本应最 先问的问题:这些数据到底可不可信?
查询随答案一同交付
每一条给出结论的行都带有 drill_down_sql:产生该结论的那条查询,可以直接发给
/v1/query。
这是我们会最坚决捍卫的设计部分,理由并不是方便,而是:可以验证的结论是另一种性质的结 论。你不必信任一份自动诊断,你的 agent 也不必。运行那条查询。把窗口放宽。改掉过滤条 件,看看结论是否还成立。
它还修掉了另一种方案修不掉的失败模式。无法验证的摘要只能被相信或被丢弃,而拿到一份不 可证伪摘要的 agent 会选择相信它。查询是可证伪的。当 sigiro 判断错了——它有时确实会错 ——查询就是你以极低成本发现这一点的途径。
还有第二个更不张扬的好处。查询也是你下一个问题的起点。复制它,改一个谓词,你就问出了 sigiro 从未想到的东西。这也是为什么下钻查询是针对真实表的原始 SQL,而不是一个指向已 保存视图的链接。
为什么是一次调用而不是五次
老式的排查形态是五次往返。查指标存储、推理,查追踪存储、推理,查日志存储、推理,查事 件存储、推理。每次往返都很便宜,每次模型调用都不便宜,于是模型主导了墙上时钟,总耗时 攀升到数十秒。而且随着上下文窗口被碎片填满,agent 还会丢掉早先的上下文。
/v1/diagnose 在 sigiro 内部并行运行这些查询,返回一整块关联好的结果。排序列表是在
Rust 里用这些查询已经返回的数据组装出来的,因此不额外花费任何查询,也不额外占用内
存。一次往返、一次模型调用、一幅连贯的图景。一个完整的块是几十 KB 的结构化证据,而不
是原始 span 转储,对现代上下文窗口而言只占很小一部分。
这个设计为此付出的代价是更大的响应体和更慢的单次调用。当读者在两次调用之间要推理数秒 时,这个取舍是对的;而对于每十秒刷新五十个面板的仪表盘来说,它是错的。sigiro 是为前 一种读者构建的。
这个设计拒绝做的事
一份诚实的清单,因为其中每一项都是合理的期待。
**它不会告警。**没有通知通道,也没有向人升级的路径。GET /v1/anomalies 是一张供你
轮询的表。预期的调用方是定时 agent 或 cron 作业。
**它不按业务影响排序。**排序是统计意义上的。sigiro 不知道你哪个服务在收款,所以后台 worker 的一次大幅偏移可能排在结账链路的一次小幅偏移之前。哪个服务重要由你知道;哪条 序列动了由 sigiro 知道。
**它不解释原因。**排序列表说明什么变了、变了多少、哪些信号一起动了,并且在时间上邻近 时点出一个可疑发布。时间上的相关不等于因果。证据和下钻查询是交给你的判断,或你 agent 的判断的,而诊断是一个假设,附带产生它的那条查询。
**它不画任何东西。**没有面板要搭,也没有面板会腐烂。这正是它的用意所在,同时也是某些 读者最会怀念的东西。
继续阅读
- 关于这些表,以及它们描述的是哪台机器
- 关于发布检测,以及你的 span 必须携带什么 —— 可疑发布从何而来
- API 参考 ——
Finding与DiagnosisBlock字段全集