曾经我们的告警群很热闹:CPU 超过 80%、内存超过 75%、某接口一分钟报错 5 次。值班人半夜打开图表,常常发现用户没有明显影响;真正发生登录大面积失败时,错误分散在多个实例,反而没有任何单机阈值触发。
2024 年我们开始从用户结果定义 SLO,再用错误预算燃烧率决定告警。资源指标仍然保留,但更多用于诊断,而不是直接把人叫醒。
图 1:一条值得打断人的告警,应该说明用户结果正在以多快速度恶化,以及谁能做什么。
先定义用户看到的成功
以“创建报表”为例,成功不是 HTTP 200,而是用户在 30 秒内得到可打开的报表,且数据查询完成。请求被网关接受但异步任务最终失败,不能计为成功。
SLI = 30 秒内完成且结果可读取的创建次数 / 有效创建请求数
SLO = 30 天滚动窗口内 SLI >= 99.9%有效请求排除明显非法输入,但不排除服务端权限判断或依赖失败。排除规则必须稳定,否则团队可以通过改变分母“优化”SLO。
错误预算把可靠性变成可消耗资源
99.9% 月度 SLO 意味着 0.1% 失败预算。燃烧率表示当前速度相对预算允许速度的倍数。短窗口高燃烧代表突发事故,长窗口持续燃烧代表慢性问题。
(
sum(rate(report_create_total{result!="success"}[5m]))
/
sum(rate(report_create_total[5m]))
) / 0.001生产规则使用 5m/1h 和 30m/6h 等多窗口组合:短、长窗口同时超过阈值才分页,降低一瞬间毛刺带来的噪声。
用数字说明会更具体。月度 99.9% 的 SLO 对应错误预算 0.1%,即 30 天里允许约 43 分钟失败。一次持续 10 分钟、错误率 6% 的事件,5 分钟窗口的燃烧率是 60 倍,足以分页;同一事件摊到 1 小时窗口只剩 10 倍。两个窗口不是重复报警,而是区分“正在发生的事故”和“一瞬间的毛刺”。
| 级别 | 条件示例 | 动作 |
|---|---|---|
| Page | 5m 与 1h 高速燃烧 | 立即叫醒值班,目标 5 分钟确认 |
| Ticket | 30m 与 6h 中速燃烧 | 工作时间处理,阻止继续消耗 |
| Dashboard | 资源接近容量但 SLO 正常 | 容量规划,不打断值班 |
告警内容必须支持第一步行动
每条告警包含:受影响用户结果、开始时间、当前燃烧率、主要维度、最近发布、负责人、仪表盘和 Runbook。不要只发一条“error rate high”。
Runbook 的第一屏回答:如何确认影响;最快止损动作;哪些操作有风险;需要升级给谁。长篇原理放在后面。事故中人的工作记忆很有限,文档要按执行顺序写。
按所有权路由,而不是按技术栈
登录 SLO 由身份团队负责,即使根因发生在 Redis;报表创建由报表团队负责,即使错误来自队列。拥有用户结果的团队先接警,再根据 trace 与依赖图升级。按“数据库群”“Node.js 群”广播,通常会让每个人都以为别人会处理。
每次告警都要有结局
我们给告警记录分类:真实事故、已知维护、阈值不合理、数据错误、无行动价值。每月看分页数量、确认时间、无行动占比和重复根因。无法导致任何动作的告警要降级或删除。
改造后值班并没有变得轻松,而是打断更少、每次打断更接近真实用户影响。SLO 的价值不是制造一个新的百分比,而是让团队明确什么结果值得保护,并把注意力留给正在快速消耗可靠性预算的问题。告警只是这条链路的一环:从用户结果定义信号、再到告警关闭后的复盘闭环,完整的做法在可观测性不是一块 Grafana 大屏里展开。