经过前几篇的逐步建设,监控体系已具备完整的采集、展示与告警能力。现在还缺最后一环:如何把监控数据翻译成可对外沟通的报告。
故障处理完毕,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 不是拍出来的,而是基于历史数据 + 业务容忍度计算出来的。
三步法:
- 回顾历史:拉取过去 30 天的 SLI 数据,观察 P50 / P95 / P99 分布;
- 设定宽松目标:以 P95 值为基准,取整作为 initial SLO;
- 迭代收严:稳定运行 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 配置,纳入下一季度报告模板
八、小结
本篇核心要点:
- SLI 从 USE → RED → 业务逐层推导,不硬凑指标;
- SLO 基于历史数据的 P95 设定,先宽松再收严,逐步逼近真实能力边界;
- 三份报告模板覆盖日报、月报、故障复盘,每份报告都有对应的 PromQL 和 Grafana 面板方案;
- 数据分析四法------基线对比、趋势预测、关联分析、三层漏斗,提供从数据到洞察的方法路径;
- 错误预算是连接监控与决策的关键桥梁,让团队从「追着告警跑」变为「理性管控风险」。