事故复盘模板:API 故障复盘怎么写,时间线怎么用数据还原
一份能用的事故复盘模板,核心是六个部分:摘要、影响、时间线、根因、促成因素、行动项(下面的模板另加了一节“做得好的地方”)。容易写虚的是其中两块:时间线要用监控数据核对,不能靠回忆拼;上游服务(比如某家 AI 模型 API)出故障时,复盘对象要换成“我们自己的应对”。下面先给可复制的模板,再逐段讲怎么写实。
为什么很多复盘写完没人看?
常见的问题有三个:
- 时间线是事后凭记忆补的,“大约 3 点左右发现异常”,没人能复查。
- 根因写成“上游服务不稳定”,然后就没有下文,行动项是“加强监控”。
- 影响只写“部分用户受影响”,没有请求数、失败率或时长。
复盘的价值在于下一次少花时间。所以每一句都应该能回答:证据在哪?下次能不能更早发现、更快恢复?
中文事故复盘模板(可直接复制)
# 事故复盘:<一句话描述现象>,<日期>
状态:草稿 / 评审中 / 已定稿
负责人:<角色,例如当周值班>
评审时间:<日期>
## 1. 摘要
- 发生了什么(一句话):
- 持续多久(开始 - 恢复,注明时区):
- 影响多大(失败请求数 / 失败率 / 受影响功能):
- 根因类别:自身代码 / 配置 / 基础设施 / 上游依赖 / 网络
- 当前状态:已恢复 / 已缓解待根治
## 2. 影响
| 指标 | 数值 | 数据来源 | 统计窗口 |
|---|---|---|---|
| 失败请求数 | | | |
| 失败率(失败 / 总请求) | | | |
| 受影响功能或接口 | | | |
| 用户可感知时长 | | | |
## 3. 时间线(统一用 UTC,括号内附北京时间)
| 时间 | 事件 | 证据(链接 / 截图 / 日志查询) |
|---|---|---|
| T0 | 第一个异常信号(探测失败、错误率上升) | |
| T1 | 告警触发 | |
| T2 | 有人确认并开始处理 | |
| T3 | 上游状态页首次更新(如适用) | |
| T4 | 采取缓解措施(切换、降级、回滚) | |
| T5 | 指标恢复 | |
| T6 | 官方宣布恢复 / 事件关闭(如适用) | |
关键间隔:发现耗时 T1-T0 = ;响应耗时 T2-T1 = ;缓解耗时 T5-T2 =
## 4. 根因
<直接原因是什么,用证据说话;不确定就写“待确认”和确认方式>
## 5. 促成因素
<哪些条件让影响变大或持续更久:缺少超时、重试放大、单一供应商、告警阈值等>
## 6. 做得好的地方 / 运气成分
-
## 7. 行动项
| 编号 | 行动 | 类型(预防 / 发现 / 缓解) | 负责人 | 截止日期 | 验收标准 |
|---|---|---|---|---|---|
| A1 | | | | | |
几点约定:
- 无责复盘:写“告警阈值设成 30 分钟导致发现晚”(示例),不写“某某没看告警”。复盘对事不对人,否则下一次大家会倾向少写细节。
- 时区统一:状态页、日志、监控平台的时区经常不一样,混写很容易让时间线错位几个小时。
- 每个行动项都要有验收标准:“加强监控”不是行动项,“对上游 API 增加每 5 分钟一次的外部探测,连续 2 次失败告警”(数值按自己的业务定)才是。
时间线怎么用数据还原,而不是靠回忆?
时间线里最有价值的是几个时间差:异常何时开始、你何时知道、官方何时承认、何时真正恢复。这几个点分别来自不同的数据源,需要逐一对上。
第一步:确定 T0,第一个异常信号
T0 优先取外部探测或请求日志里的第一次失败,而不是告警时间。告警通常有聚合窗口和阈值,会比真实开始晚。
如果你有自己的请求日志,可以按分钟统计 5xx 和超时:
# 假设日志是 JSON 行格式,含 ts(ISO 时间)和 status 字段
jq -r 'select(.status >= 500) | .ts[0:16]' app.log | sort | uniq -c | head -20
第一个明显高于基线的分钟就是候选 T0。再往前看 10 分钟,确认之前确实是正常的。
没有自己的探测时,可以参考第三方的外部探测。cdnping 的 API 状态页 从 Cloudflare 边缘每 5 分钟探测一次各官方 API 入口,保留 30 天历史;探测方法写在 数据方法 里。注意 cdnping 从 2026-10-17 才开始采集,更早的事故没有它的探测数据。外部探测的分辨率是 5 分钟,用来核对“那段时间入口是否可达”足够,精确到分钟还得靠你自己的日志。想自己搭探测,可以参考 自己测 AI API 的可用率。
第二步:对上官方状态页的时间
上游状态页给出的“事件创建时间”是 T3,它往往晚于 T0。状态页的事件等级、组件怎么读,可以看 怎么看 OpenAI 状态页和 Claude 状态页。
下面用 cdnping 同步的官方状态页记录做一个整理示例(来源:cdnping 每 15 分钟同步的官方状态页事件,取 2026-10-15 前后几条;分钟数为 cdnping 记录的事件持续时长):
| 服务商 | 事件标题(原文) | 影响级别 | 创建时间(UTC) | 北京时间 | 记录时长 |
|---|---|---|---|---|---|
| Cloudflare | Increased HTTP 502 Errors | minor | 2026-10-15 10:47:18 | 10-15 18:47 | 591 分钟 |
| OpenAI | Widespread feature degradation | major | 2026-10-15 16:29:26 | 10-16 00:29 | 146 分钟 |
| Cloudflare | Network Performance Issues in Eastern North America | major | 2026-10-15 20:48:17 | 10-16 04:48 | 49 分钟 |
| Cloudflare | Increased HTTP 5xx Errors Ashburn | minor | 2026-10-16 13:46:51 | 10-16 21:46 | -43 分钟 |
假设你的服务依赖 OpenAI API,在 10 月 15 日那次事件里受了影响,时间线可以这样写:
T0 2026-10-15 16:2x UTC 自己日志中 5xx 开始上升(填实际分钟)
T3 2026-10-15 16:29 UTC OpenAI 状态页创建事件 "Widespread feature degradation"(major)
T6 约 18:55 UTC 按记录时长 146 分钟推算的事件关闭时间
T5 ? 自己的错误率回到基线(以日志为准,可能早于或晚于 T6)
T5 和 T6 要分开写。官方关闭事件的时间和你的服务恢复的时间不一定一致:可能你的重试队列还在消化积压,也可能你早就切到了备用路径。
第三步:检查数据本身是否可信
上表最后一行的时长是 -43 分钟。在 cdnping 这段记录里还有几条类似的负数(例如 -57、-17、-183 分钟)。可能的原因是状态页事后改写了开始时间,或者事件属于补录。不管哪种原因,负数时长都不能直接写进复盘。遇到这种情况,回到官方状态页的事件详情,逐条看 “Investigating / Identified / Resolved” 的更新时间,自己算。
这一步也适用于你自己的监控:探测节点本身挂了、时钟漂移、日志延迟入库,都会让时间线错位。复盘里写清每个时间点的来源,评审时别人才能复查。
第四步:用请求计数量化影响
“影响”一节最好有分母。以 AGI Plan 公开的逐小时请求统计为例(AGI Plan 是 cdnping 的商业合作方;数据来自 https://agiplan.dev/api/status/requests,不需要登录,口径是只有 5xx 算平台失败)。下面是 2026-10-16 连续三个小时的计数,每个小时桶从整点后第 5 分钟起算:
| 小时桶起点(UTC) | 成功 | 失败 | 失败率 |
|---|---|---|---|
| 06:05 | 664 | 722 | 52.1% |
| 07:05 | 455 | 877 | 65.8% |
| 08:05 | 921 | 107 | 10.4% |
这类数据能回答“影响有多大、什么时候开始回落”,但回答不了“为什么”。同一时间段上游有没有事件,要对照状态页记录;单看这三行,不能下根因结论。这正是复盘里“根因”和“影响”要分开写的原因。
失败率、可用率和错误预算怎么换算,可以参考 SLI、SLO、SLA 到底差在哪。
上游 AI 服务出故障,自己的复盘写什么?
当根因是“OpenAI / Claude / 某家云服务出了问题”时,不少团队就不写复盘了。这种情况其实更该写,因为上游故障并不少见。按 cdnping 同步的官方状态页记录(2026-09-17 至 2026-10-17),Claude 有 6 条 major 级别事件,OpenAI 有 4 条 major、19 条 minor,Cloudflare 有 3 条 major、19 条 minor。更完整的统计见 AI 服务故障盘点:2026 年 9–10 月。
上游的根因你改不了,复盘的焦点放在下面这些问题上:
我们比官方早还是晚知道?
对比 T0、T1 和 T3。如果你的告警比官方状态页还晚,说明你在依赖用户反馈或官方公告来发现问题。改进方向是加外部探测,具体做法见 AI 应用可用性监控怎么做。遇到问题时也可以先看 /down,那里汇总了官方状态页、每 5 分钟的 API 探测和用户报告,能较快判断是不是普遍现象。
错误码处理对不对?
看日志里失败请求的状态码分布:
jq -r 'select(.upstream=="openai") | .status' app.log | sort | uniq -c | sort -rn
502、503、529 这类通常值得带退避重试;400、401 重试没用,只会放大流量。如果事故期间重试把请求量放大了几倍,这就是一条促成因素。各状态码的含义和是否该重试,见 大模型 API 错误码速查。
有没有降级或切换路径,用了吗?
逐项检查:
- 是否有备用模型或备用供应商?切换要改代码还是改配置?
- 切换花了多久?是谁决定的?有没有预先写好的判断条件?
- 降级后用户看到了什么:报错、排队提示,还是功能悄悄变差?
- 超时设置是否合理?有没有请求挂满连接池拖垮其他功能?
- 恢复后是否切回?切回时有没有引起第二次波动?
我们对用户说了什么?
记录第一次对外通告的时间、渠道和措辞。如果用户比你的公告更早发现问题,可以考虑给自己的服务做一个状态页,参考 给 AI 服务搭一个状态页。
上游故障类复盘的行动项示例
| 行动 | 类型 | 验收标准 |
|---|---|---|
| 对每个上游 API 增加外部探测,连续 2 次失败告警 | 发现 | 演练中 10 分钟内收到告警 |
| 5xx 重试改为指数退避并设上限 | 缓解 | 压测中重试请求不超过原请求量的设定倍数 |
| 写好切换备用供应商的操作手册和触发条件 | 缓解 | 值班人员按手册独立完成一次切换演练 |
| 订阅上游状态页事件通知 | 发现 | 测试事件能推送到值班群 |
表里的“设定倍数”“10 分钟”请按自己的业务定,这里只是示例。
写完之后的自查清单
提交评审前过一遍:
- 时间线每一行都有证据链接或查询语句吗?
- 时区统一了吗?
- 影响一节有分母(总请求数)和统计窗口吗?
- 根因有证据,还是推测?推测是否标了“待确认”?
- 有没有出现人名指责?改成对流程和系统的描述。
- 每个行动项都有负责人、截止日期和验收标准吗?
- 是否单独写了“如果再发生一次,我们能更早发现吗”?
- 引用的外部数据(状态页、第三方探测)是否注明来源、窗口,是否检查过异常值(例如负数时长)?
复盘定稿后,过一到两周回头看行动项完成了多少,没完成的在周会上单独过一下。