深色模式
告警规则设计与 SLO 关联:多窗口多燃烧速率
摘要:本文面向生产 SRE / 平台工程师,把"该不该叫醒人"做成可量化规则。讲清告警三原则(症状而非原因、可行动、owner 明确),给出 Prometheus Recording/Alerting Rule 完整写法,并用 Google SRE Workbook 的**多窗口多燃烧速率(MWMBR)**把 SLO 错误预算接回 page/ticket 分级告警。适用 Kubernetes v1.28+,Prometheus 2.x/3.x。
适用版本与前提
- Kubernetes:v1.28+(控制面 SLI 可用其
/metrics;业务 SLI 来自应用指标) - 组件:Prometheus(Alerting/Recording Rule)、Alertmanager(路由/抑制/静默)
- 前提:已用 Recording Rule 固化 SLI(见
metrics-method.md);SLO 数值已与业务对齐
背景与问题
"告警疲劳"比没告警更危险:团队学会忽略 pager,真正重要的那条也被淹没。坏告警的共同特征——基于原因而非症状("CPU > 80%")、无明确动作、无 owner。好告警的反面:基于用户可见的症状、触发时人应当且能够行动、分级(page/ticket)对应不同响应。
SLO 把"症状"量化成"错误预算"。问题变成:怎么用错误预算的速度(燃烧速率)决定是否告警、多快告警。Google SRE Workbook 给出的答案是 多窗口多燃烧速率(Multi-Window Multi-Burn-Rate, MWMBR)。
告警三条铁律
- Alert on symptoms, not causes. 高延迟/错误率(RED 症状)才 page;高 CPU(USE 原因)只放 dashboard。
- Every alert must be actionable. 触发时 responder 能/应当做什么?不能就降为 ticket 或删除。
- 明确 owner 与 runbook. 无 runbook 的 page 是噪音。
核心概念
- SLI:可测量的服务质量指标(如成功请求比例)。
- SLO:SLI 的目标值(如 30 天 99.9% 成功)。
- 错误预算 = 1 − SLO = 0.1%:允许失败的额度。
- 燃烧速率(Burn Rate)= 当前错误率 / 错误预算。1 表示匀速烧完;14.4 表示 1 小时烧掉约 2% 的 30 天预算。
为什么单窗口阈值不够
"错误率 > 0.1% 就告警":短尖峰误报、慢泄漏漏报。MWMBR 用长窗口证明确实烧了有意义的一部分预算 + 短窗口确认问题仍在发生,既抓快烧(page)又抓慢漏(ticket),且修复后短窗口先回落、告警快速静默。
架构与原理:MWMBR 分级
Google SRE Workbook 对 99.9% SLO 的起始建议(30 天窗口=720h):
| 级别 | 长窗口 | 短窗口 | 燃烧速率 | 触发时烧掉预算 |
|---|---|---|---|---|
| Page | 1h | 5m | 14.4 | ~2% |
| Page | 6h | 30m | 6 | ~5% |
| Ticket | 3d | 6h | 1 | ~10% |
短窗口 ≈ 长窗口的 1/12,保证修复后告警快速停止。
生产实践:Recording Rule 预计算多窗口错误率
yaml
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
name: slo-checkout-rules
namespace: monitoring
labels:
prometheus: k8s
spec:
groups:
- name: slo-checkout-recording
interval: 30s
rules:
# 每窗口的错误比例 = 5xx / 总请求
- record: job:slo_errors_per_request:ratio_rate5m
expr: |
sum by (job) (rate(http_requests_total{job="checkout",code=~"5.."}[5m]))
/
sum by (job) (rate(http_requests_total{job="checkout"}[5m]))
- record: job:slo_errors_per_request:ratio_rate30m
expr: |
sum by (job) (rate(http_requests_total{job="checkout",code=~"5.."}[30m]))
/
sum by (job) (rate(http_requests_total{job="checkout"}[30m]))
- record: job:slo_errors_per_request:ratio_rate1h
expr: |
sum by (job) (rate(http_requests_total{job="checkout",code=~"5.."}[1h]))
/
sum by (job) (rate(http_requests_total{job="checkout"}[1h]))
- record: job:slo_errors_per_request:ratio_rate6h
expr: |
sum by (job) (rate(http_requests_total{job="checkout",code=~"5.."}[6h]))
/
sum by (job) (rate(http_requests_total{job="checkout"}[6h]))
- record: job:slo_errors_per_request:ratio_rate3d
expr: |
sum by (job) (rate(http_requests_total{job="checkout",code=~"5.."}[3d]))
/
sum by (job) (rate(http_requests_total{job="checkout"}[3d]))1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
两个常见复制粘贴错误
- 告警引用了不存在的窗口 recording rule → 该 pair 静默永不触发。每个告警窗口都要有对应 recording rule。
- 阈值是固定错误百分比而非 burn rate × 预算。99.9% SLO 的阈值是
14.4 * 0.001,不是14.4%。错误预算是 0.001,不是 0.1(那是百分比写法)。
生产实践:Alerting Rule(MWMBR)
yaml
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
name: slo-checkout-alerts
namespace: monitoring
labels:
prometheus: k8s
spec:
groups:
- name: slo-checkout-alerts
rules:
# 快烧:page(~2% 预算/1h 或 ~5%/6h)
- alert: CheckoutErrorBudgetBurnFast
expr: |
(
job:slo_errors_per_request:ratio_rate1h{job="checkout"} > (14.4 * 0.001)
and
job:slo_errors_per_request:ratio_rate5m{job="checkout"} > (14.4 * 0.001)
)
or
(
job:slo_errors_per_request:ratio_rate6h{job="checkout"} > (6 * 0.001)
and
job:slo_errors_per_request:ratio_rate30m{job="checkout"} > (6 * 0.001)
)
for: 2m
labels:
severity: page
slo: "99.9"
annotations:
summary: "Checkout 错误预算燃烧过快"
description: "当前燃烧速率约 14.4x,1 小时内将烧掉约 2% 的 30 天错误预算。"
runbook: "https://runbooks.internal/checkout/fast-burn"
# 慢漏:ticket(~10% 预算/3d)
- alert: CheckoutErrorBudgetBurnSlow
expr: |
job:slo_errors_per_request:ratio_rate3d{job="checkout"} > (1 * 0.001)
and
job:slo_errors_per_request:ratio_rate6h{job="checkout"} > (1 * 0.001)
for: 15m
labels:
severity: ticket
annotations:
summary: "Checkout 错误预算缓慢泄漏"
description: "持续慢烧,3 天将耗尽约 10% 预算,开 ticket 跟进。"1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
Alertmanager:分级路由
yaml
route:
receiver: default
group_by: ['alertname', 'job']
routes:
- match:
severity: page
receiver: pagerduty
continue: false
- match:
severity: ticket
receiver: issue-tracker
receivers:
- name: pagerduty
pagerduty_configs:
- service_key: <secret>
- name: issue-tracker
webhook_configs:
- url: https://hooks.internal/alerts/ticket1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
延迟 SLO 同样适用
把"错误比例"换成"慢请求比例"即可:sum(rate(request_duration_seconds_bucket{le="0.3"}[5m])) / sum(rate(request_duration_seconds_count[5m])) 作为 SLI,预算 = 1 − 0.95 = 0.05,阈值 = 14.4 × 0.05。同一套 MWMBR 框架同时覆盖可用性与延迟 SLO。
验证
bash
# 1. Recording rule 出数
kubectl exec -n monitoring deploy/prometheus-k8s -- \
promtool query instant http://localhost:9090 'job:slo_errors_per_request:ratio_rate1h{job="checkout"}'
# 2. 告警规则语法校验
promtool check rules slo-checkout-alerts.yaml
# 3. 模拟:人为拉高 5xx 看是否 page
# 在预发注入错误,观察 Alertmanager 是否收到 CheckoutErrorBudgetBurnFast
# 4. 查看 Alertmanager 分发
kubectl port-forward -n monitoring svc/alertmanager-operated 9093 &
# 打开 http://localhost:9093 看 Alerts / Silences1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
回滚与清理
生产危险
误删告警规则会让你对真实事故"静默"。删除前确认有替代覆盖:
bash
kubectl delete prometheusrule slo-checkout-alerts -n monitoring
# 删除后该 SLO 失去 page 能力,务必先评估1
2
2
故障排查
| 现象 | 根因 | 处理 |
|---|---|---|
| 快烧告警从不触发 | 缺某窗口 recording rule | 确认 5m/30m/1h/6h/3d 五个 rule 都在 |
| 阈值永远不命中 | 用错预算值(0.1 当成 0.001) | 99.9% SLO 预算是 0.001;检查表达式 |
| 告警抖动 | for 太短 / 短窗口过长 | 快烧 for: 2m,短窗口取长窗口 1/12 |
| 全量误报 page | cause-based 告警(CPU>80%) | 改为 RED/SLO 症状告警;原因类降 dashboard |
| Alertmanager 不路由 | label 不匹配 route.match | 检查 severity label 与 route match 一致 |
性能、容量与成本
- 长窗口
[3d]/[30d]recording rule 很贵,生产用降采样或外部长存后端;dashboard 显示"预算剩余"可用 Recording Rule 而非每次算原始。 - 告警评估频率(
evaluation_interval,默认 1m)影响灵敏度;过快增加 Prometheus 负载。 - 重复告警用
group_by+group_wait/repeat_interval收敛,避免同一事故刷屏。
常见坑
- 把原因类指标(CPU/内存/磁盘)配成 page → 误报疲劳。
- 阈值写死百分比而非 burn rate × 预算 → SLO 失联。
- 4xx 计入可用性错误 → 预算被"用户输错"吃掉。
- 告警无 runbook/owner → 响了没人动。
- 单窗口告警 → 尖峰误报或慢漏漏报。
替代方案与权衡
- Sloth / Grafana SLO:从一份 SLO spec 自动生成 Recording + Alerting Rule,减少手写错误,Git 友好。
- 错误预算策略(Error Budget Policy):预算红区自动冻结发布/升级响应级别,把 SLO 接回研发流程(见
metrics-method.md的 SLO 关联)。 - 多SLO多窗口工具:商业 SLO 平台提供自动分位数 SLI 与预算预测,代价是成本与锁定。
FAQ
Q:慢烧为什么还要告警? A:慢漏不会立刻 page,但月底会悄悄耗尽预算;ticket 级提醒让团队在预算红区前介入(如冻结高风险发布)。
Q:燃烧速率阈值能改吗? A:可以。14.4/6/1 是 SRE Workbook 起始值,按服务关键度与误报容忍调整;但"短窗≈长窗1/12"与"预算消耗比例自洽"两条约束要保持。
参考资料
- Google SRE Workbook - Alerting on SLOs(MWMBR),访问日期:2026-10-08。
- Prometheus 官方文档 - Alerting rules,访问日期:2026-10-08。
- Prometheus 官方文档 - Recording rules,访问日期:2026-10-08。
- Prometheus Operator - PrometheusRule CRD,访问日期:2026-10-08。
- Grafana 文档 - SLO(生成规则),访问日期:2026-10-08。
- Last9 博客 - Error Budgets & Burn Rate,访问日期:2026-10-08(交叉验证 MWMBR 数值表)。