上一篇《Prometheus 监控报告编写:SLI报告》理清了 SLI 指标怎么选、状态怎么判、报告由哪些部分组成,并给出可直接落地的巡检报告模板与对应的 PromQL。
本篇目标:理清 SLO 怎么定义、怎么配置、错误预算怎么算、Burn Rate 怎么判,并给出可直接落地的 SLO 报告模板与对应的 PromQL。
和上一篇最大的差异只有一个,也是本篇的基准:
SLO 只统计
ingress="true"的入口服务。可用性、延迟、吞吐这类面向用户体验的指标,取数一律加
ingress="true"过滤;内部服务之间的调用不计入 SLO 的分子分母。主机层与 JVM 指标不作为 SLO 目标,只在排查时作为关联信息引用。
理由:用户只会体验到暴露出去的那个入口。内部服务之间的重试、降级、灰度流量会产生大量噪声请求,如果全算进可用性分母,5xx 错误率会被稀释,看不出真实影响,错误预算也就失去了「可用性货币」的意义。
一、核心概念回顾
在 SLI / SLO / SLA 三把尺子之上再加两把:错误预算 与燃尽率。
| 概念 | 全称 | 定义 | 示例 |
|---|---|---|---|
| SLI | Service Level Indicator | 服务等级指标,对服务某方面表现的可量化测量 | 入口服务 5xx 错误率 = 0.02% |
| SLO | Service Level Objective | 服务等级目标,对 SLI 设定的目标值和时间窗口 | 30 天内入口 5xx 错误率 ≤ 0.1%(可用性 99.9%) |
| SLA | Service Level Agreement | 服务等级协议,对客户承诺的 SLO 及违约后果 | 可用性低于 99.9% 则赔偿 |
| 错误预算 | Error Budget | 目标允许的「不达标额度」,即 1 - SLO |
SLO 99.9% → 错误预算 0.1% |
| 燃尽率 | Burn Rate | 实际错误率与错误预算速率的比值,衡量预算消耗速度 | Burn Rate 14.4 → 约 2 天耗尽 30 天预算 |
推导链不变,只是在末端多出两条支线:
markdown
业务目标 → SLA(对外承诺) → SLO(内部目标) → SLI(测量指标) → Prometheus 数据
↓
错误预算(1 - SLO) ←→ 燃尽率(消耗速度)
- 错误预算是「可以犯错的额度」:SLO 99.9%、窗口 30 天,就有 0.1% 的额度可以被打掉。额度没用完,说明可靠性投入是够的;额度提前用完,就该停下来修可靠性,而不是继续发版。
- 燃尽率是「犯错的加速度」:光看预算余额只能知道「现在还剩多少」,看不出「按当前速度还能撑多久」。Burn Rate 把这两个问题合成一个数:Burn Rate = 1 表示按当前速度刚好在窗口结束时花完预算;Burn Rate = 14.4 表示以 14.4 倍的速度在烧,30 天预算大约 2 天就烧光。
SLO 不同目标值对应的可容忍不可用时长(这是跟业务对齐 SLO 时最常用的一张表):
| SLO 目标 | 每月允许不可用(30d) | 每周 | 每天 |
|---|---|---|---|
| 99.99% | 4.32 min | 1.01 min | 8.6 s |
| 99.95% | 21.6 min | 5.04 min | 43.2 s |
| 99.9% | 43.2 min | 10.08 min | 1.44 min |
| 99.5% | 3.6 h | 50.4 min | 7.2 min |
| 99% | 7.2 h | 1.68 h | 14.4 min |
二、SLO 报告 vs SLI 报告
两篇是互补关系,边界必须先划清楚,否则同一份数据会被写成两份彼此打架的报告:
| 对比维度 | SLI 报告 | SLO 报告 |
|---|---|---|
| 面向对象 | SRE、运维、开发、值班人员 | 产品、业务、管理层、客户成功、SRE 负责人 |
| 核心问题 | 当前系统运行状态如何?哪里异常? | 服务可靠性目标是否达成?错误预算还剩多少? |
| 侧重点 | 指标当前值、趋势、异常、健康度评分 | 目标达成率、错误预算消耗、Burn Rate、合规风险 |
| 时间窗口 | 短窗口为主:5m、1h、当日 | 长窗口为主:7d、30d、90d |
| 输出频率 | 日报、实时看板、告警 | 周报、月报、季度评审、发布门禁 |
| 指标范围 | 可覆盖大量 SLI:CPU、内存、JVM、QPS、延迟、错误率等 | 只覆盖少数关键 SLI:可用性、延迟、吞吐 |
| 统计范围 | 系统级全部入口服务 + 服务级全量明细 + 主机/JVM 下钻 | 仅 ingress="true" 的入口服务,不统计内部调用与主机/JVM |
| 数据粒度 | 服务、实例、接口级明细 | 系统/服务级聚合,强调目标与预算 |
| 典型内容 | 指标表格、异常明细、Top 20 最差、趋势对比 | SLO 达成率、错误预算余额、Burn Rate、风险等级 |
| 决策用途 | 故障排查、容量规划、日常巡检 | 发布决策、资源投入、SLA 合规、可靠性改进 |
| 与告警关系 | 阈值告警:CPU > 80% | 预算告警:错误预算消耗 > 50%、Burn Rate 过高 |
| 本质 | 数据报告 | 目标管理报告 |
两条最容易搞混的边界:
- 统计范围 :SLI 报告里系统级一行统计的是该系统的入口服务,但服务级明细、主机层、JVM 都是全量的;SLO 报告从头到尾只有入口服务,超出此范围的指标一律不进入 SLO 的计算。
- 时间窗口:SLI 报告用 5m 判断「此刻是否异常」,SLO 报告用 30d 判「这一个月是否达标」。同一个指标,两个窗口给出的结论经常相反,如5m 窗口 CPU 冲到 90% 是 SLI 报告的严重项,但如果它只持续了几分钟,对 30d 的 SLO 可能毫无影响。这正是两份报告必须分开的原因。
一句话概括:SLI 报告回答「现在怎么样」,SLO 报告回答「目标达标没有」。
三、先约定好标签体系
标签体系沿用SLI报告篇。
- 系统层 :
job_code(系统编码)、job_alias(系统名称),两者 1:1。 - 服务层 :
svc。 - 实例层 :
instance。 - 入口标识 :
ingress(布尔标签,ingress="true"表示该服务是入口服务),SLO 取数必须带ingress="true"。 - 聚合时优先按
job_code,job_alias仅用于展示。
聚合维度按层区分,也是本篇的基准:
| 层 | 覆盖指标 | 聚合维度 | 是否进入 SLO |
|---|---|---|---|
| 入口服务层 | 可用性(5xx)、吞吐(QPS)、延迟 | job_code, job_alias, svc,且 ingress="true" |
✅ SLO 的唯一数据来源 |
| 内部服务层 | 5xx/4xx 错误率、平均延迟、P99/最大延迟 | job_code, job_alias, svc |
❌ 仅供 SLI 报告与排查 |
| 主机层 | CPU、内存、磁盘、网络错误率 | job_code, job_alias, svc, instance |
❌ 不作为 SLO 目标 |
| JVM | 堆使用率、Full GC 次数/分 | job_code, job_alias, svc, instance |
❌ 不作为 SLO 目标 |
- 入口服务筛选是本篇与上一篇最关键的差别 :可用性、吞吐、延迟三类 SLO 的分子(坏事件)与分母(总请求)都必须带
ingress="true",两边保持同一过滤条件,否则比值没有意义。 job_code是唯一的聚合主键,job_alias只用于展示;系统级达成率由入口服务的服务级结果回卷得到,不做跨系统合并。- 内部服务、主机、JVM 的数据不是「不重要」,而是它们不适合作为对外承诺的目标。SLO 报告需要它们时,只作为「关联风险」引用到建议动作里,指向 SLI 报告去下钻。
四、SLO 的定义、配置与状态判定规则
1. SLO 由六个要素组成
六要素缺一不可:
| 要素 | 说明 | 示例 |
|---|---|---|
| SLI | 测量什么 | 入口服务 5xx 错误率 |
| 取数范围 | 统计哪些请求 | ingress="true"(入口服务) |
| 目标值 | 期望水平 | 99.9% |
| 时间窗口 | 评估周期,建议滚动窗口 | 滚动 30 天 |
| 错误预算 | 1 - 目标值 |
0.1% |
| 事件口径 | 什么算「坏事件」 | HTTP 5xx(不含 4xx) |
两个必须显式的约定:
- 4xx 不算坏事件。4xx 是客户端错误(参数错、未授权、限流拒绝),把它算进可用性会让 SLO 随爬虫和误用波动。本篇统一:可用性只统计 5xx;4xx 在 SLI 报告里看,不进 SLO。
- 窗口用滚动而非日历 。滚动 30 天(
[30d])每天向前滚动,不会出现「月初崩一次、月底因为跨月被清零」的情况;日历窗口(自然月 1 号清零)适合做财务式的结算报表,不适合做可靠性改进。本篇统一用滚动窗口。
SLO 目标值建议按服务等级分层,不是所有系统都拍 99.99%:
| 等级 | 典型服务 | 可用性 SLO | 延迟 SLO | 月可容忍不可用 |
|---|---|---|---|---|
| L1 核心 | 支付、登录、网关 | 99.99% | 99% 请求 ≤ 200ms | 4.32 min |
| L2 重要 | 订单、用户、库存 | 99.9% | 99% 请求 ≤ 500ms | 43.2 min |
| L3 一般 | 数据、报表、后台 | 99.5% | 95% 请求 ≤ 1s | 3.6 h |
| L4 内部 | 内部工具、批处理 | 99% | 95% 请求 ≤ 2s | 7.2 h |
2. SLO 配置样例
六要素可直接交给 Sloth 生成。Sloth 是一个开源的 SLO 生成器:把六要素写成一份 prometheus/v1 的 YAML,能帮你生成 Prometheus 的 SLI 录制规则、SLO 元数据规则,以及符合 Google SRE Workbook 的多窗口多燃尽率告警规则。
这份 spec 就是第五、六、七章的唯一输入:报告里的每一行、PromQL 里的每一个阈值,都从它推导。
spec 骨架 (version / service / labels / slos[]):
| 字段 | 必填 | 说明 |
|---|---|---|
version |
✅ | 固定 "prometheus/v1" |
service |
✅ | 系统编码,对应我们的 job_code |
labels |
| 服务级公共标签,会合并进所有生成规则的标签里 |
slos[].name |
✅ | SLO 名称,会拼成 sloth_id = <service>-<name> |
slos[].objective |
✅ | 目标值,范围 (0,100],写 99.9 而不是 0.999 |
slos[].labels |
| SLO 级标签,与 service.labels 合并 |
slos[].sli.events |
✅ | error_query(坏事件)+ total_query(总事件),必须用 {{.window}} 模板变量 |
slos[].alerting |
✅ | name + labels/annotations + page_alert / ticket_alert |
slo_plugins |
| 扩展插件链,例如「预算耗尽时发一条 info 告警」(见本节末尾) |
完整样例 sloth-slo.yml:
yaml
sversion: "prometheus/v1"
service: "gw" # 对应 job_code
labels:
job_code: "GW" # 用真实标签覆盖 job_code,后面回卷、下钻都靠它
job_alias: "统一网关"
team: "sre-platform"
runbook_url: "https://runbook.internal/gw"
slos:
# 可用性:99.99%,只看入口服务 5xx
- name: "availability"
objective: 99.99
description: "入口服务 HTTP 请求可用性(仅统计 ingress=true)"
labels:
category: "availability"
tier: "L1"
sli:
events:
# 分子分母必须带同一个 ingress="true",否则比值无意义
error_query: |
sum(rate(http_server_requests_seconds_count{
job_code="GW", ingress="true", status=~"5.."}[{{.window}}]))
total_query: |
sum(rate(http_server_requests_seconds_count{
job_code="GW", ingress="true"}[{{.window}}]))
alerting:
name: "GWAvailabilityErrorBudgetBurn"
labels:
category: "availability"
annotations:
summary: "GW 入口 5xx 错误预算燃烧过快"
runbook: "https://runbook.internal/gw/availability"
page_alert:
labels:
severity: "critical"
ticket_alert:
labels:
severity: "warning"
# 延迟:99% 的请求 ≤ 800ms。Sloth 的 events 型只算「坏事件占比」,
# 延迟的「超时」是 (总数 - le 桶),所以要写成两条相减
- name: "latency-800ms"
objective: 99
description: "入口服务 99% 的请求在 800ms 内返回(仅统计 ingress=true)"
labels:
category: "latency"
tier: "L1"
sli:
events:
error_query: |
(
sum(rate(http_server_requests_seconds_count{
job_code="GW", ingress="true"}[{{.window}}]))
-
sum(rate(http_server_requests_seconds_bucket{
job_code="GW", ingress="true", le="0.8"}[{{.window}}]))
)
total_query: |
sum(rate(http_server_requests_seconds_count{
job_code="GW", ingress="true"}[{{.window}}]))
alerting:
name: "GWLatencyErrorBudgetBurn"
labels:
category: "latency"
annotations:
summary: "GW 入口延迟错误预算燃烧过快"
page_alert:
labels:
severity: "critical"
ticket_alert:
# 延迟问题不叫醒值班:Sloth 用 disable 关掉 ticket 告警
disable: true
生成规则:
bash
# 单个文件 → 规则文件
sloth generate -i ./sloth-slo.yml -o ./generated-rules.yml
# 整目录(每个系统一份 spec)→ 镜像目录结构,适合 GitOps
sloth generate -i ./slos/ -o ./rules/
# CI 里只校验不生成,配合 gitlab/github 的 PR 检查
sloth validate -i ./sloth-slo.yml
# 常用参数
# -p / --plugins-path 加载 SLI 插件目录
# -l / --extra-labels 给所有规则追加标签,如 -l env=prod
# --default-slo-period 默认 SLO 周期,默认 30d
# --slo-period-windows-path 自定义多窗口告警阈值目录
# --disable-alerts 只生成录制规则,不生成告警
# -o - 输出到 stdout
Sloth 生成的关键指标:生成后不必再手写第七章的长窗口查询,直接查它产出的序列即可。
| 生成指标 | 含义 | 对应本文 |
|---|---|---|
slo:sli_error:ratio_rate<window> |
各窗口错误率(5m/30m/1h/2h/6h/1d/3d/30d) | 可用性 SLI |
slo:objective:ratio |
目标值,如 0.9999 |
第四章 SLO 目标 |
slo:error_budget:ratio |
错误预算率,1 - objective |
错误预算 |
slo:period_burn_rate:ratio |
整个 SLO 周期的燃尽率 | 预算消耗速度 |
slo:period_error_budget_remaining:ratio |
周期内预算剩余(0~1) | 预算剩余 |
slo:current_burn_rate:ratio |
5m 短窗口燃尽率(瞬时) | 燃尽告警 |
sloth_slo_info |
元数据(objective、service 等) | 附录 SLO 清单 |
标签侧还有一个好处:service 和 slos[].name 会自动拼成 sloth_id,并铺到 sloth_service / sloth_slo / sloth_window 上,第七章按 SLO 下钻时直接 by (sloth_service, sloth_slo) 即可,不用自己维护一套 slo 标签。
默认告警窗口与本文阈值的关系:Sloth 按 Google SRE Workbook 生成两组 page 告警、两组 ticket 告警,与第四章的 14.4 / 6 / 3 / 1 完全对应,只是把「燃尽率」换成了「窗口内预算消耗百分比」表述:
| 级别 | 长 / 短窗口 | 预算消耗 | 等价 Burn Rate |
|---|---|---|---|
| Page(quick) | 1h / 5m | 2% | 14.4 |
| Page(slow) | 6h / 30m | 5% | 6 |
| Ticket(quick) | 1d / 2h | 10% | 3 |
| Ticket(slow) | 3d / 6h | 10% | 1 |
若团队想改阈值(例如更敏感),用 AlertWindows 资源覆盖,不必改 spec(下面是默认值的等价写法,改数字即可):
yaml
apiVersion: sloth.slok.dev/v1
kind: AlertWindows
spec:
sloPeriod: 30d
page:
quick: { errorBudgetPercent: 2, shortWindow: 5m, longWindow: 1h }
slow: { errorBudgetPercent: 5, shortWindow: 30m, longWindow: 6h }
ticket:
quick: { errorBudgetPercent: 10, shortWindow: 2h, longWindow: 1d }
slow: { errorBudgetPercent: 10, shortWindow: 6h, longWindow: 3d }
bash
sloth generate \
--slo-period-windows-path ./windows/ \
-i ./sloth-slo.yml -o ./generated-rules.yml
预算耗尽时冻结发布 :Sloth 自带一个 contrib 插件,可以在预算见底时额外发一条告警,用来触发「变更冻结」流程。挂在根级 slo_plugins 下,对 spec 内所有 SLO 生效:
yaml
version: "prometheus/v1"
service: "gw"
labels:
job_code: "GW"
slo_plugins:
chain:
- id: "sloth.dev/contrib/error_budget_exhausted_alert/v1"
config:
threshold: 0.05 # 预算剩余 ≤5% 时告警
alert_name: "ErrorBudgetExhausted"
alert_labels:
severity: "info"
slos:
- name: "availability"
# ... 同上
两个容易踩的坑:
①
objective写的是百分数(99.9),不是小数(0.999),写成0.999会被当成 0.999% 的目标;② 延迟类 SLO 的
error_query必须用「总数 − le 桶」的减法,直接把_bucket当坏事件会算反。
3. 状态判定规则
SLO 报告的红黄绿不看单次指标峰值,只看两件事:预算还剩多少 、烧得多快。下表是阈值设置参考,第六章模板、第七章 PromQL 均以此为准:
| 层级 | 指标 | 正常 | 警告 | 严重 |
|---|---|---|---|---|
| 预算 | 错误预算剩余 | >50% | 20%~50% | <20% |
| 燃尽 | Burn Rate(1h) | <1 | 1~6 | >6 |
| 燃尽 | Burn Rate(6h) | <1 | 1~6 | >6 |
| 达成 | 达成率 vs 目标 | ≥ 目标 | 低于目标 ≤ 0.02pp | 低于目标 > 0.02pp |
状态映射关系(满足任一即取该状态,就高不就低):
| 状态 | 条件 |
|---|---|
| 🟢 正常 | 错误预算剩余 > 50%,且各窗口 Burn Rate < 1 |
| 🟡 警告 | 错误预算剩余 20%~50%,或任一窗口 Burn Rate 在 1~6 之间 |
| 🔴 严重 | 错误预算剩余 < 20%,或任一窗口 Burn Rate > 6,或 30d SLI 已低于目标(预算透支) |
多窗口 Burn Rate 告警阈值 (对应配置里的 alerting 段),下表给出的是「烧多快才值得叫人」:
| 告警级别 | Burn Rate | 预算消耗 | 触发窗口(长 / 短) | 含义 |
|---|---|---|---|---|
| 🔴 Critical(Page) | 14.4 | 1h 消耗 2% | 1h / 5m | 约 2 天烧完 30 天预算,立即处理 |
| 🔴 Critical(Page) | 6 | 6h 消耗 5% | 6h / 30m | 约 5 天烧完,立即处理 |
| 🟡 Warning(Ticket) | 1 | 3d 消耗 10% | 3d / 6h | 约 30 天烧完,当班跟进 |
| 🟢 无需动作 | < 1 | --- | --- | 预算消耗慢于预期,健康 |
- 每个级别都用长窗口 + 短窗口 同时判定:长窗口保证不被瞬时抖动误报,短窗口保证故障恢复后告警能快速自愈。两个条件用
and连接。 - 阈值不是拍的,是倒推的:30 天窗口、1 小时消耗 2% 预算,则
2% × (30d/1h) / 1h...,化简后Burn Rate = 预算消耗% × 窗口时长 / 长窗口时长,例如 2% × 720h / 1h = 14.4。
三条展示原则
- 正文只放最差 Top N:SLO 通常只覆盖几十个系统,但加上延迟类 SLO 后会翻倍。系统级 SLO 可全量展示,错误预算明细、燃尽告警、趋势对比默认最差 20 条,完整数据放附件。
- 状态优先于评分:用 🟢🟡🔴 表达,比「健康度 87.3 分」更容易在一屋子人里达成共识。
- 诚实标注口径 :报告必须写清「本报告只统计
ingress="true"的入口服务」。漏了这句,业务方拿 SLO 达成率去对账时会发现数字和监控面板对不上,报告的可信度就没了。
注:第六、七两章,是利用 AI 工具根据第三、四、五章所定义内容自动生成,使用的是 WorkBuddy 工具,其他工具一样能达到效果。
五、报告组成
SLO 报告骨架共 7 个部分,比 SLI 报告少了主机/JVM 下钻、多了预算与燃尽:
| 序号 | 组成部分 | 目的 | 主要内容 | 展示规则 |
|---|---|---|---|---|
| 1 | 报告元信息 | 明确数据口径 | 报告日期、SLO 评估窗口、Prometheus 数据源、生成时间、标签口径、SLO 目标清单、显示规则 | 固定头部 |
| 2 | 全局概览 | 一眼看整体达标 | SLO 总数、达标/未达标数、平均错误预算剩余、透支系统数、Critical/Warning 告警数 | 汇总卡片/小表 |
| 3 | SLO 达成总览 | 所有 SLO 横向对比 | 每个 SLO 一行:目标、窗口、当前 SLI、达成率、错误预算剩余、Burn Rate、预计耗尽时间、状态。SLI 只统计 ingress="true" 入口服务 |
全部系统或最差 Top 20 |
| 4 | 错误预算消耗明细 | 看预算被谁吃掉 | 按系统/入口服务:请求总量、坏事件数、预算总量、已消耗、剩余、预计耗尽时间;再按 status 拆解坏事件构成 | 默认最差 20 条 |
| 5 | 燃尽率与告警 | 驱动处理动作 | 多窗口 Burn Rate、告警级别、触发窗口对、当前值、阈值、建议动作 | 按严重程度 Top 20 |
| 6 | 趋势对比 | 判断恶化/改善 | 与上周/上月对比:达成率、错误预算剩余、Burn Rate、告警数 | SLO 级,最差 Top 20 |
| 7 | 附录与下载 | 支撑下钻与复现 | SLO 定义、错误预算公式、Burn Rate 阈值、标签说明、完整数据附件 | 附件 JSON/CSV |
- 正文只放最差 Top N:达成总览可全量,错误预算明细、燃尽告警、趋势对比默认展示最差 20 条,完整数据放附件。
- 状态优先于评分:所有「状态」列统一使用 🟢🟡🔴,判定依据见第四章第 3 项。
- 全篇统一取数范围 :第 3、4、5 项的所有 SLI、坏事件、预算计算,分子分母都带
ingress="true",聚合维度job_code, job_alias, svc;主机层与 JVM 指标不出现在本报告中,需要时由第 5 项的建议动作指向 SLI 报告。
六、报告模板
要求默认同时生成 markdown 和 html 两种格式:html 用于周会/邮件/门户展示,markdown 用于归档与二次编辑。模板由 AI 工具生成,生成后可再调整。
模板严格按第五章的 7 个部分组织,字段与顺序一一对应;所有阈值、状态判定与第四章保持一致;取数范围与第三章保持一致。下表为示例数据(口径统一后):
markdown
## 一、全局概览
| 指标 | 数值 |
|---|---:|
| SLO 总数 | 42 |
| 达标 / 未达标 | 🟢 34 / 🔴 8 |
| 平均错误预算剩余 | 46.3% |
| 预算已透支系统 | 1 |
| 预计 7 天内耗尽预算 | 2 |
| 未恢复告警 | 🔴 Critical 3 / 🟡 Warning 12 |
> 达标指「窗口内 SLI ≥ 目标」;错误预算剩余为各 SLO 的算术平均。任一指标满足第四章的严重条件即为 🔴。
---
## 二、SLO 达成总览
> **SLI 只统计 `ingress="true"` 的入口服务**,聚合维度 `job_code, job_alias, svc`。
**可用性 SLO(滚动 30 天)**
| 系统 | 入口服务 | SLO 目标 | 当前 SLI | 达成率 | 预算剩余 | Burn Rate(1h) | 预计耗尽 | 状态 |
|---|---|---:|---:|---:|---:|---:|---|---|
| RISK | risk-gw | 99.90% | 99.8802% | 99.98% | -19.8%(透支) | 18.6 | 已耗尽 | 🔴 严重 |
| CRM | crm-gw | 99.90% | 99.9350% | 99.96% | 35.0% | 7.4 | 1.4 天 | 🔴 严重 |
| DATA | data-gw | 99.50% | 99.5020% | 100.00% | 0.4% | 3.1 | 0.9 小时 | 🔴 严重 |
| USER | user-gw | 99.90% | 99.9550% | 100.00% | 55.0% | 1.2 | 13.8 天 | 🟡 警告 |
| ORDER | order-gw | 99.90% | 99.9600% | 100.00% | 60.0% | 0.8 | 22.5 天 | 🟢 正常 |
| INV | inv-gw | 99.90% | 99.9650% | 100.00% | 65.0% | 0.7 | >30 天 | 🟢 正常 |
| PAY | pay-gw | 99.95% | 99.9800% | 100.00% | 60.0% | 0.5 | >30 天 | 🟢 正常 |
| GW | gateway | 99.99% | 99.9960% | 100.00% | 60.0% | 0.6 | >30 天 | 🟢 正常 |
**延迟 SLO(滚动 7 天)**
| 系统 | 入口服务 | SLO 目标 | 当前达标请求占比 | 达成率 | 预算剩余 | 状态 |
|---|---|---:|---:|---:|---:|---|
| CRM | crm-gw | 99% ≤ 800ms | 98.60% | 99.60% | -40.0%(透支) | 🔴 严重 |
| USER | user-gw | 99% ≤ 500ms | 99.20% | 100.00% | 20.0% | 🟡 警告 |
| RISK | risk-gw | 99% ≤ 800ms | 99.40% | 100.00% | 40.0% | 🟡 警告 |
| ORDER | order-gw | 99% ≤ 500ms | 99.60% | 100.00% | 60.0% | 🟢 正常 |
| PAY | pay-gw | 99% ≤ 500ms | 99.80% | 100.00% | 80.0% | 🟢 正常 |
| GW | gateway | 99% ≤ 200ms | 99.60% | 100.00% | 60.0% | 🟢 正常 |
> 其余 28 个系统可用性 SLO 中 🟢 正常 26 个、🟡 警告 2 个,完整数据见附件。延迟 SLO 的「达成率」上限截断为 100%(`clamp_max`),避免超额达标显示成 120% 引发误解。
---
## 三、错误预算消耗明细
> 预算按请求数口径:`预算总量 = 窗口内入口服务请求总量 × (1 - 目标值)`。**分子分母均为 `ingress="true"`**。
**最差 Top 6(按预算剩余升序)**
| 系统 | 入口服务 | 30d 请求总量 | 坏事件(5xx) | 预算总量 | 已消耗 | 预算剩余 | 预计耗尽 |
|---|---|---:|---:|---:|---:|---:|---|
| RISK | risk-gw | 15.55 亿 | 186.3 万 | 155.5 万 | 119.8% | -19.8% | 已耗尽 |
| DATA | data-gw | 10.37 亿 | 516.3 万 | 518.4 万 | 99.6% | 0.4% | 0.9 小时 |
| CRM | crm-gw | 23.33 亿 | 151.6 万 | 233.3 万 | 65.0% | 35.0% | 1.4 天 |
| USER | user-gw | 46.66 亿 | 210.0 万 | 466.6 万 | 45.0% | 55.0% | 13.8 天 |
| ORDER | order-gw | 82.94 亿 | 331.8 万 | 829.4 万 | 40.0% | 60.0% | 22.5 天 |
| GW | gateway | 129.60 亿 | 51.8 万 | 129.6 万 | 40.0% | 60.0% | >30 天 |
> 共 36 个入口服务,正文展示最差 6 条;完整明细见附件。「预算剩余」为负表示已透支,即当前 SLI 已低于 SLO 目标。
**坏事件构成(RISK / risk-gw,30d)**
| 状态码 | 坏事件数 | 占比 | 主要来源 |
|---|---:|---:|---|
| 500 | 112.6 万 | 60.4% | risk-eval 规则引擎超时 |
| 504 | 51.2 万 | 27.5% | 下游画像服务网关超时 |
| 503 | 22.5 万 | 12.1% | 实例摘除期间的连接拒绝 |
> 坏事件构成按 `status` 拆解 `http_server_requests_seconds_count{ingress="true", status=~"5.."}`。这一张表决定了第 4 项建议动作的方向------若 504 占比高,方向是下游依赖与超时配置;若 503 占比高,方向是发布流程与摘流节奏。
---
## 四、燃尽率与告警
> 多窗口判定:长窗口 + 短窗口需同时超阈值才触发。阈值为相对错误预算的 Burn Rate。
| 系统 | SLO | 级别 | 触发窗口 | 长窗口 BR | 短窗口 BR | 阈值 | 窗口内预算消耗 | 建议动作 |
|---|---|---|---|---:|---:|---:|---:|---|
| RISK | 可用性 99.9% | 🔴 Critical | 1h / 5m | 18.6 | 21.4 | >14.4 | 2.6%(1h) | 立即止损:回滚最近变更,入口降级非核心路径 |
| CRM | 可用性 99.9% | 🔴 Critical | 6h / 30m | 7.4 | 5.1 | >6 | 6.2%(6h) | 排查下游依赖与线程池,入口限流熔断 |
| DATA | 可用性 99.5% | 🟡 Warning | 3d / 6h | 3.1 | 2.4 | >1 | 31.0%(3d) | 检查批处理调度窗口与慢依赖,错峰执行 |
| USER | 延迟 99% ≤500ms | 🟡 Warning | 3d / 6h | 1.2 | 1.4 | >1 | 12.0%(3d) | 排查 DB 慢查询与连接池,关注缓存命中率 |
| RISK | 延迟 99% ≤800ms | 🟡 Warning | 3d / 6h | 1.1 | 1.3 | >1 | 11.0%(3d) | 关联 SLI 报告查看 risk-eval 的 P99 明细 |
| INV | 可用性 99.9% | 🟢 无需动作 | --- | 0.7 | 0.6 | <1 | --- | 无需动作 |
> 按严重程度排序,正文展示 Top 20;完整告警列表见附件。「窗口内预算消耗」= `Burn Rate × 触发窗口时长 / SLO 总窗口时长`。
---
## 五、趋势对比
> 与上周同口径对比;本周为本份报告的滚动窗口,上周为 `offset 7d`。所有数值均只统计 `ingress="true"` 入口服务。
| 系统 | 达成率 上周 | 达成率 本周 | 预算剩余 上周 | 预算剩余 本周 | BR 上周 | BR 本周 | 告警 上周 | 告警 本周 | 趋势判断 |
|---|---:|---:|---:|---:|---:|---:|---:|---:|---|
| RISK | 99.920% | 99.880% | 42.0% | -19.8% | 1.2 | 18.6 | 0 | 3 | 显著恶化 |
| CRM | 99.940% | 99.935% | 58.0% | 35.0% | 0.9 | 7.4 | 1 | 4 | 恶化 |
| DATA | 99.510% | 99.502% | 62.0% | 0.4% | 0.4 | 3.1 | 0 | 2 | 恶化 |
| USER | 99.960% | 99.955% | 63.0% | 55.0% | 0.5 | 1.2 | 0 | 1 | 略恶化 |
| INV | 99.960% | 99.965% | 64.0% | 65.0% | 0.6 | 0.7 | 0 | 0 | 平稳 |
| ORDER | 99.960% | 99.960% | 60.0% | 60.0% | 0.8 | 0.8 | 0 | 0 | 平稳 |
| GW | 99.996% | 99.996% | 60.0% | 60.0% | 0.6 | 0.6 | 0 | 0 | 平稳 |
> 「告警」为未恢复告警数。如需与上月对比,按同一表格追加「上月」列,PromQL 中把 `offset 7d` 换成 `offset 30d` 即可。
---
## 六、附录与下载
**SLO 定义清单(节选)**
| SLO 名称 | 系统 | 类型 | 取数范围 | 目标 | 窗口 | 坏事件口径 | Owner |
|---|---|---|---|---|---|---:|---|
| gw-availability | GW | 可用性 | `ingress="true"` | 99.99% | 30d | 5xx | sre-platform |
| pay-availability | PAY | 可用性 | `ingress="true"` | 99.95% | 30d | 5xx | pay-team |
| crm-availability | CRM | 可用性 | `ingress="true"` | 99.90% | 30d | 5xx | crm-team |
| data-availability | DATA | 可用性 | `ingress="true"` | 99.50% | 30d | 5xx | data-team |
| gw-latency-800ms | GW | 延迟 | `ingress="true"` | 99% ≤ 800ms | 30d | >800ms | sre-platform |
> 表名即 Sloth spec 里的 `slos[].name`,`sloth_id` 为 `<service>-<name>`(如 `gw-availability`)。Owner 来自 spec 的 `labels.team`,查 `sloth_slo_info` 即可还原这份清单。
**标签说明**
| 层级/层 | 标签或维度 | 说明 |
|---|---|---|
| 系统层 | `job_code` | 系统编码,聚合主键 |
| 系统层 | `job_alias` | 系统名称,仅用于展示,与 `job_code` 1:1 |
| 服务层 | `svc` | 服务名 |
| 实例层 | `instance` | 实例/主机标识(本报告不使用) |
| 入口标识 | `ingress` | **`ingress="true"` 是 SLO 的唯一取数范围**,表示入口服务(一般是 Gateway) |
| SLO 标识(Sloth) | `sloth_id` / `sloth_service` / `sloth_slo` | 由 spec 的 `service` + `slos[].name` 自动生成,形如 `gw-availability` |
| SLO 标识(自定义) | `slo` | 自行加在 spec `labels` 上的类型标签,`availability` / `latency` |
| SLO 聚合维度 | `job_code, job_alias, svc` + `ingress="true"` | 可用性、吞吐、延迟 |
**错误预算与 Burn Rate 公式**
| 指标 | 公式 | Sloth 生成序列 |
|---|---|---|
| 错误预算率 | `1 - objective`(如 99.9% → 0.001) | `slo:error_budget:ratio` |
| 预算总量(坏事件数) | `窗口内入口请求总量 × (1 - objective)` | --- |
| 已消耗预算 | `窗口内入口 5xx 请求数` | `slo:sli_error:ratio_rate30d` |
| 预算剩余(%) | `1 - 已消耗 / 预算总量` | `slo:period_error_budget_remaining:ratio` |
| Burn Rate | `实际错误率 / (1 - objective)` | `slo:current_burn_rate:ratio` / `slo:period_burn_rate:ratio` |
| 预计耗尽时间 | `SLO 窗口时长 × 预算剩余比例 / Burn Rate` | 由上面两条算出 |
**Burn Rate 阈值**
| 告警级别 | Burn Rate | 预算消耗 | 触发窗口(长 / 短) |
|---|---:|---|---|
| 🔴 Critical(Page) | 14.4 | 1h 消耗 2% | 1h / 5m |
| 🔴 Critical(Page) | 6 | 6h 消耗 5% | 6h / 30m |
| 🟡 Warning(Ticket) | 3 | 1d 消耗 10% | 1d / 2h |
| 🟡 Warning(Ticket) | 1 | 3d 消耗 10% | 3d / 6h |
**附件**:
- `slo_detail_2026-10-15.csv`:全部 SLO 达成情况与预算余额明细
- `slo_detail_2026-10-15.json`:结构化原始数据
- `sloth-slo.yml`:本次评估使用的 Sloth spec 快照(SLO 目标配置)
- `generated-rules.yml`:`sloth generate` 产出的录制规则与告警规则
> 若 Spring Boot 未开启直方图,延迟类 SLO 无法用 `le` 桶计算达标占比,此时应改用 `http_server_requests_seconds_max` 统计「超标请求数」,并在报告元信息中注明口径变化。
*本报告由 SLO 达标系统自动生成,SLI 仅统计 `ingress="true"` 的入口服务。*
七、涉及的 PromQL
第六章的每一张表都由下面这些查询填充。本章的取数范围与第三章严格一致 (ingress="true",聚合维度 job_code, job_alias, svc),窗口与第四章的配置一致 (可用性 30d,延迟 7d,Burn Rate 按多窗口)。
长窗口查询在生产环境必须用 Recording Rule 预聚合。直接对 [30d] 跑 rate/increase 既慢又吃内存,下面第 0 节给出推荐做法,第 1 节之后为了可读性统一用 increase(...[30d]) 表达等价逻辑。
本章由 AI 工具自动生成,若你重新用 AI 跑一遍,需检查生成的 PromQL 是否正确。小节顺序按第四章的判定链路排布:预聚合 → 可用性 SLI → 错误预算 → 延迟 SLI → 燃尽率 → 告警 → 趋势。
0. 预聚合(Recording Rule,推荐)
yaml
groups:
- name: slo_ingress_recording
interval: 30s
rules:
# 入口服务请求速率(按系统 + 服务),只统计 ingress="true"
- record: slo:ingress_requests:rate5m
expr: |
sum by (job_code, job_alias, svc) (
rate(http_server_requests_seconds_count{ingress="true"}[5m])
)
# 入口服务坏事件速率(5xx),分子分母同一过滤条件
- record: slo:ingress_errors:rate5m
expr: |
sum by (job_code, job_alias, svc) (
rate(http_server_requests_seconds_count{ingress="true", status=~"5.."}[5m])
)
有了这两条序列,30d 窗口的比值用 sum_over_time 即可,无需回查原始计数器:
bash
# 30d 错误率(近似),分子分母均为入口服务
sum by (job_code, job_alias, svc) (sum_over_time(slo:ingress_errors:rate5m[30d]))
/
sum by (job_code, job_alias, svc) (sum_over_time(slo:ingress_requests:rate5m[30d]))
1. 可用性 SLI 与达成率
bash
# 30d 可用性 SLI(仅 ingress="true")
1 - (
sum by (job_code, job_alias, svc) (
increase(http_server_requests_seconds_count{ingress="true", status=~"5.."}[30d])
)
/
sum by (job_code, job_alias, svc) (
increase(http_server_requests_seconds_count{ingress="true"}[30d])
)
)
# 30d 达成率(%,以目标值 99.9% 为例),上限截断为 100
clamp_max(
(
1 - (
sum by (job_code, job_alias, svc) (
increase(http_server_requests_seconds_count{ingress="true", status=~"5.."}[30d])
)
/
sum by (job_code, job_alias, svc) (
increase(http_server_requests_seconds_count{ingress="true"}[30d])
)
)
) / 0.999 * 100,
100
)
# 若目标值已做成合成序列 slo_target{slo="availability"}
clamp_max(
(
1 - (
sum by (job_code, job_alias) (
increase(http_server_requests_seconds_count{ingress="true", status=~"5.."}[30d])
)
/
sum by (job_code, job_alias) (
increase(http_server_requests_seconds_count{ingress="true"}[30d])
)
)
)
/ on (job_code, job_alias) group_left()
slo_target{slo="availability"} * 100,
100
)
# 是否达标(返回 1 = 达标 / 0 = 未达标)
(
(
1 - (
sum by (job_code, job_alias) (
increase(http_server_requests_seconds_count{ingress="true", status=~"5.."}[30d])
)
/
sum by (job_code, job_alias) (
increase(http_server_requests_seconds_count{ingress="true"}[30d])
)
)
) >= on (job_code, job_alias) group_left() slo_target{slo="availability"}
)
全篇最容易写错的一处 :分子(5xx)和分母(总请求)必须带同一个
ingress="true"。只给分子加过滤会算出虚低的错误率,只给分母加过滤会算出虚高的错误率。看板对不上,九成是这里漏了一个过滤条件。
2. 错误预算
bash
# 窗口内入口请求总量(用于算预算总量)
sum by (job_code, job_alias, svc) (
increase(http_server_requests_seconds_count{ingress="true"}[30d])
)
# 预算总量(允许的坏事件数),目标 99.9% 即乘以 0.001
sum by (job_code, job_alias, svc) (
increase(http_server_requests_seconds_count{ingress="true"}[30d])
) * 0.001
# 已消耗预算(坏事件数)
sum by (job_code, job_alias, svc) (
increase(http_server_requests_seconds_count{ingress="true", status=~"5.."}[30d])
)
# 错误预算剩余(%),负值表示已透支
(
1 - (
sum by (job_code, job_alias, svc) (
increase(http_server_requests_seconds_count{ingress="true", status=~"5.."}[30d])
)
/
(
sum by (job_code, job_alias, svc) (
increase(http_server_requests_seconds_count{ingress="true"}[30d])
) * 0.001
)
)
) * 100
# 坏事件构成:按状态码拆解(用于「坏事件构成」小表)
sum by (job_code, job_alias, svc, status) (
increase(http_server_requests_seconds_count{ingress="true", status=~"5.."}[30d])
)
3. 延迟 SLI(达标请求占比)
bash
# 延迟 SLI:≤800ms 的请求占比(仅 ingress="true")
sum by (job_code, job_alias, svc) (
increase(http_server_requests_seconds_bucket{ingress="true", le="0.8"}[7d])
)
/
sum by (job_code, job_alias, svc) (
increase(http_server_requests_seconds_count{ingress="true"}[7d])
)
# 延迟 SLO 的错误预算剩余(%),目标 99% 即预算率 0.01
(
1 - (
1 - (
sum by (job_code, job_alias, svc) (
increase(http_server_requests_seconds_bucket{ingress="true", le="0.8"}[7d])
)
/
sum by (job_code, job_alias, svc) (
increase(http_server_requests_seconds_count{ingress="true"}[7d])
)
)
) / 0.01
) * 100
# 未开启直方图时的退路:超标请求数(按 max 近似)
sum by (job_code, job_alias, svc) (
http_server_requests_seconds_max{ingress="true"} > 0.8
)
le="0.8"这个桶必须显式声明才会存在,Spring Boot 侧需配置:
management.metrics.distribution.slo.http.server.requests=200ms,500ms,800ms,1s,并开启
management.metrics.distribution.percentiles-histogram.http.server.requests=true。只开直方图不开 SLO 边界,桶是默认的(1ms~10s 指数分布),大概率没有正好 0.8 的桶。
4. 燃尽率(Burn Rate)
bash
# 通用形式:某一窗口的 Burn Rate(目标 99.9% → 预算率 0.001)
# 把 [WINDOW] 换成 1h / 6h / 3d / 5m / 30m 即可
(
sum by (job_code, job_alias, svc) (
rate(http_server_requests_seconds_count{ingress="true", status=~"5.."}[1h])
)
/
sum by (job_code, job_alias, svc) (
rate(http_server_requests_seconds_count{ingress="true"}[1h])
)
) / 0.001
# Critical:1h 长窗口 + 5m 短窗口,阈值 14.4(两个窗口需同时超阈值)
(
(
sum by (job_code, job_alias) (
rate(http_server_requests_seconds_count{ingress="true", status=~"5.."}[1h])
)
/
sum by (job_code, job_alias) (
rate(http_server_requests_seconds_count{ingress="true"}[1h])
)
) / 0.001 > 14.4
)
and
(
(
sum by (job_code, job_alias) (
rate(http_server_requests_seconds_count{ingress="true", status=~"5.."}[5m])
)
/
sum by (job_code, job_alias) (
rate(http_server_requests_seconds_count{ingress="true"}[5m])
)
) / 0.001 > 14.4
)
# Critical:6h 长窗口 + 30m 短窗口,阈值 6(结构同上,仅替换窗口与阈值)
(
(
sum by (job_code, job_alias) (
rate(http_server_requests_seconds_count{ingress="true", status=~"5.."}[6h])
)
/
sum by (job_code, job_alias) (
rate(http_server_requests_seconds_count{ingress="true"}[6h])
)
) / 0.001 > 6
)
and
(
(
sum by (job_code, job_alias) (
rate(http_server_requests_seconds_count{ingress="true", status=~"5.."}[30m])
)
/
sum by (job_code, job_alias) (
rate(http_server_requests_seconds_count{ingress="true"}[30m])
)
) / 0.001 > 6
)
# Warning:3d 长窗口 + 6h 短窗口,阈值 1
(结构同上,替换窗口为 3d / 6h、阈值改为 1)
# 预计耗尽时间(天)= 窗口天数 × 预算剩余比例 / Burn Rate
30 * (
1 - (
sum by (job_code, job_alias) (
increase(http_server_requests_seconds_count{ingress="true", status=~"5.."}[30d])
)
/
(
sum by (job_code, job_alias) (
increase(http_server_requests_seconds_count{ingress="true"}[30d])
) * 0.001
)
)
) / (
(
sum by (job_code, job_alias) (
rate(http_server_requests_seconds_count{ingress="true", status=~"5.."}[1h])
)
/
sum by (job_code, job_alias) (
rate(http_server_requests_seconds_count{ingress="true"}[1h])
)
) / 0.001
)
5. 告警统计
告警序列
ALERTS由 Prometheus 告警规则产生,默认仅带job_code(及severity)等标签;SLO 相关的 Burn Rate 告警建议在规则里补上slo标签,以便按 SLO 维度统计。
bash
# 未恢复告警数(按系统 + 严重程度)
sum by (job_code, severity) (
ALERTS{alertstate="firing"}
)
# SLO 燃尽告警数(按系统 + SLO 类型 + 级别)
sum by (job_code, slo, severity) (
ALERTS{alertstate="firing", alert_type="slo_burn_rate"}
)
6. 趋势对比(上周 / 上月)
对应第五章第 6 项。全部为入口服务口径,
offset 7d取上周,offset 30d取上月,其余结构不变。
bash
# 上周同期的 30d 可用性 SLI
1 - (
sum by (job_code, job_alias) (
increase(http_server_requests_seconds_count{ingress="true", status=~"5.."}[30d] offset 7d)
)
/
sum by (job_code, job_alias) (
increase(http_server_requests_seconds_count{ingress="true"}[30d] offset 7d)
)
)
# 上周同期的错误预算剩余(%)
(
1 - (
sum by (job_code, job_alias) (
increase(http_server_requests_seconds_count{ingress="true", status=~"5.."}[30d] offset 7d)
)
/
(
sum by (job_code, job_alias) (
increase(http_server_requests_seconds_count{ingress="true"}[30d] offset 7d)
) * 0.001
)
)
) * 100
# 上周同期的 Burn Rate(1h 窗口)
(
sum by (job_code, job_alias) (
rate(http_server_requests_seconds_count{ingress="true", status=~"5.."}[1h] offset 7d)
)
/
sum by (job_code, job_alias) (
rate(http_server_requests_seconds_count{ingress="true"}[1h] offset 7d)
)
) / 0.001
# 上周同期未恢复告警数
sum by (job_code) (
ALERTS{alertstate="firing"} offset 7d
)
# 与上月对比:将上述 offset 7d 替换为 offset 30d 即可
八、两种报告编写方式
和 SLI 报告一样,SLO 报告的编写路径也是两条。
1. 利用 AI 工具自动生成报告
- 把第三到七章直接转成 SKILL:标签体系、SLO 定义与判定规则、报告组成、报告模板、PromQL 这五章合起来,就是一份完整的 SLO 技能定义。
- 人工检查 SKILL 是否符合要求 ,SLO 报告比 SLI 报告多三个高危检查点:
- 每个 PromQL 的分子分母是否都带
ingress="true"; - 目标值、窗口、Burn Rate 阈值是否与第四章配置一致;
- 「达成率」是否做了
clamp_max(..., 100),避免超额达标被显示成 120%。
- 每个 PromQL 的分子分母是否都带
- 直接调用 SKILL 生成报告:跑一次即可产出 markdown 与 html 两份报告。
2. 开发 Python 脚本自动生成报告
适用于公司内部没有部署 AI 工具的情况。用 AI 工具把上一步的 SKILL 转成可执行的 Python 脚本,在内网执行并查看输出。脚本化时建议把 SLO 配置(第四章的 YAML)作为唯一输入,不要在脚本里硬编码目标值与阈值,否则改一次 SLO 就要改一次代码。
九、小结
本篇核心要点:
- 取数范围只有一个 :SLO 的 SLI、坏事件、错误预算全部只统计
ingress="true"的入口服务;分子分母必须带同一个过滤条件。 - SLI 报告看现状,SLO 报告看目标:前者用 5m/1h 判异常、覆盖全量指标(含主机与 JVM),后者用 7d/30d 判达标、只覆盖入口服务的可用性/延迟/吞吐。两份报告数字不同口径不同,不是矛盾。
- SLO 六要素缺一不可:SLI、取数范围、目标值、时间窗口、错误预算、坏事件口径;目标值建议按 L1~L4 分层,不要一律拍 99.99%。
- 错误预算回答「还剩多少」,Burn Rate 回答「烧得多快」 :
Burn Rate = 实际错误率 / (1 - SLO),14.4 / 6 / 1 三档对应 1h / 6h / 3d 的预算消耗节奏,用长窗口 + 短窗口同时判定。 - 报告正文只放最差 Top N:达成总览可全量,错误预算明细、燃尽告警、趋势对比默认 20 条,完整明细进附件。
- 两种落地方式:有 AI 工具就把这几章转成 SKILL 直接生成,没有就把 SKILL 转成 Python 脚本在内网跑。