概述

SRE 有一句名言:“系统一定会出故障,区别在于你是被它叫醒的还是主动管理它的。” 事件管理不是"出了事再处理",而是一套从预防、检测、响应到学习的完整工程体系。

从事件分级、On-Call 轮值、事故响应流程、Postmortem 文化、告警治理五个方面,详细梳理如何构建一个可落地的 On-Call 体系。

关于事件管理的系统方法论,可参考 Google SRE Book - Managing IncidentsGoogle SRE Book - Postmortem Culture

一、事件分级标准

没有分级的事件管理等于没有管理——所有事件都按紧急处理,结果就是没有真正的紧急。合理的事件分级是 On-Call 体系的基石。

P0-P4 事件定义

级别定义影响范围响应时效示例
P0生产服务完全不可用全量用户受影响立即响应,<5min核心服务宕机、数据库不可用
P1核心功能严重降级大量用户受影响<15min支付失败率飙升、API 错误率 >10%
P2部分功能降级部分用户受影响<30min某区域延迟劣化、非核心服务异常
P3潜在风险暂无直接影响<2h(工作时间)磁盘水位 >80%、单节点故障
P4优化建议无影响下一工作日告警阈值优化、文档补充

分级原则

分级的本质是资源调度优先级——让有限的人力优先处理影响最大的问题:

  1. 以用户影响为准,而非技术指标:CPU 99% 是 P3,但如果导致用户请求超时就是 P1
  2. 明确定义,避免模糊:“大量用户"是 30% 还是 50%?需要量化
  3. 可自动判定:理想状态下,事件级别应该能通过告警规则自动确定
# 告警分级规则示例
groups:
  - name: incident-grading
    rules:
      # P0: 核心服务完全不可用
      - alert: P0ServiceDown
        expr: up{job="critical-service"} == 0
        for: 1m
        labels:
          severity: P0
          page: true           # 触发电话告警
          escalation: true     # 自动升级到主管
        annotations:
          summary: "核心服务不可用"
          runbook: "https://wiki/incident/p0-service-down"

      # P1: 错误率超过 SLO
      - alert: P1HighErrorRate
        expr: |
          sum(rate(http_requests_total{status=~"5.."}[5m]))
          /
          sum(rate(http_requests_total[5m])) > 0.05          
        for: 2m
        labels:
          severity: P1
          page: true
        annotations:
          summary: "错误率超过 5%"
          runbook: "https://wiki/incident/high-error-rate"

      # P2: 延迟劣化
      - alert: P2LatencyDegraded
        expr: |
          histogram_quantile(0.99,
            sum(rate(http_request_duration_seconds_bucket[5m])) by (le)
          ) > 0.5          
        for: 5m
        labels:
          severity: P2
          page: false           # 仅通知,不电话呼叫
          slack: "#alerts-prod"

二、On-Call 轮值机制设计

基本原则

On-Call 机制的核心目标是:让正确的人在正确的时间收到正确的告警。设计时需遵循以下原则:

  1. 可持续:On-Call 不是惩罚,不能让个别人长期超负荷
  2. 主备制:始终有 Primary + Secondary 两层保障
  3. 明确边界:响应时间、处理范围、升级路径必须清晰
  4. 尊重生活:夜间告警必须有补偿,避免 burnout

轮值排班设计

主备轮值制

# On-Call 排班配置示例
oncall_schedule:
  timezone: "Asia/Shanghai"

  # Primary:第一响应人
  primary:
    rotation: weekly          # 每周轮换
    members:
      - name: "张三"
        phone: "+86-138xxxx0001"
      - name: "李四"
        phone: "+86-138xxxx0002"
      - name: "王五"
        phone: "+86-138xxxx0003"
    handoff_time: "10:00"     # 每周一 10:00 交接
    response_sla:
      P0: "5min"
      P1: "15min"
      P2: "30min"

  # Secondary:升级兜底人
  secondary:
    rotation: weekly
    members:
      - name: "赵六"
        phone: "+86-138xxxx0004"
      - name: "孙七"
        phone: "+86-138xxxx0005"
    escalation_after: "10min"  # Primary 10min 未响应,升级到 Secondary

  # Manager Escalation
  manager:
    escalation_after: "30min"  # Secondary 30min 未响应,升级到 Manager
    members:
      - name: "周经理"
        phone: "+86-138xxxx0006"

  # 假期覆盖
  holidays:
    - date: "2026-01-01"
      primary: "张三"
      secondary: "李四"

Follow-the-Sun 模式

对于全球化团队,可以采用 Follow-the-Sun 模式,让 On-Call 总是在白天进行:

时区        08:00 - 20:00 覆盖
──────────────────────────────────
北京 (UTC+8)   |  轮值 A  |
伦敦 (UTC+0)            |  轮值 B  |
旧金山 (UTC-8)                    |  轮值 C  |
──────────────────────────────────
            全天覆盖,无夜间告警

排班表示例

Week of 2026-07-06 ~ 2026-07-12

┌──────────┬──────────┬──────────┐
│   日期   │ Primary  │ Secondary│
├──────────┼──────────┼──────────┤
│ 周一 7/6 │   张三   │   赵六   │
│ 周二 7/7 │   张三   │   赵六   │
│ 周三 7/8 │   张三   │   赵六   │
│ 周四 7/9 │   李四   │   赵六   │
│ 周五 7/10│   李四   │   孙七   │
│ 周六 7/11│   李四   │   孙七   │
│ 周日 7/12│   王五   │   孙七   │
└──────────┴──────────┴──────────┘

交接时间:每日 10:00 (Asia/Shanghai)
响应工具:PagerDuty / OpsGenie / 自研告警平台

On-Call 健康度指标

On-Call 机制的可持续性需要量化监控:

指标健康范围预警阈值
每周告警次数<10 次>20 次
夜间告警次数<2 次/周>5 次/周
平均响应时间<5min (P0)>15min
平均处理时间<1h>4h
On-Call 工时占比<25%>50%

如果告警次数超过预警阈值,说明系统存在系统性问题,需要立即治理而非加人。

三、事故响应流程

Google 提出了结构化的事故响应模型——SEV 模型,将事故响应分为五个阶段:

Detection → Triage → Mitigation → Resolution → Postmortem
  检测      分级      缓解          解决          复盘

1. Detection(检测)

检测是事故响应的第一步,也是最重要的环节。检测手段包括:

  • 告警系统:基于 SLO 的主动告警(而非被动阈值)
  • 用户反馈:客服工单、社区舆情
  • 主动巡检:定期健康检查、混沌工程

检测的黄金指标:

指标目标
MTTD(平均检测时间)<1min(自动检测) / <15min(人工发现)
误报率<5%
检出率>95%

2. Triage(分级)

确认告警后,第一件事不是修故障,而是分级:

  1. 确认影响范围:哪些用户受影响?哪些功能受影响?
  2. 确定事件级别:根据 P0-P4 分级标准定级
  3. 决定响应模式:单人处理 / 组建虚拟团队 / 升级

3. Mitigation(缓解)

缓解的核心原则是:先止血,后治病

  • 不是找根因:根因分析是 Postmortem 阶段的事
  • 是恢复服务:回滚、切流、扩容、降级,任何能让用户恢复的手段

常见的缓解手段优先级:

1. 回滚(最快)    → 部署有问题
2. 切流(次快)    → 某区域/某节点有问题
3. 扩容(较慢)    → 容量不足
4. 降级(兜底)    → 关闭非核心功能保核心
5. 重启(最后)    → 临时恢复,根因待查

4. Resolution(解决)

缓解之后是根本解决——修复根因、恢复全量服务、验证系统状态:

# 事故解决检查清单
- [ ] 根因已确认并修复
- [ ] 受影响服务已恢复到 SLO 范围
- [ ] 所有节点状态正常
- [ ] 监控告警已恢复正常
- [ ] 受影响用户已确认恢复
- [ ] 事后改进项已记录

5. Postmortem(复盘)

详见下一节。

四、Postmortem 文化

Blameless 原则

Postmortem 的第一条原则是 Blameless——无指责。

“The purpose of a postmortem is to learn from the incident, not to assign blame.” 来源:Google SRE Book - Postmortem Culture

无指责不是不追究责任,而是追究系统责任而非个人责任。假设同样的错误换了任何人都会犯,那问题就出在系统设计上,而不是人身上。

Postmortem 模板

以下是一个生产级的 Postmortem 模板:

# Postmortem: [事件标题]

## 基本信息

| 字段 | 内容 |
|------|------|
| 事件级别 | P0 |
| 发生时间 | 2026-07-08 14:32 UTC+8 |
| 恢复时间 | 2026-07-08 15:48 UTC+8 |
| 持续时长 | 1h16min |
| 影响范围 | 全站用户无法下单,约 12 万用户受影响 |
| 撰写人 | 张三 |
| 审核人 | 周经理 |
| 状态 | 已完成 |

## 事件摘要

7 月 8 日 14:32,订单服务因数据库连接池耗尽导致全站无法下单。
故障持续 76 分钟,影响约 12 万用户,预估损失订单金额约 ¥230 万。
根因为一次配置变更未经过灰度发布直接全量推送,导致连接池参数错误。

## 时间线

| 时间 | 事件 |
|------|------|
| 14:32 | 告警系统触发 P0 告警:订单服务错误率 100% |
| 14:35 | On-Call 工程师张三确认告警,开始排查 |
| 14:38 | 确认为数据库连接池耗尽,大量请求超时 |
| 14:42 | 发现 14:25 有一笔配置变更(连接池上限从 200 改为 50)|
| 14:45 | 执行回滚,恢复连接池配置 |
| 14:52 | 订单服务开始恢复,错误率下降 |
| 15:10 | 错误率恢复至 SLO 范围 |
| 15:48 | 全量验证完成,事件关闭 |

## 根因分析

### 直接原因
配置变更将数据库连接池上限从 200 误改为 50,导致高并发下连接池耗尽。

### 深层原因
1. 变更未经灰度发布,直接全量推送
2. 配置变更缺少自动化校验(连接池下限不应低于 100)
3. 变更审批流程未覆盖配置类变更
4. 缺少配置变更的回滚预案

## 影响评估

- 受影响用户:约 12 万
- 业务影响:损失订单约 ¥230 万
- 声誉影响:社交媒体投诉 47 条
- SLI 违约:订单可用性 SLO (99.9%) 本月错误预算消耗 100%

## 改进项(Action Items)

| 编号 | 改进项 | 负责人 | 截止日期 | 优先级 |
|------|--------|--------|---------|--------|
| #1 | 配置变更强制灰度发布 | 李四 | 2026-07-15 | P0 |
| #2 | 连接池参数自动化校验(下限不低于 100) | 王五 | 2026-07-20 | P0 |
| #3 | 配置变更纳入审批流程 | 赵六 | 2026-07-25 | P1 |
| #4 | 订单服务增加连接池水位告警 | 张三 | 2026-07-12 | P1 |
| #5 | 制定配置变更回滚 SOP | 孙七 | 2026-07-18 | P2 |

## 经验教训

### 做得好的
- 告警检测及时(MTTD < 3min
- On-Call 响应迅速确认 + 排查 < 10min
- 回滚决策果断没有过度分析根因

### 做得不好的
- 配置变更缺少灰度机制
- 连接池参数缺少合理下限保护
- 配置类变更游离于审批流程之外

## 附注
- 相关告警记录: alert-20260708-1432
- 相关变更记录: change-20260708-1425
- 相关监控图表: grafana dashboard "订单服务概览"

Postmortem 的执行要点

  1. 及时性:事件解决后 48 小时内 完成复盘,趁记忆清晰
  2. 参与度:所有相关人员参与,包括开发、运维、产品
  3. 可追踪:每个 Action Item 必须有负责人和截止日期
  4. 可共享:Postmortem 文档全员可见,组织级知识沉淀
  5. 闭环:定期回顾 Action Item 完成情况,确保落地

五、告警疲劳治理

告警疲劳的危害

告警疲劳(Alert Fatigue)是 On-Call 的头号杀手。当告警数量超过人的处理能力时,On-Call 工程师会开始忽略告警——这正是最危险的时刻。很多重大事故的根因,都包含"告警被忽略"这个环节。

识别告警疲劳

# 每周告警总量趋势
sum by (week) (rate(ALERTS_FIRED[7d]))

# 告警噪声比(误报率 = 已触发但未确认处理的告警比例)
sum(ALERTS_FIRED{alertstate="firing"}) / sum(ALERTS_FIRED) * 100

# 告警响应时间趋势(上升趋势说明告警被忽略)
avg_over_time(ALERTS_ACK_TIME[7d])

告警分级策略

不是所有告警都需要叫醒人。建立三层告警分级:

层级通知方式触发条件示例
Page电话 + 短信影响 SLO 的事故服务不可用、错误率超阈值
Ticket工单系统需处理但不紧急磁盘水位 80%、单节点故障
InfoSlack/钉钉仅需知晓部署完成、扩容触发
# 告警路由配置示例(Alertmanager)
route:
  receiver: 'default'
  group_by: ['alertname', 'cluster']
  group_wait: 10s          # 首次告警等待时间
  group_interval: 30s     # 同组告警间隔
  repeat_interval: 4h     # 重复通知间隔

  routes:
    # P0/P1:立即电话通知
    - matchers: ['severity=~"P0|P1"']
      receiver: 'pagerduty-critical'
      group_wait: 0s
      repeat_interval: 30m

    # P2:工作时间通知
    - matchers: ['severity="P2"']
      receiver: 'slack-alerts'
      group_wait: 30s
      repeat_interval: 2h
      active_time_intervals:
        - business-hours

    # P3/P4:仅记录
    - matchers: ['severity=~"P3|P4"']
      receiver: 'null'

receivers:
  - name: 'pagerduty-critical'
    pagerduty_configs:
      - routing_key: '<key>'
        severity: critical
        send_resolved: true

  - name: 'slack-alerts'
    slack_configs:
      - api_url: '<webhook-url>'
        channel: '#alerts-prod'
        send_resolved: true

  - name: 'null'

time_intervals:
  - name: business-hours
    time_intervals:
      - weekdays: ['monday:friday']
        times:
          - { start_time: '09:00', end_time: '18:00' }

降噪策略

1. 告警聚合

同一根因触发的多个告警应聚合为一个:

# Alertmanager 分组配置
route:
  group_by: ['cluster', 'service']  # 按 cluster + service 分组
  group_wait: 10s                     # 等待 10s 收集同组告警
  group_interval: 30s

2. 抑制规则

当高级别告警触发时,抑制低级别告警:

# Alertmanager 抑制规则
inhibit_rules:
  # 当 P0 服务不可用时,抑制该服务的 P2 延迟告警
  - source_matchers: ['severity="P0"', 'alertname="ServiceDown"']
    target_matchers: ['severity="P2"', 'alertname="HighLatency"']
    equal: ['service']

  # 当节点宕机时,抑制该节点上的所有应用告警
  - source_matchers: ['alertname="NodeDown"']
    target_matchers: ['severity=~"P2|P3"']
    equal: ['node']

3. 告警 SLO

给告警本身设定 SLO,定期审查:

# 告警质量 SLO
alert_quality_slo:
  precision: 0.95        # 95% 的告警是真实问题(误报率 < 5%)
  recall: 0.95           # 95% 的真实故障被告警检出
  weekly_volume: 20      # 每周告警总量不超过 20 条
  ack_time_p90: 5m       # 90% 的 P0/P1 告警在 5 分钟内确认

4. 定期告警审计

#!/bin/bash
# 每月告警审计脚本:找出噪声最大的告警规则
# 适用于 Prometheus + Alertmanager

ALERTMANAGER_API="http://alertmanager:9093/api/v2"

echo "=== 月度告警审计报告 ==="
echo "统计周期: $(date -d '30 days ago' +%Y-%m-%d) ~ $(date +%Y-%m-%d)"
echo ""

# Top 10 噪声告警(触发次数最多但未触发升级的告警)
echo "=== Top 10 噪声告警 ==="
curl -s "$ALERTMANAGER_API/alerts" | \
  jq -r '.[] | .labels.alertname' | \
  sort | uniq -c | sort -rn | head -10

echo ""
echo "=== 从未确认的告警(疑似误报) ==="
curl -s "$ALERTMANAGER_API/alerts?active=true" | \
  jq -r '.[] | select(.status.state != "suppressed") | .labels.alertname' | \
  sort | uniq -c | sort -rn | head -10

echo ""
echo "=== 建议处理 ==="
echo "1. 触发次数 >50 且无需升级的告警 → 考虑降噪或降级为 Ticket"
echo "2. 从未确认的告警 → 审查是否为误报,考虑删除"
echo "3. 同一时间段集中触发的告警 → 考虑聚合或加抑制规则"

总结

事件管理与 On-Call 体系是 SRE 的核心实践,其设计目标是:

维度设计目标
事件分级量化影响,合理分配响应资源
On-Call 轮值可持续、有主备、尊重个人生活
事故响应先止血后治病,结构化流程
PostmortemBlameless,聚焦系统改进
告警治理精准、可行动、不疲劳

核心原则:On-Call 的终极目标不是"更快地救火”,而是"让火越来越少"。每一次故障都是改进系统的机会,每一次 Postmortem 都应该产出减少未来故障的 Action Item。当你的告警从每周 50 条降到每周 5 条,而且每一条都值得响应时,你的 On-Call 体系才算是真正成熟的。

关于 SRE 的度量体系基础——SLI/SLO 与错误预算,可以参考上一篇文章

参考资料与致谢

本文在撰写过程中参考了以下资料,感谢原作者的贡献:

  1. Google SRE Book - Managing Incidents — Google SRE 团队,参考了Google SRE Book - Managing Incidents相关内容
  2. Google SRE Book - Postmortem Culture — Google SRE 团队,参考了Google SRE Book - Postmortem Culture相关内容