2026-04-13 · architecture
【系统架构设计】告警策略:如何避免"狼来了"
大多数团队的告警系统都在制造噪声而不是传递信号。阈值告警看似直观,实则产生大量误报和漏报,值班工程师在凌晨三点被叫醒,却发现只是一次无害的毛刺。本文从告警疲劳的工业数据出发,拆解基于 SLO 的多窗口燃烧率告警算法,深入 Alertmanager 的路由、抑制与分组机制,结合 PagerDuty 的告警疲劳研究和真实工程案例,给出一套可落地的告警策略设计方法。



























你的系统上线了,可用性”挺高的”。具体多高?“反正没怎么出过事。”上个月有没有故障?“好像有一次,持续了几分钟,影响不大。”影响了多少用户?“不太清楚。”
这段对话在无数团队中反复出现。系统稳定性的管理停留在”感觉”层面——感觉还行,感觉没事,感觉问题不大。当产品经理要求加一个高风险的新功能时,工程师说”可能影响稳定性”,产品经理说”上次你也这么说,结果没事”。争论的双方都没有数据支撑。
Google 的 SRE(Site Reliability Engineering,站点可靠性工程)团队在 2003 年就开始面对这个问题。他们的解决方案不是”让系统更稳定”——因为无限稳定的成本是无限的——而是给稳定性一个精确的数字,然后围绕这个数字构建决策框架。
这个框架的核心是三个概念:SLI(Service Level Indicator,服务水平指标)、SLO(Service Level Objective,服务水平目标)和 SLA(Service Level Agreement,服务水平协议)。它们不是运维团队的内部指标,而是连接产品、工程、管理层的共同语言。
本文要回答三个问题:
在上一篇中,我们讨论了容灾架构——如何在灾难发生时保障业务连续性。SLO 工程是容灾的另一面:不是应对极端事件,而是管理日常的可靠性水位。
SLI 是对系统行为的一个可量化的度量。它必须是具体的、可测量的、有业务含义的。
好的 SLI 的特征:
常见的 SLI 类型:
| SLI 类型 | 定义 | 适用场景 |
|---|---|---|
| 可用性(Availability) | 成功请求数 / 总请求数 | 几乎所有在线服务 |
| 延迟(Latency) | 请求处理时间的分布(通常取 p50、p99) | 面向用户的 API |
| 吞吐量(Throughput) | 单位时间处理的请求或事件数 | 数据管道、批处理系统 |
| 正确性(Correctness) | 返回正确结果的请求数 / 总请求数 | 搜索、推荐系统 |
| 新鲜度(Freshness) | 数据更新到可查询之间的延迟 | 数据仓库、搜索索引 |
| 持久性(Durability) | 成功存储的数据比例 | 存储系统 |
SLO 是在 SLI 上设定的目标值。它表达的是:我们承诺这个 SLI 在某个时间窗口内达到某个水平。
例如:
SLO 的关键特征:
SLA 是与外部客户签订的法律契约。如果服务未达到 SLA 约定的水平,服务提供方需要承担后果(通常是经济赔偿)。
SLA 通常比 SLO 宽松。例如:
SLO 和 SLA 之间的差距是一个”安全边际”。如果 SLO 和 SLA 设为相同值,那么一旦 SLO 被突破,就立刻触发赔偿——没有任何缓冲。
graph LR
SLI["SLI<br/>指标:我们度量什么"] --> SLO["SLO<br/>目标:我们期望达到什么水平"]
SLO --> SLA["SLA<br/>契约:未达标的后果是什么"]
SLI --> |"成功请求/总请求"| M1["99.95%"]
M1 --> SLO
SLO --> |"安全边际"| SLA
SLA --> |"赔偿条款"| C["客户补偿"]
选择 SLI 是 SLO 工程中最关键的一步。选错了 SLI,后面的所有工作都是在错误的方向上努力。
基于请求的 SLI(Request-based SLI):每个请求独立判断是”好”还是”坏”,然后计算好请求的比例。
可用性 SLI = 状态码非 5xx 的请求数 / 总请求数
适用于请求-响应模式的在线服务(HTTP API、gRPC 服务等)。
基于窗口的 SLI(Window-based SLI):把时间切分为固定长度的窗口(例如每分钟),每个窗口判断是”好”还是”坏”,然后计算好窗口的比例。
可用性 SLI = 数据同步延迟 < 5 秒的分钟数 / 总分钟数
适用于后台服务、数据管道等非请求-响应模式的系统。
同一个 SLI,在不同位置度量,结果可能截然不同。
用户浏览器 → CDN → 负载均衡 → API 网关 → 应用服务 → 数据库
① ② ③ ④ ⑤ ⑥
在位置 ⑤ 度量的延迟可能是 20 毫秒,但用户在位置 ① 感受到的延迟可能是 200 毫秒(加上 DNS 解析、TCP 握手、TLS 协商、网络传输、前端渲染等)。
原则:SLI 应该尽可能靠近用户度量。 如果用户通过 HTTP API 访问服务,那么在负载均衡器或 API 网关处采集的请求日志是最佳的度量点——它既靠近用户端,又在你的控制范围内。
以下指标看起来有用,但作为 SLI 容易误导:
如果 SLO 设为 99.999%(“五个九”),意味着每月只允许 26 秒的不可用时间。这意味着:
如果 SLO 设为 99%,意味着每月允许 7.2 小时的不可用时间。这意味着:
| SLO 可用性 | 年度允许停机时间 | 月度允许停机时间 | 每日允许停机时间 |
|---|---|---|---|
| 99%(两个九) | 3.65 天 | 7.2 小时 | 14.4 分钟 |
| 99.9%(三个九) | 8.76 小时 | 43.8 分钟 | 1.44 分钟 |
| 99.95% | 4.38 小时 | 21.9 分钟 | 43.2 秒 |
| 99.99%(四个九) | 52.56 分钟 | 4.38 分钟 | 8.64 秒 |
| 99.999%(五个九) | 5.26 分钟 | 26.3 秒 | 0.86 秒 |
Error Budget(错误预算)是 Google SRE 最重要的概念之一。它的核心思想是:如果 SLO 不是 100%,那么 SLO 与 100% 之间的差距就是你”允许犯错”的空间。
如果 SLO 是 99.9% 的可用性,每月的 Error Budget 是:
Error Budget = 1 - SLO = 1 - 99.9% = 0.1%
每月总请求数 = 1000 万
允许的失败请求数 = 1000 万 × 0.1% = 1 万
或者按时间计算:
每月总分钟数 = 30 × 24 × 60 = 43200 分钟
允许的不可用分钟数 = 43200 × 0.1% = 43.2 分钟
Error Budget 像一个存款账户。每次故障都会从中”消费”一部分:
当 Error Budget 还有剩余时——团队可以:
当 Error Budget 即将耗尽或已经耗尽时——团队必须:
这就是 Error Budget 的仲裁价值。产品经理说”我要上新功能”,工程师说”系统不稳定不能上”——这个争论没有赢家,因为双方都在用主观判断。但如果引入 Error Budget:
争论的对象从”人的判断”变成了”数据”,这是 SLO 工程最大的组织价值。
flowchart TD
A["每月 Error Budget 重置"] --> B{"当前 Budget 剩余"}
B -->|充足 > 50%| C["正常迭代<br/>功能发布、实验"]
B -->|告警 20%-50%| D["谨慎迭代<br/>增加发布审批<br/>优先修复已知问题"]
B -->|耗尽 < 20%| E["冻结功能发布<br/>全力修复可靠性<br/>事后复盘"]
C --> F["监控 Budget 消耗速率"]
D --> F
E --> G["恢复 Budget 后解冻"]
G --> F
F --> B
传统的可用性告警方式是设定一个固定阈值:如果过去 5 分钟的错误率超过 1%,就触发告警。这种方式有两个严重问题:
Google SRE Workbook 提出的解决方案是多窗口燃烧率告警(Multi-Window, Multi-Burn-Rate Alerting)。
燃烧率是 Error Budget 消耗速率的倍数。
燃烧率 = 实际错误率 / (1 - SLO)
如果 SLO = 99.9%(允许错误率 = 0.1%):
单一时间窗口的燃烧率告警仍然有问题:短窗口(5 分钟)容易误报,长窗口(1 小时)反应慢。解决方案是同时检查两个窗口:
Google SRE Workbook 推荐的配置:
| 告警级别 | 长窗口 | 短窗口 | 燃烧率阈值 | Budget 消耗占比(如果持续) | 响应要求 |
|---|---|---|---|---|---|
| 紧急(Page) | 1 小时 | 5 分钟 | 14.4x | 2%(1 小时内) | 立即响应 |
| 紧急(Page) | 6 小时 | 30 分钟 | 6x | 5%(6 小时内) | 立即响应 |
| 工单(Ticket) | 3 天 | 6 小时 | 1x | 10%(3 天内) | 工作时间处理 |
以下是基于 Prometheus 的多窗口燃烧率告警规则示例:
# 预聚合:计算不同时间窗口的错误率
groups:
- name: slo-error-rate
rules:
# 5 分钟窗口错误率
- record: slo:http_request_error_rate:5m
expr: |
sum(rate(http_requests_total{status=~"5.."}[5m]))
/
sum(rate(http_requests_total[5m]))
# 30 分钟窗口错误率
- record: slo:http_request_error_rate:30m
expr: |
sum(rate(http_requests_total{status=~"5.."}[30m]))
/
sum(rate(http_requests_total[30m]))
# 1 小时窗口错误率
- record: slo:http_request_error_rate:1h
expr: |
sum(rate(http_requests_total{status=~"5.."}[1h]))
/
sum(rate(http_requests_total[1h]))
# 6 小时窗口错误率
- record: slo:http_request_error_rate:6h
expr: |
sum(rate(http_requests_total{status=~"5.."}[6h]))
/
sum(rate(http_requests_total[6h]))
- name: slo-burn-rate-alerts
rules:
# 紧急告警:14.4x 燃烧率,1h/5m 双窗口
- alert: SLOBurnRateCritical
expr: |
slo:http_request_error_rate:1h > (14.4 * 0.001)
and
slo:http_request_error_rate:5m > (14.4 * 0.001)
labels:
severity: critical
annotations:
summary: "SLO burn rate critical: 14.4x"
description: "Error budget will be exhausted in less than 2 hours."
# 紧急告警:6x 燃烧率,6h/30m 双窗口
- alert: SLOBurnRateHigh
expr: |
slo:http_request_error_rate:6h > (6 * 0.001)
and
slo:http_request_error_rate:30m > (6 * 0.001)
labels:
severity: warning
annotations:
summary: "SLO burn rate high: 6x"
description: "Error budget will be exhausted in less than 5 days."SLO Dashboard 需要回答三个核心问题:
┌─────────────────────────────────────────────────┐
│ API 可用性 SLO: 99.9% │
│ ┌───────┐ ┌──────────────────────┐ │
│ │ 🟢 │ │ Budget: 72% 剩余 │ │
│ │ 达标 │ │ ████████████░░░░░ │ │
│ └───────┘ └──────────────────────┘ │
│ │
│ 当前可用性: 99.94% | 月初至今 Error Budget 消耗: 28%│
│ ──────────────────────────────────────────── │
│ Budget 消耗趋势 │
│ 100%| ╱ │
│ | ╱ │
│ | ╱ │
│ | 当前消耗线 ────────╱ │
│ |───────────────── │
│ 0%|________________________________ 时间 │
│ 1日 10日 20日 30日 │
└─────────────────────────────────────────────────┘
SLO 不仅仅是告警的依据。在架构决策中,SLO 提供了一个客观的判断框架。
“缓存能提升性能”是一个模糊的理由。用 SLO 来分析:
但缓存引入了新的故障模式:缓存集群不可用时,所有请求穿透到数据库,可能导致数据库过载。需要评估这个故障模式对可用性 SLO 的影响。
一个单体服务的可用性是 99.95%。如果拆分成 3 个串行调用的微服务,假设每个微服务的可用性也是 99.95%:
串联系统可用性 = 99.95% × 99.95% × 99.95% = 99.85%
可用性从 99.95% 下降到 99.85%,月度停机时间从 21.9 分钟增加到 65.7 分钟。如果 SLO 是 99.9%,这个拆分方案直接导致 SLO 无法达标。
这不意味着不能拆分,但意味着拆分后必须通过其他手段(重试、熔断、降级)来补偿可用性损失。SLO 让这个权衡变得可量化。
在选择数据库时,不同产品的可靠性特征不同:
如果服务的 SLO 是 99.95% 可用性,选项 B 的 99.9% 可用性已经低于服务 SLO,仅数据库一个组件就可能耗尽全部 Error Budget。选项 A 虽然延迟稍高,但可用性更有保障。
SLO 的影响不仅限于技术层面,它改变了组织的行为模式。
最常见的冲突是:产品团队想快速发布新功能,工程团队想花时间修复技术债务和提升稳定性。
没有 SLO 时,这个冲突的解决方式通常是:谁的职级高听谁的,或者谁声音大听谁的。结果是要么稳定性被牺牲(系统频繁出故障),要么迭代速度被牺牲(团队变得过度保守)。
有 SLO 和 Error Budget 后,规则变成:
这个机制的关键是双方在事前就同意了规则。Error Budget 策略应该以书面文件的形式存在,由产品负责人和工程负责人共同签字。
SLO 和 Error Budget 还可以用来量化发布的”安全余量”。
假设每次发布平均消耗 2% 的月度 Error Budget(来自灰度发布期间的少量错误)。如果月度 Error Budget 总量是 43.2 分钟,那么每次发布平均消耗 0.864 分钟。一个月最多可以做 43.2 / 0.864 ≈ 50 次发布。
如果团队想增加发布频率(比如从每周发布改为每日发布),就需要降低每次发布消耗的 Error Budget——这意味着需要投资更好的灰度发布机制、更完善的自动化回滚、更细粒度的特性开关(Feature Flag)。
什么事故值得做事后复盘(Postmortem)?一个实用的标准是:消耗了超过 N% 月度 Error Budget 的事故。
例如:
这比传统的”影响了 X 个用户”或”持续了 X 分钟”的分类标准更有意义,因为 Error Budget 已经综合考虑了影响范围和持续时间。
以下案例来自国内某中型电商平台的公开技术分享,展示了从”没有 SLO”到”SLO 驱动”的转变过程。
团队识别出 3 个核心用户旅程(User Journey):
为每个旅程选择了 SLI:
| 用户旅程 | SLI:可用性 | SLI:延迟 |
|---|---|---|
| 商品浏览 | 搜索 API 成功率 | 搜索 API p99 延迟 |
| 下单支付 | 下单 API 成功率 | 下单 API p99 延迟 |
| 物流查询 | 物流 API 成功率 | 物流 API p99 延迟 |
度量点选择在 API 网关层(Nginx 访问日志),因为这是距离用户最近的可控度量点。
基于过去 90 天的历史数据:
| SLI | 历史实际水平 | 设定的 SLO |
|---|---|---|
| 搜索 API 成功率 | 99.95% | 99.9% |
| 搜索 API p99 延迟 | 420 毫秒 | 500 毫秒 |
| 下单 API 成功率 | 99.92% | 99.9% |
| 下单 API p99 延迟 | 850 毫秒 | 1000 毫秒 |
| 物流 API 成功率 | 99.97% | 99.9% |
| 物流 API p99 延迟 | 200 毫秒 | 300 毫秒 |
SLO 设定略低于历史水平,给发布和实验留出 Error Budget。
产品总监和技术总监共同签署了 Error Budget 策略文件:
运行 6 个月后的数据:
99.99% 听起来比 99.9% “好”,但它意味着 10 倍的成本和 10 倍的运维压力。大多数业务不需要四个九。一个内部管理系统的 SLO 设为 99.99% 是资源浪费。
SLO 是内部目标,SLA 是外部承诺。它们的受众、后果和设定逻辑完全不同。把 SLO 和 SLA 设为同一个值,意味着你没有安全边际——SLO 刚被突破就要赔钱。
很多团队只设了可用性 SLO,忽略了延迟 SLO。一个可用性 99.99% 但 p99 延迟 10 秒的系统,用户体验并不好。可用性和延迟应该成对出现。
SLO 应该是一个活文档(Living Document)。业务变化了(用户量翻倍)、架构变化了(从单体拆成微服务)、用户期望变化了——SLO 都需要相应调整。建议每季度 Review 一次 SLO 的合理性。
你的服务依赖数据库、缓存、消息队列、第三方 API。每个依赖都有自己的可靠性上限。如果你的数据库 SLA 是 99.95%,你的服务 SLO 就不可能比 99.95% 更高(除非你有完善的降级方案)。设定 SLO 时,必须考虑整个依赖链的可靠性。
| 维度 | 没有 SLO | SLO 过于宽松 | SLO 合理 | SLO 过于严格 |
|---|---|---|---|---|
| 稳定性管理 | 靠感觉 | 形同虚设 | 数据驱动 | 过度保守 |
| 产品迭代速度 | 不可预测 | 无约束 | 有节奏 | 接近停滞 |
| 告警噪声 | 高(随意设阈值) | 低(几乎不告警) | 低(有意义的告警) | 极高(频繁告警) |
| 工程师体验 | 疲于救火 | 缺乏挑战 | 专注 | 疲于奔命 |
| 故障复盘 | 凭感觉分级 | 缺乏标准 | Budget 驱动的分级 | 过度复盘 |
| 成本 | 低(无额外投入) | 低 | 中(可观测性投入) | 高(冗余和容灾投入) |
| 组织信任 | 低 | 低 | 高 | 中(团队压力大) |
| 适用阶段 | 早期探索 | 不推荐 | 成长期和成熟期 | 极端关键系统 |
SLO 工程的核心不是设定一个数字,而是围绕这个数字建立一套决策机制:
SLO 不是银弹。它不能阻止故障发生,也不能自动修复 Bug。但它提供了一个共同语言,让产品、工程、管理层在”可靠性应该是多少”这个问题上达成共识。在大多数团队中,这个共识的价值远大于任何单一技术改进。
导航
上一篇:容灾架构:多活与灾备设计
下一篇:容量规划:从拍脑袋到数据驱动
把当前热点继续串成多页阅读,而不是停在单篇消费。
2026-04-13 · architecture
大多数团队的告警系统都在制造噪声而不是传递信号。阈值告警看似直观,实则产生大量误报和漏报,值班工程师在凌晨三点被叫醒,却发现只是一次无害的毛刺。本文从告警疲劳的工业数据出发,拆解基于 SLO 的多窗口燃烧率告警算法,深入 Alertmanager 的路由、抑制与分组机制,结合 PagerDuty 的告警疲劳研究和真实工程案例,给出一套可落地的告警策略设计方法。
2026-04-13 · architecture
凌晨三点的告警响了,你打开笔记本,盯着一堆指标不知道从哪里下手——两小时后发现是配置改错了。这种经历几乎每个 oncall 工程师都有过。本文从 Incident Command System 在 SRE 中的适配讲起,拆解从告警到根因的系统化排查路径,覆盖事件分级、假设驱动调试、事后复盘的无责文化、Google 与 Meta 的 oncall 体系,给出可落地的 Runbook 模板和 Postmortem 模板。
2026-04-22 · architecture / observability
USE 方法论适用于资源,RED 方法论适用于请求,Golden Signals 适用于服务——三套方法论各有其适用对象。本文从 Brendan Gregg、Tom Wilkie、Google SRE 的原始定义出发,构建覆盖资源→服务→业务的完整指标体系,并给出 Prometheus 命名规范、基数治理策略与可抄的指标清单。
2026-04-22 · architecture / observability
从 Metrics、Logs、Traces 到 Profiling、eBPF、OpenTelemetry 与 SLO 治理,面向中国工程团队的可观测性系统化手册。
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。