第11篇 监控报告编写:SLO/SLI 与数据分析方法

经过前几篇的逐步建设,监控体系已具备完整的采集、展示与告警能力。现在还缺最后一环:如何把监控数据翻译成可对外沟通的报告。

故障处理完毕,Leader 问「这次影响有多大?」;月底复盘,需要回答「上个月的可用性达标了吗?」;新系统上线前,业务方问「这个服务的 SLO 是多少?」。这些问题单靠看 Dashboard 无法回答,需要一份结构化的监控报告。

本篇目标:讲清楚 SLO/SLI 的定义方法,给出基于现有 Prometheus 指标计算可用性的 PromQL,并提供一套可复用的监控报告模板,让监控数据最终服务于决策。


一、SLO 与 SLI:先定义「好不好」

1、基本概念

术语 全称 含义 一句话理解
SLI Service Level Indicator 服务等级指标,即一个可量化的测量值 你测量的东西
SLO Service Level Objective 服务等级目标,即 SLI 的目标阈值 你承诺的值
SLA Service Level Agreement 服务等级协议,即未达标时的后果 你承诺不兑现的代价

⚠️ 本篇只讲 SLI 与 SLO。SLA 涉及合同、赔偿等法务内容,不在监控工程师的职权范围内,但监控数据是 SLA 核算的事实依据。

关系公式:

复制代码
SLI(实际值)≤ SLO(目标值)→ 服务达标
SLI(实际值)> SLO(目标值)→ 服务未达标

2、SLI 的选择原则:USE → RED → 业务

选择哪些指标作为 SLI,应遵循方法论逐层推导。

第一层:基础设施 SLI(USE 方法)

资源 SLI 数据来源 PromQL
CPU 利用率 ≤ 80% node_exporter 100 - (avg by(instance) (rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100)
内存 利用率 ≤ 80% node_exporter 100 - (node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes) * 100
磁盘 使用率 ≤ 80% node_exporter `(1 - node_filesystem_avail_bytes{fstype!~"tmpfs
网络 丢包率 ≤ 0.1% node_exporter rate(node_network_receive_errs_total[5m]) / rate(node_network_receive_bytes_total[5m]) * 100

第二层:应用 SLI(RED 方法)

维度 SLI 数据来源 PromQL
Rate(流量) QPS 在预期范围内 Micrometer / Nginx sum(rate(http_server_requests_seconds_count[5m])) by (job, svc)
Errors(错误) 5xx 错误率 ≤ 1% Micrometer sum(rate(http_server_requests_seconds_count{status=~"5.."}[5m])) by (job, svc) / sum(rate(http_server_requests_seconds_count[5m])) by (job, svc) * 100
Duration(延迟) P99 延迟 ≤ 500ms Micrometer(需开启 histogram) histogram_quantile(0.99, sum(rate(http_server_requests_seconds_bucket[5m])) by (le))
Duration(延迟) 成功请求的平均响应时间 ≤ 500ms(延迟折中方案) Micrometer sum(rate(http_server_requests_seconds_sum{status=~"2.."}[5m])) by (job, svc) / sum(rate(http_server_requests_seconds_count{status=~"2.."}[5m])) by (job, svc) * 1000

第三层:业务 SLI(自定义埋点)

此层业务属性强,不在本篇展开。典型业务 SLI 如:订单成功率 ≥ 99.9%、支付完成耗时 ≤ 2s。

3、SLO 的设定方法

SLO 不是拍出来的,而是基于历史数据 + 业务容忍度计算出来的。

三步法:

  1. 回顾历史:拉取过去 30 天的 SLI 数据,观察 P50 / P95 / P99 分布;
  2. 设定宽松目标:以 P95 值为基准,取整作为 initial SLO;
  3. 迭代收严:稳定运行 1~2 个月后,逐步向 P90 靠拢。

示例:某服务过去 30 天 P99 延迟分布

百分位 值
P50 120ms
P95 380ms
P99 650ms
最大值 2.1s

初始 SLO 可以设为 P99 ≤ 800ms (给 P99 留 20% 余量),稳定后收严到 P99 ≤ 500ms。

4、SLI → SLO 配置示例

以下是一个配置示例,涵盖主机、应用、依赖三层,但注意还未完整验证过,后续再写一篇专门介绍 SLO 的文章:

yaml 复制代码
# SLO 配置示例
slos:
  # 主机层
  - name: "主机 CPU 可用性"
    sli: "avg_over_time((100 - (avg by(instance) (rate(node_cpu_seconds_total{mode=\"idle\"}[5m])) * 100))[1h:1m])"
    target: "< 80"
    period: "30d"
    severity: "warning"

  # 应用层
  - name: "应用 5xx 错误率"
    sli: "avg_over_time((sum(rate(http_server_requests_seconds_count{status=~\"5..\"}[5m])) / sum(rate(http_server_requests_seconds_count[5m])) * 100)[1h:1m])"
    target: "< 1"
    period: "30d"
    severity: "critical"

 # - name: "应用 P99 延迟"
 #   sli: "avg_over_time((histogram_quantile(0.99, sum(rate(http_server_requests_seconds_bucket[5m])) by (le)))[1h:1m])"
 #   target: "< 0.5"
 #   period: "30d"
 #   severity: "warning"
  - name: "应用平均响应时间"
    sli: "sum(rate(http_server_requests_seconds_sum{status=~\"2..\"}[5m])) by (job, svc) /  sum(rate(http_server_requests_seconds_count{status=~\"2..\"}[5m])) by (job, svc) * 1000 "
    target: "< 500"
    period: "30d"
    severity: "warning"

  # 依赖层
  - name: "外部探测可用率"
    sli: "avg_over_time(avg_over_time(probe_success[5m])[1h:1m]) * 100"
    target: ">= 99.9"
    period: "30d"
    severity: "critical"

三、用 PromQL 计算可用性

SLO 不是静态值,需要定期计算「实际 SLI 达标了多少时间」。以下给出两类常用计算方法。

1、基于时间的可用性(最常用)

promql 复制代码
# 计算过去 30 天 CPU 使用率 ≤ 80% 的占比
avg_over_time(
  (
    100 - (avg by(instance) (rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100)
    <= bool 80
  )[30d:1m]
) * 100
  • <= bool 80:达标返回 1,未达标返回 0
  • [30d:1m]:按每分钟采样一次
  • avg_over_time(...) * 100:达标时间百分比

示例:计算应用 5xx 错误率 ≤ 1% 的可用性

promql 复制代码
avg_over_time(
  (
    (sum(rate(http_server_requests_seconds_count{status=~"5.."}[5m]))
     / sum(rate(http_server_requests_seconds_count[5m]))
     * 100)
    <= bool 1
  )[30d:1m]
) * 100

2、基于请求量的可用性(更精确)

promql 复制代码
# 成功请求数 / 总请求数
sum(
  increase(http_server_requests_seconds_count{status!~"5.."}[30d])
)
/
sum(
  increase(http_server_requests_seconds_count[30d])
)
* 100

3、多维度 SLI 汇总视图

在 Grafana 中可以用 Stat 面板展示多个 SLI 的当前状态,用 Threshold 设置颜色:

SLI 达标值 警告值 严重值
CPU 利用率 ≤ 80% > 80% > 90%
内存可用率 ≥ 20% < 20% < 10%
5xx 错误率 ≤ 1% > 1% > 5%
P99 延迟 ≤ 500ms > 500ms > 1000ms

四、监控报告的三种场景与模板

监控报告的价值在于把数据翻译为结论,而非罗列曲线。以下给出三种常见场景的报告模板。

场景一:日报 / 值班交接报告

报告用途:值班人员交接时快速了解夜间发生了哪些事件、当前各系统健康状况。

报告结构:

erlang 复制代码
┌──────────────────────────────────────────┐
│         【日报】监控运行报告               │
│  日期:2026-09-21 18:00 ~ 2026-09-22 18:00│
├──────────────────────────────────────────┤
│  一、整体健康状态(红/黄/绿)             │
│     ● 主机层:🟢 全部正常                │
│     ● 应用层:🟡 1 条警告(见下)        │
│     ● 依赖层:🟢 全部正常                │
├──────────────────────────────────────────┤
│  二、告警摘要                            │
│  告警数:8 条(Critical:1 / Warning:7)   │
│  已恢复:7 条 / 未恢复:1 条              │
│                                          │
│  #1 [Critical] 10.0.2.15 CPU > 85%       │
│     触发:03:12 → 恢复:03:45            │
│     根因:夜间离线任务高峰期               │
│                                          │
│  #2 [Warning] nodb 应用 5xx > 1%         │
│     触发:03:15 → 恢复:03:50            │
│     关联:CPU 过载导致的超时             │
├──────────────────────────────────────────┤
│  三、关键 SLI 当日表现                    │
│  · 主机 CPU 可用性:99.97%  ✅            │
│  · 应用 5xx 错误率:0.23% ✅             │
│  · 应用 P99 延迟:320ms  ✅              │
│  · 外部探测可用率:100%  ✅              │
├──────────────────────────────────────────┤
│  四、Top 5 资源消耗                       │
│  · CPU 最高:nodb(65%)                  │
│  · 内存最高:nodb(72%)                  │
│  · 磁盘 I/O 最高:/data 分区(30%)      │
├──────────────────────────────────────────┤
│  五、待办事项                            │
│  □ 确认离线任务是否需要优化               │
├──────────────────────────────────────────┤
│  编写人:xxx    审核人:xxx               │
└──────────────────────────────────────────┘

对应的 PromQL 填充模板:

promql 复制代码
# 当日主机 CPU 可用性(过去 24h)
avg_over_time(
  (
    100 - (avg by(instance) (rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100)
    <= bool 80
  )[1d:1m]
) * 100

# 当日告警总数(从 Alertmanager 拉取)
# 如果有 Alertmanager 指标暴露,如 alertmanager_alerts{state="firing"}
count(alertmanager_alerts{state="firing"})

# 当日 Top CPU 消耗实例
topk(5, avg by(instance) (rate(node_cpu_seconds_total{mode!="idle"}[1d])))

场景二:月度 SLO 报告

报告用途:向技术和业务方汇报本月服务等级达标情况,用于复盘和改进规划。

报告结构:

erlang 复制代码
┌──────────────────────────────────────────┐
│        【月报】SLO 达标报告               │
│  周期:2026-09-01 ~ 2026-09-30           │
├──────────────────────────────────────────┤
│  一、SLO 达标总览                         │
│                                          │
│  SLI           目标        实际    状态   │
│  ─────────────────────────────────────  │
│  主机可用性    ≥ 99.9%    99.97%   ✅   │
│  应用错误率    ≤ 1%       0.31%    ✅   │
│  P99 延迟      ≤ 500ms    412ms    ✅   │
│  探测可用率    ≥ 99.5%    99.82%   ✅   │
│                                          │
│  ✅ 本月四项 SLO 全部达标                  │
├──────────────────────────────────────────┤
│  二、未达标事件分析(如有)               │
│  (本节本月无内容)                       │
├──────────────────────────────────────────┤
│  三、可用性趋势图(P99 延迟逐日)         │
│  → 参见 Grafana Dashboard 附图           │
│  说明:本月 P99 延迟中位 380ms,           │
│  峰值出现在 9/14 19:00(620ms),          │
│  其余时间保持在 400ms 以下。              │
├──────────────────────────────────────────┤
│  四、错误预算消耗                          │
│  错误预算 = (1 - SLO) × 总分钟数          │
│                                          │
│  例:P99 延迟 SLO=99.9%,                  │
│  当月总分钟数=43200                        │
│  错误预算 = (1 - 0.999) × 43200 = 43.2 分钟│
│  本月实际消耗 = 12 分钟(P99>500ms 时长)   │
│  剩余错误预算 = 31.2 分钟(72.2%)         │
├──────────────────────────────────────────┤
│  五、改进建议                            │
│  1. 9/14 延迟峰值待排查,考虑扩容         │
│  2. 建议下月收紧 P99 SLO 至 400ms        │
├──────────────────────────────────────────┤
│  编写人:xxx    审核人:xxx               │
└──────────────────────────────────────────┘

对应的 PromQL 填充模板:

promql 复制代码
# 月度主机可用性
avg_over_time(
  (
    100 - (avg by(instance) (rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100)
    <= bool 80
  )[30d:1m]
) * 100

# 月度 P99 延迟 P50 值(中位趋势)
quantile_over_time(0.5,
  avg_over_time(
    histogram_quantile(0.99, sum(rate(http_server_requests_seconds_bucket[5m])) by (le))
  [1d])[30d:1d]
)

# 月度 P99 延迟超出 500ms 的时间占比(错误预算消耗)
avg_over_time(
  (
    histogram_quantile(0.99, sum(rate(http_server_requests_seconds_bucket[5m])) by (le))
    > bool 0.5
  )[30d:1m]
) * 100

场景三:故障复盘报告

报告用途:重大故障后的根因分析与改进跟踪。

报告结构:

ini 复制代码
┌──────────────────────────────────────────┐
│       【复盘报告】故障分析                │
│  故障编号:INC-20260922-001              │
├──────────────────────────────────────────┤
│  一、故障概要                            │
│  时间:2026-09-22 14:23 ~ 14:58(35min) │
│  影响:nodb 服务 5xx 错误率飙升至 12%     │
│  影响范围:3 个下游服务出现级联超时        │
│  等级:P1                                │
├──────────────────────────────────────────┤
│  二、时间线                              │
│  14:23 告警触发:nodb 5xx 错误率 > 5%   │
│  14:25 值班确认                          │
│  14:28 定位到数据库连接池耗尽             │
│  14:35 紧急扩容连接池                     │
│  14:45 错误率开始下降                     │
│  14:58 确认恢复                          │
├──────────────────────────────────────────┤
│  三、根因分析                            │
│  · 直接原因:数据库慢查询导致连接长期占用  │
│  · 间接原因:连接池 max=20 过低           │
│  · 深层原因:慢查询未纳入日常巡检          │
├──────────────────────────────────────────┤
│  四、数据佐证(附 PromQL / 截图)         │
│  1. 错误率飙升曲线                        │
│     sum(rate(http_server_requests_seconds_count{  │
│       job="nodb", status=~"5.."}[1m]))    │
│  2. 连接池使用率                          │
│     hikaricp_connections_active /         │
│     hikaricp_connections_max * 100        │
│  3. P99 延迟飙升                          │
│     histogram_quantile(0.99, ...)         │
├──────────────────────────────────────────┤
│  五、改进措施                            │
│  #  措施                负责人  截止日期  │
│  1  增加连接池监控告警    xxx    9/25    │
│  2  慢查询巡检纳入月报    xxx    9/30    │
│  3  扩容连接池上限        xxx    9/23    │
├──────────────────────────────────────────┤
│  编写人:xxx    审核人:xxx              │
└──────────────────────────────────────────┘

五、数据分析方法:从数据到洞察

报告不止是堆数字,关键是从数据中提炼出可行动的信息。以下是三种常用的数据分析方法。

1、基线对比法(同比/环比)

对比当前周期与历史周期的指标变化,快速识别异常波动。

对比类型 窗口 公式(PromQL) 用途
日环比 当前 1h vs 昨日同时段 avg_over_time(QPS[1h]) / avg_overetime(QPS[1h] offset 1d) 检测小时级波动
周同比 今天 vs 上周同日 avg_over_time(QPS[1h]) / avg_over_time(QPS[1h] offset 1w) 排除周末效应
基线偏离 与过去 7 天中位对比 avg_over_time(QPS[10m]) / quantile_over_time(0.5, avg_over_time(QPS[10m])[7d:10m]) 检测异常偏离

实战示例:

bash 复制代码
# 应用 QPS 日环比波动(> 50% 触发告警)
(
  avg_over_time(sum(rate(http_server_requests_seconds_count[5m]))[1h:1m])
  /
  avg_over_time(sum(rate(http_server_requests_seconds_count[5m] offset 1d))[1h:1m])
) * 100

2、趋势分析法

使用 deriv() 或 predict_linear() 对未来趋势进行预测,实现主动预警。

promql 复制代码
# 磁盘可用空间预测(预计满盘时间)
(time() - predict_linear(node_filesystem_avail_bytes{mountpoint="/data"}[7d], 0) / predict_linear(node_filesystem_avail_bytes{mountpoint="/data"}[7d], 86400*7))
promql 复制代码
# 内存可用量下降趋势(MB/h,连续 3h 下降 > 200MB/h 提示内存泄漏)
- deriv(node_memory_MemAvailable_bytes[3h]) / 1024 / 1024

3、关联分析法

通过多指标同屏对比,建立因果链条。

下钻链路:

css 复制代码
总览 → 发现 5xx 错误率上升
   → 看延迟:P99 同步上升
       → 看 CPU:iowait 升高
           → 看磁盘 I/O 延迟:读写延迟 > 20ms
               → 结论:磁盘 I/O 瓶颈导致应用响应变慢

在报告中,用时间对齐的多图对比呈现这种链路:

复制代码
┌───── 时间轴 ──────────────────────────┐
│  图1:5xx 错误率 curve                │ ▲ 第一个动
│  图2:P99 延迟 curve                  │ ▲ 同步上升
│  图3:CPU iowait curve                │ ▲ 同类波动
│  图4:磁盘读写延迟 curve              │ ▲ 根因
└───────────────────────────────────────┘

4、异常检测的「三个漏斗」

在报告中对告警做三层收敛,避免「一页纸全是告警,一个都看不进去」:

markdown 复制代码
原始事件 → 第1层:Prometheus 规则匹配
    ↓
告警条目 → 第2层:Alertmanager 分组+抑制
    ↓
  通知   → 第3层:人工降噪(静默、合并、关闭)
    ↓
有效告警 → 写入复盘报告

报告中只呈现第 3 层过滤后的有效告警,附上抑制和被静默的数量说明。例如:

本月产生告警 83 条 ,经 Alertmanager 分组合并后发出 31 条通知 ,其中 12 条 经人工确认为有效告警并纳入复盘,19 条为已知模式或重复通知。


六、自动化报告生成

手动撰写报告容易遗漏数据、难以保持格式一致。建议将报告模板化,并通过 Grafana 的 Reporting 功能或脚本定时生成。

1、Grafana Reporting(企业版功能)

Grafana 企业版支持定时导出 PDF 报告。开源版可借助 Image Renderer 插件手动导出:

bash 复制代码
# 安装 Image Renderer(见第1篇引用)
docker run -d --name=grafana-renderer \
  --network my-bridge \
  -e HTTP_HOST=renderer \
  -e ENABLE_METRICS=true \
  grafana/grafana-image-renderer:latest

2、基于 Python 的定制报告脚本

用 Python + Prometheus API 拉取数据,生成 Markdown / HTML 报告并定时推送。

伪代码架构:

python 复制代码
import requests
import datetime

PROMETHEUS_URL = "http://localhost:9090"

def query_promql(promql):
    resp = requests.get(f"{PROMETHEUS_URL}/api/v1/query", params={"query": promql})
    return resp.json()["data"]["result"]

def generate_daily_report():
    # 拉取 SLI 数据
    cpu_avail = query_promql("avg_over_time((...CPU可用性...)[1d:1m]) * 100")
    error_rate = query_promql("avg_over_time((...5xx错误率...)[1d:1m]) * 100")

    # 填充模板
    report = f"""
# 日报 {datetime.date.today()}

## SLI 状态
- CPU 可用性:{cpu_avail[0]["value"][1]}%
- 5xx 错误率:{error_rate[0]["value"][1]}%
    """
    return report

if __name__ == "__main__":
    print(generate_daily_report())

3、利用 Alertmanager API 获取告警统计

bash 复制代码
# 获取当前 firing 的告警列表
curl -s http://localhost:9093/api/v2/alerts?state=active | jq '.'

七、SLO 持续治理:错误预算与改进循环

1、什么是错误预算

错误预算 = (1 - SLO) × 总时间,量化了「系统允许出多长时间的错」。该预算是决策的依据:

错误预算状态 应对策略
富裕(> 50%) 可正常发版,继续观察
紧张(20%~50%) 控制变更节奏,优先稳定性优化
耗尽(< 20%) 冻结新功能上线,全力修复
已超支(0%) 启动故障复盘

2、在 Grafana 中展示错误预算

promql 复制代码
# 错误预算消耗率(以 P99 延迟为例)
# SLO 目标:P99 ≤ 500ms,对应 99.9% 可用性
# 当月总分钟数
(avg_over_time(
  (histogram_quantile(0.99, sum(rate(http_server_requests_seconds_bucket[5m])) by (le)) > bool 0.5)[30d:1m]
) * 100)

# 剩余错误预算
100 - (上一条查询结果)

在 Grafana 中用 Gauge 面板展示,绿色 >50%、黄色 20-50%、红色 <20%。

3、SLO 的季度复盘节奏

markdown 复制代码
每季度末
    ↓
回顾本季度所有 SLI 实际值
    ↓
与上一季度对比趋势
    ↓
判断是否需要收严或放宽 SLO
    ↓
更新 SLO 配置,纳入下一季度报告模板

八、小结

本篇核心要点:

  1. SLI 从 USE → RED → 业务逐层推导,不硬凑指标;
  2. SLO 基于历史数据的 P95 设定,先宽松再收严,逐步逼近真实能力边界;
  3. 三份报告模板覆盖日报、月报、故障复盘,每份报告都有对应的 PromQL 和 Grafana 面板方案;
  4. 数据分析四法------基线对比、趋势预测、关联分析、三层漏斗,提供从数据到洞察的方法路径;
  5. 错误预算是连接监控与决策的关键桥梁,让团队从「追着告警跑」变为「理性管控风险」。

​

相关推荐
鸿观工坊3 天前
第09篇 告警体系设计:Alertmanager 路由与告警治理
监控
做萤石二次开发的哈哈6 天前
视频解码器怎么对接?解码上墙、电视墙开窗与场景切换的ISAPI接入实战
人工智能·物联网·监控·视频编解码·大屏端·萤石开放平台·蓝海aiot一站式工作台
lisanmengmeng7 天前
nagios 图形化监控部署
监控·nagios
鸿观工坊7 天前
第5篇 Nginx 监控:流量、连接与性能指标
监控
鸿观工坊7 天前
第4篇 Spring Boot 应用监控:Micrometer 与 JVM 指标
监控
鸿观工坊7 天前
第1篇 Prometheus 监控体系全景与基础环境搭建
监控
苏渡苇15 天前
Spring Insight 里如何把 Span 画成瀑布时间线
后端·spring·spring cloud·springboot·监控·apm
行百里er15 天前
轻量级 Spring 监测工具——Spring Insight 发布了
spring boot·后端·监控
宋均浩16 天前
告警规则 243 砍到 27:Prometheus 降噪实战,日均打扰 47 次 → 3 次
云原生·监控·devops