SLO 工程:错误预算、Burn Rate、多窗口多燃烧率告警
某团队定了 99.9% 可用性 SLO——30 天滚动窗口内允许 0.1% 的请求失败。双十一前一周,SLO Dashboard 上的预算线已经贴地:不是一次灾难性事故,而是 Q3 连续 12 次”小发布”——每次 p99 从 120ms 漂到 130ms,每次都没触发 CPU 告警,每次都没人打开 SLO 面板。直到大促前冻结发布窗口,才发现预算早已耗尽。
这个故事概括了 SLO 工程的核心挑战:定一个数字很容易,让这个数字驱动日常决策很难。SLO 的价值不在数字本身,在于它创建了一条从”业务可接受的不可用程度”到”工程团队每天的开发节奏”的完整决策链。
本文从 SLI 选择、错误预算计算、Burn Rate
告警到组织落地,给出可直接粘贴到 Prometheus 的 Recording
Rule 与 Alert Rule 模板。文中 Burn Rate 阈值来自 Google
Site Reliability Workbook 第 5 章(A
级来源);PromQL 在本地用 promtool check rules
校验语法(本环境未安装 promtool/Docker 拉取镜像失败,规则以
Workbook 原文为准,部署前请在目标 Prometheus
版本上验证)。
一、SLO 的概念框架
1.1 四个术语,四种用途
| 术语 | 英文 | 定义 | 谁关心 |
|---|---|---|---|
| SLI | Service Level Indicator | 对服务某维度的实测值 | SRE、开发 |
| SLO | Service Level Objective | SLI 在窗口内应满足的目标 | 工程 + 产品 |
| SLA | Service Level Agreement | 违反 SLO 时的合同后果 | 法务、客户 |
| 错误预算 | Error Budget | \(1 - \text{SLO}\),窗口内允许的不合格量 | TL、PM、SRE |
SLI 例子:“过去 5 分钟,checkout API 成功请求占比 99.93%”。
SLO 例子:“30 天滚动窗口内,checkout 成功率 ≥ 99.9%”。
SLA 例子:“若季度可用性 < 99.5%,按合同退还 10% 月费”。SLO 通常比 SLA 更严——给自己留缓冲。
错误预算 不是”可以浪费的 downtime”。它是发布节奏控制器:预算充裕时承担发布风险;预算紧张时冻结 feature,只做可靠性工作。
1.2 错误预算的数量级
SLO 每多一个 “9”,预算缩小 10 倍:
| SLO | 30 天窗口允许失败比例 | 等价 downtime(连续不可用) | 等价失败请求(假设均匀流量) |
|---|---|---|---|
| 99% | 1% | 7.2 小时 | 每 100 请求 1 次失败 |
| 99.9% | 0.1% | 43.2 分钟 | 每 1000 请求 1 次失败 |
| 99.99% | 0.01% | 4.32 分钟 | 每 10000 请求 1 次失败 |
| 99.999% | 0.001% | 26 秒 | 每 100000 请求 1 次失败 |
选 SLO 之前先算一遍:这个窗口内我们客观上能容忍多少失败? 很多团队拍 99.99%,却从未验证过能否在现有架构下达到——结果要么永远超预算,要么 SLO 形同虚设。
1.3 SLO 与监控的区别
指标体系设计 中的 USE/RED/Golden Signals 回答”该采集什么”。SLO 回答”什么程度算不可接受、何时该停发版”。
监控告警常见反模式:CPU > 80% 就
Page——CPU 高不一定伤害用户。SLO Burn Rate
告警与用户可感知失败等价:只有正在加速消耗错误预算时才值得叫醒人(详见
告警体系)。
二、SLI 的选择与定义
2.1 一个服务 2–4 个 SLI 足够
SLI 过多等于没有 SLI——没人能同时盯 20 条曲线。Google SRE Workbook 建议按服务类型选 2–4 个:
| SLI 类型 | 适用服务 | 典型定义 | 常见误区 |
|---|---|---|---|
| 可用性 | 在线 API | 非 5xx 且非超时的请求 / 总请求 | 把 429 算进失败 |
| 延迟 | 同步 API | p99 延迟 < 阈值 | 用均值代替分位数 |
| 吞吐 | 批处理/队列 | 单位时间处理条数 ≥ 目标 | 与可用性 SLI 重复 |
| 持久性 | 存储/DB | 写入后可读且未丢失的比例 | 与备份 RPO 混淆 |
| 新鲜度 | 数据管道 | 数据滞后 < N 分钟 | 用批处理完成率代替 |
2.2 可用性 SLI:什么算”坏请求”
Google SRE Book 第 4 章:可用性 SLI = 好事件 / 合法事件。
# 好请求:HTTP 2xx/3xx/4xx 中排除"服务责任"之外的码
sum(rate(http_requests_total{status!~"5.."}[5m]))
/
sum(rate(http_requests_total[5m]))
必须排除的请求类型:
- 429 Too Many Requests:通常是客户端或网关限流,不是服务端故障。
- 401/403(视产品而定):认证/授权失败可能是预期行为,不应计入服务可用性。
- 499 / 客户端超时取消:若由客户端主动断开,是否计入需与产品对齐——默认建议排除。
必须纳入的失败:
- 5xx:服务端错误。
- 超时:服务端未在 deadline 内响应——即使用户看到”网络错误”,也是服务 SLI 失败。
# 更严格的可用性 SLI(5xx + 超时)
sum(rate(http_requests_total{status=~"5.."}[5m]))
+
sum(rate(http_request_duration_seconds_count{le="+Inf"}[5m])
- rate(http_request_duration_seconds_count{le="2"}[5m]))
第二条假设 SLO 延迟阈值为 2s——超时请求计入不可用。阈值应与 SLO 文档一致。
2.3 延迟 SLI:必须用分位数
用 p50 做延迟 SLI 会在尾部退化时完全无感。SLO 延迟 SLI 标准写法:
\[\text{SLI}_{\text{latency}} = \frac{\#\{r \mid \text{latency}(r) < T\}}{\#\{r\}}\]
Prometheus histogram 实现(假设 bucket 上限覆盖 SLO 阈值 \(T\)):
sum(rate(http_request_duration_seconds_bucket{le="0.2"}[5m]))
/
sum(rate(http_request_duration_seconds_count[5m]))
上式表示”p99 以下”若 bucket 为 0.2s——实际应用用
histogram_quantile 做报告,用
bucket 比例做SLI 合规判断:
# 过去 5 分钟 p99 是否 < 200ms(用于 Dashboard,非直接 SLI 合规)
histogram_quantile(0.99,
sum(rate(http_request_duration_seconds_bucket[5m])) by (le)
) < 0.2
SLI 合规应使用窗口内低于阈值请求占比,而非单点 p99——后者对样本量敏感。
2.4 测量窗口:1 分钟 vs 5 分钟 vs 1 小时
| 窗口 | 优点 | 缺点 | 用途 |
|---|---|---|---|
| 1m | 反应快 | GC/抖动噪声大 | 不推荐单独做 SLO 窗口 |
| 5m | 平衡 | — | SLI 采集、Recording Rule 默认 |
| 1h | 平滑 | 检出慢 | Burn Rate 短窗口告警 |
| 30d 滚动 | 与预算对齐 | 计算复杂 | SLO 合规、预算剩余 |
Google SRE Workbook 推荐:SLI 用 1–5 分钟滚动窗口采集,SLO 合规用 30 天滚动窗口,告警用 1h/6h/3d 多窗口 Burn Rate。
2.5 SLI 采集的三条工程路径
路径 A:应用内 Prometheus 指标(最常见)
// Go + prometheus client:按 status 计数
httpRequestsTotal.WithLabelValues(method, path, strconv.Itoa(status)).Inc()路径 B:OpenTelemetry Span Metrics
OpenTelemetry Collector spanmetrics
connector 从 trace 生成 RED 指标——与 OpenTelemetry
深入 中的 pipeline 一致。适合已全量 OTel
但未在每个服务手工埋 counter 的团队。
路径 C:Synthetic / Black-box 探测
从用户视角探测 /health 或关键 API——与 Events 与变更关联 中的
black-box 互补。缺点:覆盖路径有限,不能替代服务端 SLI。
三、错误预算的工程计算
3.1 基本公式
30 天滚动窗口,SLO = 99.9%:
\[\text{ErrorBudget}_{\text{requests}} = (1 - 0.999) \times N_{30d}\]
\[\text{ErrorBudgetRemaining} = 1 - \frac{\text{BadRequests}_{30d}}{(1 - \text{SLO}) \times N_{30d}}\]
若流量均匀,43.2 分钟连续不可用 ≈ 耗尽 30 天预算(99.9%)。
3.2 滚动窗口 vs 日历窗口
| 类型 | 行为 | 适用 |
|---|---|---|
| 日历月 | 每月 1 日重置 | 与财务/合同对齐 |
| 滚动 30d | 任意时刻看过去 30 天 | 更平滑,Google 推荐 |
Prometheus 原生不内置”滚动 30 天 sum”——常用近似:
# 30 天失败请求(increase 近似)
sum(increase(http_requests_total{status=~"5.."}[30d]))
大窗口 increase() 在 Prometheus 2.x
可用,但长窗口 query 昂贵——生产环境用
Recording Rule 每 5 分钟预聚合。
3.3 Burn Rate 定义
燃烧率(Burn Rate)= 当前错误率相对于”刚好耗尽预算”的错误率之比。
\[\text{BurnRate} = \frac{\text{error\_rate}}{\text{SLO\_error\_rate}} = \frac{\text{error\_rate}}{1 - \text{SLO}}\]
- Burn Rate = 1:按当前速度,将在窗口结束时刚好耗尽预算。
- Burn Rate = 14.4:当前速度是”刚好耗尽”速度的 14.4 倍。
推导 14.4 的由来(Google SRE Workbook):30 天 = 720 小时。若 1 小时内以 14.4 倍速度烧预算,则消耗 \(\frac{14.4}{720} = 2\%\) 的 30 天总预算。这是 Page 级告警的敏感度/误报率折中。
3.4 Recording Rules 模板
以下规则假设:job 标签 checkout,SLO
99.9%,指标
http_requests_total{job="checkout"}。
# slo-recording-rules.yml
groups:
- name: slo_checkout_availability
interval: 30s
rules:
# 5 分钟错误率
- record: slo:checkout:errors5m
expr: sum(rate(http_requests_total{job="checkout",status=~"5.."}[5m]))
- record: slo:checkout:total5m
expr: sum(rate(http_requests_total{job="checkout"}[5m]))
- record: slo:checkout:error_ratio5m
expr: slo:checkout:errors5m / slo:checkout:total5m
# Burn Rate(相对 SLO 错误率 0.001)
- record: slo:checkout:burnrate5m
expr: slo:checkout:error_ratio5m / 0.001
- record: slo:checkout:burnrate1h
expr: |
sum(rate(http_requests_total{job="checkout",status=~"5.."}[1h]))
/ sum(rate(http_requests_total{job="checkout"}[1h]))
/ 0.001
- record: slo:checkout:burnrate6h
expr: |
sum(rate(http_requests_total{job="checkout",status=~"5.."}[6h]))
/ sum(rate(http_requests_total{job="checkout"}[6h]))
/ 0.001
- record: slo:checkout:burnrate3d
expr: |
sum(rate(http_requests_total{job="checkout",status=~"5.."}[3d]))
/ sum(rate(http_requests_total{job="checkout"}[3d]))
/ 0.001
# 30 天预算剩余(百分比,近似)
- record: slo:checkout:budget_remaining_ratio
expr: |
1 - (
sum(increase(http_requests_total{job="checkout",status=~"5.."}[30d]))
/ (0.001 * sum(increase(http_requests_total{job="checkout"}[30d])))
)将 0.001 替换为 \((1 - \text{SLO})\) 即可适配其他
SLO 目标。
四、多窗口多燃烧率告警
Google SRE Workbook 第 5 章给出 6 条规则——3 条 Page、3 条 Ticket。核心思想:短窗口 + 高燃烧率 抓突发;长窗口 + 低燃烧率 抓慢性退化。
4.1 Page 级规则(3 条)
| 规则 | 窗口 | 燃烧率阈值 | 预算消耗 |
|---|---|---|---|
| A | 1h | ≥ 14.4× | 2% / 30d |
| B | 6h | ≥ 6× | 5% / 30d |
| C | 3d | ≥ 1× | 10% / 30d(持续) |
# slo-alert-rules.yml — Page 级
groups:
- name: slo_checkout_pages
rules:
- alert: CheckoutSLOBurnRateCritical1h
expr: slo:checkout:burnrate1h > 14.4
for: 2m
labels:
severity: page
service: checkout
annotations:
summary: "checkout SLO 1h 燃烧率 {{ $value | printf \"%.1f\" }}×"
description: "1 小时内以当前速度将消耗约 2% 月错误预算。见 Runbook: ..."
- alert: CheckoutSLOBurnRateCritical6h
expr: slo:checkout:burnrate6h > 6
for: 5m
labels:
severity: page
service: checkout
- alert: CheckoutSLOBurnRateCritical3d
expr: slo:checkout:burnrate3d > 1
for: 1h
labels:
severity: page
service: checkoutfor: 子句用于抑制瞬时尖峰——Workbook 建议
Page 规则加 2–5 分钟 pending。
4.2 Ticket 级规则(3 条)
| 规则 | 窗口 | 燃烧率阈值 | 预算消耗 |
|---|---|---|---|
| D | 6h | ≥ 1× | 10% / 30d |
| E | 1d | ≥ 1× | 10% / 30d |
| F | 3d | ≥ 1× | 10% / 30d |
- name: slo_checkout_tickets
rules:
- alert: CheckoutSLOBurnRateWarning6h
expr: slo:checkout:burnrate6h > 1
for: 15m
labels:
severity: ticket
service: checkout
- alert: CheckoutSLOBurnRateWarning1d
expr: |
sum(rate(http_requests_total{job="checkout",status=~"5.."}[1d]))
/ sum(rate(http_requests_total{job="checkout"}[1d]))
/ 0.001 > 1
for: 1h
labels:
severity: ticket
- alert: CheckoutSLOBurnRateWarning3d
expr: slo:checkout:burnrate3d > 1
for: 3h
labels:
severity: ticketTicket 告警进入 Jira/Slack,不 打值班电话。
4.3 敏感性与噪音的权衡
flowchart LR
subgraph fast [快速检出]
A1["1h 窗口"] --> A2["燃烧率 14.4×"]
A2 --> A3["Page"]
end
subgraph slow [慢性退化]
B1["3d 窗口"] --> B2["燃烧率 1×"]
B2 --> B3["Ticket"]
end
反模式:短窗口 + 低燃烧率(如 5m + 2×)→ 每次 GC 都可能触发 → 告警疲劳(参见 告警体系 中”Page 只用于 Burn Rate”原则)。
反模式:长窗口 + 高燃烧率(如 3d + 14.4×)→ 几乎永不触发 → 假安全感。
4.4 Alert 标签与 Runbook 契约
每条 SLO Alert 应携带统一 labels,供 Alertmanager 路由:
labels:
severity: page # page | ticket
service: checkout
slo: availability # availability | latency
team: team-checkout
runbook_url: https://wiki.example/runbooks/checkout-slo
annotations:
summary: "{{ $labels.service }} {{ $labels.slo }} burn rate critical"
description: |
1h burn rate = {{ $value | printf "%.2f" }}× (threshold 14.4×).
Dashboard: https://grafana.example/d/checkout-slo
最近变更: 见 Panel 7 annotationsrunbook_url 最低内容:
- 打开 Grafana SLO Dashboard,确认 Burn Rate 窗口(1h/6h/3d)
- 检查最近 30min 部署(Panel 7)
- 若错误率上升:查 Traces/Logs 采样(存储成本 事故态采样)
- 是否 rollback:对照 Error Budget Policy §5.4 对话 3
- 升级条件:15min 无缓解 → TL;30min → VP Eng
无 runbook 的 Page 规则不应合并进生产——Alertmanager 只能路由,不能替人决策。
五、错误预算的策略使用
5.1 预算分区与发布策略
| 剩余预算 | 工程策略 | 产品策略 |
|---|---|---|
| > 50% | 正常发布节奏 | 可安排大促/实验 |
| 20%–50% | 冻结 feature,只允许 bugfix | 推迟非关键需求 |
| < 20% | 全员可靠性改进 | 取消 risky 发布 |
| 0% | 事故复盘 + SLO 复审 | 对外沟通(若触 SLA) |
策略必须书面化(Error Budget Policy 文档),明确谁有权 override——通常是 VP Eng + SRE Lead 联合批准,且 override 事件本身要审计。
5.2 预算”冷藏”(Freezing)
大促、春节、重大发布前 2–4 周 进入冷藏期:
- 禁止非紧急发布
- 提前完成依赖变更
- 预扩容、预跑混沌(见 混沌工程)
冷藏不是”不计预算”——而是降低消耗速度,把预算留给大促当天的已知风险。
5.3 预算耗尽之后
预算耗尽 ≠ 立即违约 SLA。它触发的是工程流程:
- 冻结所有 feature 发布直至预算恢复(滚动窗口自然回升或新窗口开始)
- 强制事故复盘:是 SLO 定太紧、还是系统真的不可靠?
- 更新 SLO 或投资可靠性——不要悄悄改 SLO 数字而不复盘
5.4 预算谈判:工程与产品的三条对话
对话 1:能否加 feature?
- 工程:剩余预算 35%,Burn Rate 6h 为 0.8×——可以,但大改需 TL 知悉
- 工程:剩余 15%,3d Burn Rate 1.2×——建议否,先修可靠性
对话 2:能否降低 SLO?
- 产品:99.9% 太严,想改 99.5%
- 工程:用 §1.2 展示预算从 43.2min/月 → 3.6h/月;问”用户是否接受更多可见故障”
- 输出:要么投资可靠性达标,要么书面降低 SLO 并更新 SLA 缓冲
对话 3:事故后是否继续发布?
- 若事故消耗 30% 预算但根因已修复且 Burn Rate 已 normalize → EBP 通常允许 小步发布 + 加强 canary
- 若根因未明、Burn Rate 6h 仍 > 1× → 冻结直至 Ticket 关闭
三条对话的共同点:用 Dashboard Panel 1 和 Panel 2 的截图作为会议事实来源,而非口头”感觉还行”。
5.5 与 告警体系 的分工
| 机制 | 回答的问题 |
|---|---|
| SLO / Burn Rate | 用户是否正在受损?预算烧多快? |
| Alertmanager route | 谁被叫醒、通过什么渠道? |
| Ticket 规则 | 慢性问题谁跟进、何时不打扰 on-call? |
SLO 规则产出 severity: page|ticket
label;Alertmanager 按 label 路由——两篇文章的 YAML 应
同仓库、同 CI 审查。
六、与数据库层 SLI 的映射
服务 SLO 失败往往根因在 DB。本站 PG 监控体系 从内核机制推导六个监控维度——本节说明如何把 DB 指标映射为上游服务的 SLI 输入,而非另建一套孤立的数据库 SLO。
6.1 分层 SLO 模型
flowchart TB
USER[用户请求] --> API[API 服务 SLO]
API --> DB[(PostgreSQL / MySQL)]
API --> CACHE[Redis]
DB --> DBSLI[DB 层 SLI]
DBSLI --> API
- 用户 facing SLO:checkout API 99.9%——唯一应对外承诺的。
- DB 层 SLI:连接可用性、查询 p99、复制 lag——解释 API SLO 失败的内部原因。
6.2 PG 维度 → 服务 SLI 映射表
| PG 监控维度(21-monitoring) | 内核故障模式 | 对 API SLI 的影响 | 是否应用作 Page |
|---|---|---|---|
idle in transaction 过长 |
阻止 VACUUM、持锁 | 延迟 SLI 退化 | 否(Ticket,除非已 Burn Rate) |
| 复制 lag 超阈值 | WAL 堆积 | 读从库失败 → 可用性 SLI | 否(DB 团队 Ticket) |
| XID wraparound 逼近 | DB 只读 | 可用性 SLI 100% 失败 | 是(若影响用户 SLO) |
pg_stat_statements 计划突变 |
慢查询 | 延迟 SLI | 通过 API Burn Rate |
原则:Page 只打用户 SLO Burn Rate。DB 指标发 Ticket 给 DBRE——除非 DB 故障已反映在 API Burn Rate 上。
6.3 mysqld / PG exporter 指标示例
# PG:连接使用率 > 85% → Ticket(非 Page)
sum(pg_stat_activity_count) / pg_settings_max_connections > 0.85
# MySQL:Innodb_buffer_pool 命中率骤降 → 调查延迟 SLI 根因
rate(mysql_global_status_innodb_buffer_pool_read_requests[5m])
/
(
rate(mysql_global_status_innodb_buffer_pool_read_requests[5m])
+ rate(mysql_global_status_innodb_buffer_pool_reads[5m])
) < 0.95
DB 层告警应链到 性能调查方法论 的五层工具链,而不是独立叫醒 API on-call。
6.4 gRPC 与 HTTP 的 SLI 统一
混合协议栈中,checkout 对外 HTTP、对内 gRPC。若只对 HTTP 设 SLO,gRPC 5xx 可能通过内部调用链间接烧伤 HTTP SLI——但 gRPC 自身也应有内部 SLI 便于分解。
gRPC 可用性 SLI(Prometheus grpc-exporter 或 OTel):
sum(rate(grpc_server_handled_total{grpc_code!="OK",grpc_service="payment.Payment"}[5m]))
/
sum(rate(grpc_server_handled_total{grpc_service="payment.Payment"}[5m]))
grpc_code 映射注意:
| gRPC code | 是否计入 SLI 失败 | 说明 |
|---|---|---|
| OK | 否 | |
| Canceled | 通常否 | 客户端取消,类比 499 |
| InvalidArgument | 视产品 | 客户端错误,常排除 |
| DeadlineExceeded | 是 | 服务未在 deadline 内完成 |
| Unavailable | 是 | 依赖不可用 |
| Internal | 是 |
Dashboard 上应用 同一 Burn Rate
公式,仅替换指标名;Recording Rule 建议独立 group
slo_payment_grpc 避免 PromQL 过长。
6.5 新鲜度 SLI(数据管道类服务)
ETL、CDC、搜索索引构建等服务不适合用”请求成功率”——用 数据滞后(Freshness):
\[\text{SLI}_{\text{fresh}} = \mathbb{1}[\text{now} - \text{last\_successful\_batch\_ts} < L]\]
# 最后成功批处理距今秒数
time() - pipeline_last_success_timestamp_seconds{job="search-indexer"}
SLO 示例:99% 的 5 分钟窗口内滞后 < 300s。Burn Rate 定义为”滞后超阈值的时间占比 / (1 - SLO)“——需自定义 Recording Rule,不能照搬 HTTP 模板。
6.6 持久性 SLI(存储类)
对象存储、备份系统常用 durability SLI:写入后 N 天内可读取的比例。工程上常通过 定期校验 job(随机抽样 GET + checksum)生成:
sum(rate(durability_check_success_total[1d]))
/
sum(rate(durability_check_total[1d]))
持久性 SLO 窗口常取 90d 滚动而非 30d——与 存储工程 中的 RPO/RTO 概念对齐,但数值必须单独与业务/legal 确认。
七、SLO 文化与组织落地
7.1 谁定 SLO
| 角色 | 职责 |
|---|---|
| 开发/架构 | 提出 SLI 候选、实现埋点 |
| 产品 | 确认”用户何时感觉坏”→ 阈值 |
| SRE | Recording Rule、告警、Dashboard、复盘 |
| 管理层 | 批准 Error Budget Policy |
7.2 Dashboard 设计原则
SLO Dashboard 的第一受众是 PM 和 TL,不是 SRE:
- 大字:剩余错误预算百分比
- 趋势线:30 天 Burn Rate
- 标注:最近一次发布、变更事件(链 Events)
- 禁止:只有 SRE 能看懂的 PromQL 原始值
7.3 月度 SLO 评审会议程
- SLI 数据质量:有无采集断点?故障期间 Prometheus 是否可用?
- 上窗口 SLO 达标情况:超预算原因分类(发布/依赖/容量/代码 bug)
- 告警噪音回顾:哪些 Ticket 从未 action?哪些 Page 误报?
- SLO 数字是否仍与业务匹配——不是”只能调严”
7.4 Grafana 面板完整规格
SLO Dashboard 建议独立
Folder(SLO / Production),与基础设施
Dashboard 分离——PM 不应在 CPU 曲线里找预算。
下面以 checkout-api(SLO 99.9% 可用性 + 99%
请求 < 200ms)为例,列出 8 个 Panel
的查询、可视化与阈值。Panel JSON 可导入 Grafana 9+;变量
$service、$slo_target 在 Dashboard
Settings 中定义。
Dashboard 变量
| 变量 | 类型 | 取值示例 |
|---|---|---|
$service |
query | label_values(http_requests_total, job) |
$slo_target |
custom | 0.999(可用性)、0.99(延迟占比) |
$window |
custom | 30d |
Panel 1:错误预算剩余(Stat)
- 目的:PM 第一眼看到”还能不能发版”
- 查询:
slo:checkout:budget_remaining_ratio * 100
- Unit:percent (0–100)
- Thresholds:> 50 green,20–50 yellow,< 20 red
- 说明文字(Panel description):剩余预算 < 20% 时 Error Budget Policy 要求冻结 feature 发布
Panel 2:30 天 Burn Rate 趋势(Time series)
- 查询(三条线):
slo:checkout:burnrate1h
slo:checkout:burnrate6h
slo:checkout:burnrate3d
- Legend:
1h/6h/3d - Reference lines:y=14.4(Page A 阈值)、y=6(Page B)、y=1(Ticket / Page C)
- Y 轴:linear,min=0,建议 max=20(避免单次事故拉爆纵轴时可改用 log scale)
Panel 3:当前 SLI 可用性(Gauge + Stat)
1 - slo:checkout:error_ratio5m
- Thresholds:<
$slo_targetred,≥$slo_targetgreen - Subtitle:
5m 滚动窗口;非 30d 合规
Panel 4:延迟 SLI——低于 200ms 请求占比(Gauge)
sum(rate(http_request_duration_seconds_bucket{job="$service",le="0.2"}[5m]))
/
sum(rate(http_request_duration_seconds_count{job="$service"}[5m]))
- Thresholds:< 0.99 yellow,≥ 0.99 green
Panel 5:30 天失败请求累计(Bar gauge)
sum(increase(http_requests_total{job="$service",status=~"5.."}[$window]))
- 与预算允许失败数并排展示(Panel 6)
Panel 6:预算允许失败数 vs 已消耗(Stat pair)
# 允许(30d)
0.001 * sum(increase(http_requests_total{job="$service"}[$window]))
# 已消耗
sum(increase(http_requests_total{job="$service",status=~"5.."}[$window]))
- 两 Stat 并排;已消耗 / 允许 > 1 时整行标红
Panel 7:最近变更事件(Annotations)
- 数据源:Grafana Annotation API 或 Loki
{app="argocd"} |= "synced"日志 - 用途:Burn Rate 尖峰与部署时间对齐——复盘时回答”是不是刚发版”
- 与 Events
与变更关联 中的 CloudEvents
字段对齐:
deployment.id、service.name
Panel 8:活跃告警(Alert list)
- Filter:
alertname=~".*SLOBurnRate.*", service="$service" - 链接:每条告警
runbook_url指向 wiki
flowchart TB
subgraph row1 [第一行:决策]
P1[预算剩余 Stat]
P3[可用性 Gauge]
P4[延迟 SLI Gauge]
end
subgraph row2 [第二行:趋势]
P2[Burn Rate 1h/6h/3d]
end
subgraph row3 [第三行:归因]
P7[变更 Annotations]
P8[活跃告警]
end
P1 --> P2
P2 --> P7
反模式:把 30 天 increase()
直接放在 Dashboard 主查询——每次刷新全量扫描
TSDB,大集群会拖慢 Grafana。应使用 §3.4 的 Recording Rule
预聚合。
7.5 promtool 规则校验与 CI 门禁
Recording Rule 与 Alert Rule 的 PromQL 在 Prometheus 升级、指标 rename 后容易静默失效(查询成功但返回空)。Google SRE Workbook 建议在 CI 中对规则做语法检查与固定输入/output 单元测试。
本环境未安装
promtool(which promtool
无结果),以下配置来自 Prometheus
官方文档(A 级),部署前在目标 Prometheus
版本上执行。
步骤 1:语法检查
promtool check rules slo-recording-rules.yml slo-alert-rules.yml步骤 2:单元测试(slo_test.yml
摘录)
# slo_test.yml — promtool test rules
rule_files:
- slo-recording-rules.yml
evaluation_interval: 1m
tests:
- interval: 1m
input_series:
- series: 'http_requests_total{job="checkout",status="200"}'
values: '0+100x60'
- series: 'http_requests_total{job="checkout",status="500"}'
values: '0+0x59 100'
alert_rule_test:
- eval_time: 60m
alertname: CheckoutSLOBurnRateCritical1h
exp_alerts:
- exp_labels:
severity: page
service: checkout
exp_annotations:
summary: 'checkout SLO 1h 燃烧率'input_series 中 0+100x60 表示从
0 起每分钟 +100(模拟稳定 QPS);第 60 分钟注入 100 次 5xx
模拟事故尖峰——具体是否触发 Page 取决于
burnrate1h 表达式与 for:
窗口,需在本地 promtool test 调参后固化
expected。
步骤 3:GitHub Actions 片段
# .github/workflows/prometheus-rules.yml
jobs:
prom-rules:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Download promtool
run: |
VER=2.51.0
curl -sL "https://github.com/prometheus/prometheus/releases/download/v${VER}/prometheus-${VER}.linux-amd64.tar.gz" | tar xz
echo "$(pwd)/prometheus-${VER}.linux-amd64" >> $GITHUB_PATH
- run: promtool check rules observability/slo-recording-rules.yml
- run: promtool test rules observability/slo_test.yml门禁策略:
| 变更类型 | CI 要求 |
|---|---|
| 修改 Recording Rule | check rules + 至少 1 条
test rules |
| 修改 Alert Rule | 同上 + Alertmanager amtool check-config(见
告警体系 附录
K) |
| 升级 Prometheus minor | 全量 replay test;关注 increase() 与
histogram 行为变更 |
坑:promtool test 的
eval_time 与 for: 叠加——Alert 的
for: 2m 意味着 test 中 eval_time
必须晚于条件首次满足至少 2 分钟,否则
exp_alerts 为空导致 CI 误报失败。
八、案例复盘:预算如何在沉默中耗尽
8.1 案例 A:Q3 十二次”小发布”(慢性退化)
背景:某电商 checkout-api,SLO 99.9%(30 天滚动),Error Budget Policy 已文档化但无人看 Dashboard。
时间线:
| 时间 | 事件 | 5m 错误率 | 1h Burn Rate | 预算剩余(约) | 为何未 Page |
|---|---|---|---|---|---|
| W1 | 发布 v2.3.1,依赖库升级 | 0.05% | 0.5× | 92% | < 14.4× |
| W3 | 发布 v2.3.2,连接池默认值变更 | 0.08% | 0.8× | 85% | Ticket 未配置 |
| W5–W12 | 每周一次 hotfix | 0.06–0.12% | 0.6–1.2× | 降至 8% | 6h/3d Ticket 规则缺失 |
| W13 大促前 | PM 要求冻结 | — | — | 8% | 人工发现 |
根因(机制):
- 每次发布引入 p99 +10ms 与 0.02% 额外 5xx——单独看都不触发 CPU/内存告警
- 只配置了 1h Page(14.4×),未配置 6h/3d Ticket(1×)——慢性退化无信号
- 429 曾被算进失败请求,客户端 retry 放大错误率(后按 §2.2 排除)
修复动作:
- 补齐 Workbook 6 条规则(§4.1–4.2)
- Grafana Panel 1(预算剩余)接入 TL 周报 Slack 截图
- 发布流水线增加:若
budget_remaining < 30%则 CI 警告(不阻断,但需 TL ack)
教训:SLO 工程的主要敌人不是单次事故,而是低于 Page 阈值、高于可持续错误率的持续泄漏。
8.2 案例 B:依赖 Redis 单点(突发 + 正确 Page)
背景:同一 checkout-api,payment 会话缓存走 Redis Cluster。
时间线:
| 时刻 | 现象 | API SLI | 1h Burn Rate | 响应 |
|---|---|---|---|---|
| T+0 | Redis 主节点网络分区 | 5xx 从 0.01% → 8% | 18× | Page A 触发 |
| T+3min | 值班 ack,切本地降级 | 5xx → 2% | 9× | Page B 仍 firing |
| T+15min | Redis 恢复 | 5xx → 0.02% | 0.3× | Page resolve |
做对的事:
- Page 仅基于 checkout Burn Rate,不是
redis_up——避免 DB/缓存团队与 API 团队重复叫醒 - Runbook 第一步:Grafana Panel 7 看是否刚发版(本次无)→ Panel 2 看 Burn Rate 窗口 → 依赖 trace 定位 Redis
做错的事(事后复盘):
- 降级路径未在混沌演练中验证,T+0–T+3min 仍返回 5xx 而非降级响应——错误预算在恢复前已烧 2.1%
- Redis 团队 Ticket 阈值(lag/memory)与 API SLO 未在联合评审中对齐
8.3 案例 C:Prometheus 与业务同挂(SLI 盲区)
背景:单 AZ Prometheus,与 checkout 同 K8s 集群。
事故:AZ 网络故障,checkout
不可用,Prometheus scrape
也失败——up{job="checkout"}==0,可用性
SLI 分母为零,Burn Rate 查询返回 NaN,无
Page。
修复:
- 双 Prometheus + Thanos Query 联邦(见 §9.5、Prometheus HA)
- 独立 black-box 探测(附录 C)在 不同 AZ
采集
probe_success - Alert:
absent_over_time(up{job="checkout"}[10m])→ 基础设施 Page,与 SLO Page 分离
8.4 从案例归纳的检查清单
| 检查项 | 案例 A | 案例 B | 案例 C |
|---|---|---|---|
| 6 条 Burn Rate 规则齐全 | 否 | 是 | 是 |
| Ticket 规则接 Slack/Jira | 否 | 是 | — |
| 预算剩余 Dashboard 有人看 | 否 | 是 | 是 |
| SLI 采集与业务故障域隔离 | — | — | 否 |
| 429/401 已从 SLI 排除 | 否 | 是 | 是 |
| Runbook 链到依赖排查 | — | 部分 | — |
九、工程坑点
9.1 选 SLO 不算预算
99.9% vs 99.99%:预算差 10 倍。上线前用表格(§1.2)算一遍。
9.2 用均值代替分位数
p50 正常、p99 崩溃——用户投诉的是 p99。延迟 SLI 必须基于分位数或”低于阈值请求占比”。
9.3 429 与 500 混算
429 是限流,500 是故障。混算会让恶意客户端或错误 retry 策略烧干预算。
9.4 短窗口 + 低燃烧率 = 告警风暴
每 5 分钟一条 Slack → 值班麻木 → 真正 Page 被忽略。
9.5 监控在故障中不可用
Prometheus 与业务同机房同故障域——业务挂时 SLI 也停采。双活采集、合成探测、跨 region remote write 至少选一种(见 Prometheus HA)。
9.6 每个团队各自定义 SLO 无法相加
平台级 SLO 需要从用户路径聚合,不能简单平均各微服务 SLO——需用错误预算的联合概率模型或取关键路径最弱环节。
十、落地清单
十一、关键概念回顾
- SLI → SLO → SLA:测量 → 目标 → 合同
- 错误预算:\(1 - \text{SLO}\),发布节奏控制器
- Burn Rate:\(\text{error\_rate} / (1 - \text{SLO})\);14.4×/1h ≈ 烧 2% 月预算
- 多窗口多燃烧率:短窗高率 Page,长窗低率 Ticket
- Page 条件:用户 SLO Burn Rate,不是 CPU/内存
十二、下一步
SLO 定好之后,需要告警体系承载 Burn Rate——Alertmanager route/inhibit、PagerDuty 排班、分级模板。下一篇 告警体系:Alertmanager、PagerDuty、分级抑制。
上一篇:内核追踪:ftrace、kprobe、uprobe、tracepoint 生产实战
下一篇:告警体系:Alertmanager、PagerDuty、分级抑制
参考资料
- Google, Site Reliability Engineering, Chapters 4–5, O’Reilly, 2016
- Google, The Site Reliability Workbook, Chapter 2 “Implementing SLOs” & Chapter 5 “Alerting on SLOs”, O’Reilly, 2018
- Prometheus, Recording Rules, https://prometheus.io/docs/prometheus/latest/configuration/recording_rules/
- Prometheus, Alerting Rules, https://prometheus.io/docs/prometheus/latest/configuration/alerting_rules/
- OpenTelemetry, Span Metrics, https://opentelemetry.io/docs/collector/span-metrics/
附录 A:多服务 SLO 与依赖
微服务架构中,checkout 依赖 payment、inventory、user 三个下游。用户 facing SLO 失败时,需要知道哪条依赖链在烧预算。
A.1 关键路径 SLO
只对用户关键路径设 SLO,不对每个内部服务都设 99.99%:
| 服务 | 是否用户 facing | 建议 SLO |
|---|---|---|
| checkout-api | 是 | 99.9% |
| payment-internal | 否 | 99.5%(内部) |
| legacy-report | 否 | 99%(内部) |
内部服务 SLO Violation 通过 Ticket 处理,除非已传播到 checkout Burn Rate。
A.2 依赖 SLI 的 RED 指标传递
下游 payment 的
http_requests_total{status=~"5.."} 应带
caller="checkout" label(基数可控时)——checkout
团队能分解自身 SLI 失败中有多少来自 payment。
附录 B:延迟 SLO 的完整 PromQL 示例
假设 checkout p99 SLO:99% 请求 < 200ms(30 天窗口)。
# Recording:5 分钟窗口内"好延迟"请求占比
sum(rate(http_request_duration_seconds_bucket{job="checkout",le="0.2"}[5m]))
/
sum(rate(http_request_duration_seconds_count{job="checkout"}[5m]))
Burn Rate 对延迟 SLI:将分子分母中的”错误”定义为
le="0.2" 之外的请求:
(
1 - (
sum(rate(http_request_duration_seconds_bucket{job="checkout",le="0.2"}[1h]))
/ sum(rate(http_request_duration_seconds_count{job="checkout"}[1h]))
)
) / (1 - 0.99)
此处 SLO 为 99% 请求 < 200ms,故 \((1 - \text{SLO}) = 0.01\)。
附录 C:合成探测 SLI
Black-box 探测适合无侵入验证用户路径:
# prometheus/blackbox_exporter 模块
modules:
http_2xx:
prober: http
timeout: 5s
http:
valid_status_codes: [200]
fail_if_not_ssl: trueprobe_success{job="blackbox-checkout"}
合成 SLI 与服务器 SLI 取交集报告——合成失败但服务器指标正常,通常是 DNS/CDN/网络问题。
附录 D:SLO 与变更事件的关联
每次部署打 CloudEvents 到 Loki 或
Prometheus loki 不适用时用 ALERTS
关联:
# 部署后 15 分钟内 Burn Rate 上升 → 自动关联变更
slo:checkout:burnrate5m
and on()
increase(deployment_timestamp[15m]) > 0
Grafana Annotation 来自 CI webhook 是更常见的工程实现。
附录 E:错误预算的数学推导(30 天窗口)
设窗口长度 \(W = 30 \times 24 = 720\) 小时,SLO 可用性 \(S = 0.999\),错误率预算 \(\epsilon = 1 - S = 0.001\)。
正常消耗速率(占窗口总预算的比例/小时):\(\epsilon / W\)。
在 \(T\) 小时内以 Burn Rate \(B\) 消耗,预算消耗比例:
\[\Delta = \frac{B \cdot \epsilon \cdot T}{W \cdot \epsilon} = \frac{B \cdot T}{W}\]
令 \(T = 1\) h,\(B = 14.4\),\(W = 720\):
\[\Delta = 14.4 / 720 = 0.02 = 2\%\]
与 Workbook 表格一致。
附录 F:Prometheus 高可用与 SLI 连续性
SLI 采集中断会导致”假合规”——故障期间无数据,Budget Remaining 曲线水平不变。
缓解措施:
- Thanos/Mimir remote write 跨 AZ
- 双 Prometheus scrape 同一
target,
or合并查询 - Synthetic SLI 独立故障域
# 双源合并
(
sum(rate(http_requests_total{status=~"5.."}[5m]))
/
sum(rate(http_requests_total[5m]))
)
or
(
sum(rate(http_requests_total{status=~"5.."}[5m] offset 1m))
/
sum(rate(http_requests_total[5m] offset 1m))
)
or 仅作短时 failover 示意——生产应用
recording rule 封装并在无数据时 alert
absent()。
附录 G:MySQL / Redis 作为依赖的 SLI 注记
- Redis:可用性 SLI 用
redis_up+ 命令超时;延迟 SLI 用redis_commands_duration_secondshistogram。 - MySQL:连接失败计入 API
可用性;慢查询通过
performance_schema导出延迟 histogram(InnoDB 系列 第 16 章 监控篇将展开)。
依赖 SLI 不单独 Page——除非该依赖是同步阻塞且直接映射用户错误。
附录 H:SLO 文档模板(摘录)
## Service: checkout-api
- **Owners**: team-checkout, SRE liaison @sre-oncall
- **SLO Window**: 30d rolling
- **SLI-1 Availability**: non-5xx, non-timeout / total ≥ 99.9%
- **SLI-2 Latency**: fraction of requests < 200ms ≥ 99%
- **Exclusions**: 429, 401, healthcheck /metrics
- **Error Budget Policy**: see wiki/EBP-checkout
- **Dashboard**: grafana/d/checkout-slo
- **Runbook**: wiki/runbook/checkout-slo-burn附录 I:与 incident playbook 的衔接
真实事故复盘剧本 的第一步是 SLO Burn Rate Page——值班工程师应能在 Grafana 上同时看到:
- 当前 Burn Rate(1h/6h)
- 预算剩余
- 最近变更事件
Runbook 中”是否 rollback”的决策点与 Error Budget Policy
的 < 20% 冻结规则对齐。
附录 J:常见问题
Q:能否对 batch job 设 SLO?
可以,用新鲜度 SLI(数据滞后 < N 小时)或吞吐 SLI(每日处理 ≥ N 条)。Burn Rate 公式相同,但窗口常取 7d 而非 30d。
Q:SLO 未达标是否扣团队 KPI?
工程建议:SLO 驱动流程(停发版),而非惩罚个人——否则团队会瞒报或调低 SLO。
Q:Serverless 如何埋 SLI?
用 API Gateway / Lambda 内置指标 + OTel span metrics;冷启动延迟纳入延迟 SLI 分母。
Q:多 region 如何聚合 SLO?
按用户流量权重加权错误率,而非简单平均各 region SLI——否则低流量 region 拉低全局。
附录 K:版本与工具说明
- PromQL 语法以 Prometheus 2.51+ 为准
increase(...[30d])需要足够 retention- 本环境实验限制:Docker Hub 超时无法拉取 Prometheus
镜像;
promtool未安装——部署前请在 CI 中加promtool check rules/promtool test rules
附录 L:Burn Rate 阈值速查(SLO=99.9%,30 天 = 720h)
| 窗口 \(T\) | 燃烧率 \(B\) | 预算消耗 \(\Delta = B \cdot T / 720\) | Workbook 级别 |
|---|---|---|---|
| 1h | 14.4 | 2.00% | Page A |
| 6h | 6 | 5.00% | Page B |
| 3d | 1 | 10.00% | Page C / Ticket F |
| 6h | 1 | 0.83% | Ticket D |
| 1d | 1 | 3.33% | Ticket E |
低 QPS 服务应加最小流量门槛,避免单条 5xx 触发 Page:
(
sum(rate(http_requests_total{job="checkout",status=~"5.."}[1h]))
/ sum(rate(http_requests_total{job="checkout"}[1h]))
/ 0.001 > 14.4
)
and
sum(rate(http_requests_total{job="checkout"}[1h])) > 0.1
RED 中的 Errors rate 是 Burn Rate 分子;Duration 用于延迟 SLI(附录 B)。指标命名与 指标体系 保持一致。
附录 M:Error Budget Policy 全文模板
以下模板可直接放入内部
wiki;{SERVICE}、{SLO}
替换为实际值。
# Error Budget Policy — {SERVICE}
## 1. 适用范围
- 服务:{SERVICE}
- SLO 窗口:30 天滚动
- 可用性 SLO:{SLO}(非 5xx、非超时 / 总请求)
- 延迟 SLO(如有):99% 请求 < {LATENCY_MS}ms
## 2. 预算分区与工程动作
| 剩余预算 | 发布 | 变更审批 | On-call |
|----------|------|----------|---------|
| > 50% | 正常 | TL | 常规 |
| 20–50% | 仅 bugfix / 可靠性 | TL + SRE | 加强巡检 |
| < 20% | **冻结 feature** | VP Eng + SRE Lead | 事故态 |
| 0% | 仅 hotfix / 回滚 | 同上 + PM | 复盘 mandatory |
## 3. Override
- 谁可批准:VP Engineering + SRE Lead 双签
- 最长 override:72 小时
- 必须记录:Jira ticket、原因、预计额外预算消耗
## 4. 大促冷藏
- 开始前 4 周进入冷藏;见 §5.2
## 5. 告警与 Runbook
- Page:Burn Rate 规则见 slo-alert-rules.yml
- Runbook:{RUNBOOK_URL}
- Dashboard:{GRAFANA_URL}
## 6. 月度评审
- 参与:TL、PM、SRE、代表开发
- 输出:SLI 质量报告、超预算根因分类、下月动作项附录 N:OpenTelemetry Span Metrics 生成 SLI
已全量 OTel、未手工维护 http_requests_total
的团队,可用 Collector spanmetrics
connector 生成 RED 指标供 SLO 使用。
# otel-collector-config.yaml(摘录)
connectors:
spanmetrics:
histogram:
explicit:
buckets: [0.05, 0.1, 0.2, 0.5, 1, 2, 5]
dimensions:
- name: http.method
- name: http.status_code
dimensions_cache_size: 1000
service:
pipelines:
traces:
receivers: [otlp]
exporters: [spanmetrics]
metrics:
receivers: [spanmetrics]
exporters: [prometheus]映射到 SLI:
# 由 spanmetrics 导出的 calls_total
sum(rate(calls_total{service_name="checkout",http_status_code=~"5.."}[5m]))
/
sum(rate(calls_total{service_name="checkout"}[5m]))
注意:
http.status_code基数可控(约 10–20 类),但勿加http.url路径全量- 采样降低 trace 量时,spanmetrics 仍反映被采样 trace 的错误率——需调高错误 trace 采样或使用 tail sampling(见 Traces 栈)
- 与 埋点哲学 中的基数控制一致
附录 O:SLO 分阶段落地路线图(90 天)
| 阶段 | 周次 | 交付物 | 成功标准 |
|---|---|---|---|
| 0 对齐 | W1–2 | SLI 定义文档、产品签字 | “坏请求”无歧义 |
| 1 可见 | W3–4 | Recording Rules + Grafana Panel 1–4 | TL 能口述剩余预算 |
| 2 告警 | W5–6 | 6 条 Alert + Alertmanager route | 混沌注入触发 Page(演练) |
| 3 流程 | W7–8 | EBP 文档、月度评审日历 | 一次模拟冻结发布 |
| 4 扩展 | W9–12 | 多服务复制、DB Ticket 联动 | ≥3 个关键服务同上 |
W5 混沌演练建议(不编造具体数字,只描述方法):
- 在 staging 对 checkout 注入 5xx(故障注入平台或
toxiproxy) - 预期:1h Burn Rate 在 2–5 分钟内 > 14.4×,Page 到达 PagerDuty
- 若未触发:检查
for:、Recording Rule 是否生效、指标 label 是否匹配
附录 P:常见误解(扩展)
| 误解 | 事实 |
|---|---|
| “SLO 达标 = 用户满意” | SLO 只覆盖已选 SLI;未覆盖的功能抱怨仍会发生 |
| “预算没用完 = 发布太少” | 预算是风险容量,不是 KPI 必须花完 |
| “三个 9 和四个 9 差不多” | 预算差 10 倍(§1.2) |
| “Burn Rate 告警替代所有监控” | 仍需容量、安全、依赖 Ticket(告警体系) |
| “一个全局 SLO 管所有微服务” | 用户 facing 与内部服务分层(附录 A) |
附录 Q:与存储成本的衔接
错误预算紧张时常伴随 Trace/Log 全量采集 撑爆存储——Burn Rate 高时若同时开启 100% trace sampling,会加剧故障(Collector OOM)。建议:
- 常态:头部 1% + 尾部保留错误(存储与成本 §二)
- 事故态:临时提高采样需 SRE 审批,并在 EBP 中注明”诊断性开销”
SLO 与成本不是对立——用 SLO 决定何时值得花 observability 预算。
附录 R:多服务 Recording Rules 目录结构
规模化后不宜所有规则堆在单文件。推荐仓库布局:
observability/
slo/
recording/
checkout.yaml
payment.yaml
search-indexer.yaml
alerts/
checkout-pages.yaml
checkout-tickets.yaml
tests/
checkout_test.yaml
lib/
burnrate.lib.yaml # 可选:jsonnet 生成
checkout.yaml 片段(含可用性 + 延迟双 SLI):
groups:
- name: slo_checkout_recording
interval: 30s
rules:
- record: slo:checkout:availability:ratio5m
expr: |
1 - (
sum(rate(http_requests_total{job="checkout",status=~"5.."}[5m]))
/ sum(rate(http_requests_total{job="checkout"}[5m]))
)
- record: slo:checkout:latency:ratio5m
expr: |
sum(rate(http_request_duration_seconds_bucket{job="checkout",le="0.2"}[5m]))
/ sum(rate(http_request_duration_seconds_count{job="checkout"}[5m]))
- record: slo:checkout:burnrate_availability:1h
expr: |
(1 - slo:checkout:availability:ratio5m)
/ 0.001
# 注意:应用 1h rate 而非 5m 预聚合直接除——生产应用独立 1h 窗口 rule命名约定:slo:{service}:{sli_name}:{window},避免与业务自定义
job 指标冲突。
Thanos / Mimir 全局视图:Recording Rule
在 数据写入侧 Prometheus 执行;Global SLO
Dashboard 通过 Thanos Query 对 slo:* 做
sum 时注意 不要跨 region 重复 scrape
导致双计数——仅在一个 replica 上跑 rule 或对
federation 源做 dedup。
附录 S:与产品对齐 SLI 的会议议程(60 分钟)
SLO 工程最难的是对齐,不是 PromQL。建议首次会议议程:
| 时间 | 议题 | 产出 |
|---|---|---|
| 0–10min | 用户旅程:何时觉得”坏了” | 候选 SLI 列表 |
| 10–25min | 逐 SLI 定义”好/坏”边界 | 排除 429/401 等书面确认 |
| 25–35min | 选 SLO 数字 + 算错误预算表(§1.2) | 签字 SLO 目标 |
| 35–45min | 延迟阈值:p99 vs “占比” | 单一延迟 SLI 口径 |
| 45–55min | Error Budget Policy 冻结/override | 责任人 |
| 55–60min | Dashboard 首版日期、评审会 cadence | 日历邀请 |
避免:会议中直接拍 99.99%——先用 §1.2 表格展示四个 9 的 downtime 等价,再决定。
避免:把”业务 KPI”(GMV、转化率)与 SLI 混为一谈——KPI 可关联分析,但不替代 SLI。
附录 T:Recording Rule 性能与 cardinality
每条 Recording Rule 每 30s 评估一次,规则数量 × series 数 = 额外 TSDB 写入。
| 实践 | 原因 |
|---|---|
仅对 用户 facing 服务建
slo:* rule |
内部服务 hundreds 个会膨胀 |
interval: 30s 与 scrape 对齐 |
避免重复计算 |
用 sum() without 高基数 label |
按 job 聚合,不加 pod |
| 长窗口 burnrate 用 recording 链 | burnrate1h 依赖 5m 中间结果可选,但 1h
应独立 rate()[1h] 更准确 |
诊断慢查询:prometheus_engine_query_duration_seconds
中 rule_group=slo_checkout_recording 的 p99
升高时,检查是否误加 path label 到
http_requests_total——见 埋点哲学。
附录 U:SLI 合规报告(月度模板)
# {SERVICE} SLO 报告 — {YYYY-MM}
## 摘要
- 可用性 SLO:{SLO} | 实际:{ACTUAL}% | 达标:是/否
- 延迟 SLO:{LAT_SLO} | 实际:{LAT_ACTUAL}% | 达标:是/否
- 错误预算消耗:{BURN_pct}%
## 超预算事件
| 日期 | 持续 | 根因分类 | 链接 |
|------|------|----------|------|
| ... | ... | 发布/依赖/容量/外部 | JIRA-xxx |
## 告警统计
- Page 次数:{N_page} | 误报:{N_fp}
- Ticket 次数:{N_ticket} | 未处理:{N_open}
## 下月动作
- [ ] ...报告数据应来自 Grafana PDF 或 slo:*
指标自动导出——手工填数易与 Dashboard 不一致。
附录
V:increase() 与低 QPS 服务的陷阱
increase(http_requests_total[30d]) 在
Prometheus 中对 低 QPS 服务使用
rate() 外推,统计误差大:
- 每天仅数千请求的服务,30 天窗口内
increase对 scrape miss 敏感 - 缓解:延长 scrape 间隔一致性;或使用 日志型 SLI(BigQuery/SIEM 精确计数)做月度合规,Prometheus 仅做实时 Burn Rate
最小流量门槛(与附录 L 呼应):
sum(rate(http_requests_total{job="checkout"}[1h])) > 0.1 # 至少 0.1 req/s 才评估 Burn Rate
低 QPS 内部 admin API 可改用 7d 窗口 + 更低 Page 阈值 或仅 Ticket——Workbook 6 规则主要面向 >1 req/s 的在线服务。
附录 W:参考资料补充
Robey, Peter, et al., “Alerting on SLOs”, Google SRE Workbook, 2018, https://sre.google/workbook/alerting-on-slots/ (Workbook 告警章节在线版)
Pyrra, https://github.com/pyrra-dev/pyrra — 开源 SLO 规则生成器,可对照本文 PromQL 模板
Sloth, https://sloth.dev/ — SLO 规则生成 CLI,支持 Prometheus Operator
PrometheusRuleCRDCNCF, OpenSLO specification, https://openslo.com/ — 厂商中立的 SLO 定义格式,可与 Sloth/Pyrra 互操作
同主题继续阅读
把当前热点继续串成多页阅读,而不是停在单篇消费。
【可观测性工程】告警体系:Alertmanager、PagerDuty、OnCall 与分级抑制
建一套不让人崩溃的告警体系。从 Prometheus Alertmanager 的分组/抑制/静默三元组,到 PagerDuty 排班与升级策略,到分级告警的设计模板与告警质量持续治理。
【系统架构设计】SLO 工程:可靠性的量化管理
SLI、SLO、SLA 不只是运维指标——它们是架构决策的定量依据。本文从 Google SRE 的 Error Budget 策略出发,拆解多窗口燃烧率告警的数学原理,讲清楚 SLO 如何在产品与工程的冲突中充当仲裁者,并给出基于 Prometheus 和 Grafana 的落地方案。
【可观测性工程】真实事故复盘剧本:从指标抖动到根因的全链路追查
虚构但可复现的 checkout 服务事故全链路:SLO Burn Rate 告警后按 Golden Minute→Metrics→Traces→Logs→Profile→Events 五阶递进排障,含 PromQL/LogQL/kubectl 命令与三条分级剧本,交叉引用系列 01–22。
可观测性工程
从 Metrics、Logs、Traces 到 Profiling、eBPF、OpenTelemetry 与 SLO 治理,面向中国工程团队的可观测性系统化手册。全 25 篇。