事故指挥(IC):一次事故里谁说话、谁决策、谁对外 原创
本文是 SRE 实战系列第 22 篇,第二循环第二组的第一篇。
前面二十一篇,主角基本都是"被叫醒的那个人":第 8 篇教他 5 分钟止血,第 13 篇决定他该不该被叫,第 18 篇给他一份能照着敲的 Runbook,第 20 篇把他拉到演练里撞一次墙。这些讲的都是一个人的动作。
但真实事故里,动作从来不是一个人做的。20:02 到 21:40 之间,群里会有三十个人说话,八个窗口在改配置,同一条现状被反复打听,还有人是在手机上一边赶路一边看监控。没有人宣布开始,没有人宣布结束,也没有人知道谁是那个"说了算"的人。
全文约 1.3 万字。一次 98 分钟的故障,加副本、改超时、回滚、清理缓存,四个运维各自正确,四个动作叠在一起把影响面放大了。剩下的时间不花在技术上,花在协调上。然后回答五个问题:事故里到底需要几个角色、谁有权拍板回滚、跨团队升级怎么说才不撕破脸、对外说什么、以及怎么证明这一套真的在起作用。
这一篇的立场:事故不是被"处理好的",是被"组织好的"。技术上正确但组织上混乱的事故,损失经常比技术原因本身大。
一、98 分钟,四个人都在做对的事
一个电商平台,周四晚上的促销活动。20:02 开始,下单接口的 P99 从 220ms 涨到 3 秒,错误率 7%。
事后拉的完整时间线是这样:
| 时间 | 发生了什么 | 谁做的 | 当时他的判断 |
|---|---|---|---|
| 20:02 | 告警触发 OrderApiHighLatency,打到值班同学 A 的手机 | 告警 | — |
| 20:03 | A 确认指标是真的,按第 8 篇的四步验证走完 | 值班 A | 影响面:下单失败率 7% |
| 20:05 | A 在应急群里发了一条通知(第 8 篇那个模板) | 值班 A | "先止血" |
| 20:07 | A 扩容订单服务 40 → 80 副本 | 值班 A | 流量突增是常见原因 |
| 20:09 | 订单服务负责人 B 被拉进群,看到 A 扩了容,说"先看下 DB" | 负责人 B | 他怀疑是库 |
| 20:14 | 副本起来了,P99 没降,错误率到 9% | — | — |
| 20:15 | B 判断超时设得太长,把客户端超时从 3s 改成 800ms | 负责人 B | 快速失败,保护线程池 |
| 20:18 | 超时缩短后,前端重试逻辑被放大,QPS 上涨 40% | — | 没人注意到 |
| 20:22 | 基础设施同学 C 在群里说"数据库连接数已经 88%",建议回滚最近一次发布 | 基础设施 C | 他看到连接数 |
| 20:24 | 有人执行了回滚。但回滚的是网关服务,不是订单服务 | 值班 D | 他以为 C 说的是网关那次发布 |
| 20:31 | 网关回滚后,灰度阶段的 10% 流量被打回旧版本,而旧版本有另一个已知 bug | — | 没人知道 |
| 20:38 | 客服转来第一条用户投诉,群里开始有人问"要不要发公告" | — | 无人回答 |
| 20:41 | 支付团队在群里问"是不是下单链路的问题?我们成功率也掉了" | 支付团队 | 他们自己也在排查 |
| 20:47 | C 指出"回滚错服务了",群里出现两分钟内 40 条消息 | — | 混乱高峰 |
| 20:55 | 订单服务回滚完成 | 值班 A | 正确动作,但迟了 30 分钟 |
| 21:04 | 错误率回落到 2%,P99 仍在 1.2s | — | — |
| 21:12 | 缓存同学 E 发现热点 key 没命中,怀疑缓存被击穿 | 缓存 E | 另一个方向 |
| 21:20 | E 清理了一批缓存,P99 掉到 600ms | 缓存 E | 有效 |
| 21:26 | 群里有人问"恢复了没",四个人分别回答"我这点好了" | — | 没有统一口径 |
| 21:40 | 最后一个告警停止。群里逐渐安静 | — | — |
复盘的时候,最扎眼的不是哪个动作错了。四个人的每个动作单独拿出来都能站得住:
- A 扩容:流量型故障的标准第一反应,第 8 篇的止血工具箱里就有
- B 改超时:快速失败保护线程池,是教科书做法
- C 看连接数:他看到了真正的问题所在
- D 回滚:C 建议的就是回滚
- E 清缓存:最后确实是它起效的
问题在别的地方,数一下就知道:
- 四个方向同时在跑:扩容、改配置、回滚、清缓存,没有一处记录了"现在系统处于什么状态"
- 一条建议被错误执行,30 分钟才发现:C 说"回滚最近一次发布",没人定义是哪一次
- 用户投诉来了 12 分钟,没人回答要不要发公告
- 支付团队自己在查,因为他们不知道有人在管这条链路
- 结束没有时刻:最后是"群里逐渐安静",不是谁宣布的
98 分钟里,真正用于技术操作的时间加起来不到 20 分钟。剩下的 78 分钟分布在:互相确认、重复判断、等待、找错方向、以及对"现在到底怎么样了"的反复打听。
这就是这一篇要解决的问题。它不是"怎么更快地恢复"——那是第 8、10、19 篇的事。它问的是:一次事故里,谁在说话、谁在决策、谁对外,以及这些角色在 20:02 那一刻是怎么就位的。
二、先承认一件事:事故里最稀缺的不是技术,是决策吞吐
几乎所有团队在复盘时都会写"响应慢""排查效率低"。但把时间线摊开看,慢的往往不是排查,是决策排队。
2.1 一个简单的模型
把一次事故看成一条决策流水线。假设事故需要做 n 个决定(回不回滚、扩不扩容、发不发公告、叫不叫 DBA、限不限流、要不要止损关闭入口……),每个决定平均需要 t 分钟被做出来,那么:
事故时长 T ≈ n × t + 技术修复时间大多数团队的真实情况是:n 大概在 8~15 之间,t 是 5~15 分钟。也就是说,决策排队本身就吃掉了 40~150 分钟,而技术修复可能只需要 10 分钟。
t 为什么这么大?三个原因,都不是"人笨":
- 不知道谁有权决定。 谁都能提建议,但谁都不敢拍板。于是提出建议 → 等 owner 表态 → owner 不在线 → 再找一个人 → 这个人说"我不确定"。
- 不知道别人已经决定了什么。 A 以为 B 在回滚,B 以为 A 在扩容,两个人都在等对方。信息不同步的代价不是"多花点时间沟通",而是所有人都在做重复的、有时互相抵消的动作。
- 不知道现在该先做哪个。 同时有五件事可做,五个人各自挑一件,没有优先级排序。上一节那个 98 分钟就是典型:四个方向并行,没有一个被做到位。
2.2 所以事故管理要解决的是 n 和 t
不是"让工程师更聪明",而是两件事:
- 把
t压到 2 分钟以内:靠明确的角色授权,不靠开会。谁有权在什么范围内拍板,事先写清楚,事故里不用再讨论。 - 把
n压到必要的量:靠把"已经在事故里被证明有效的决策"固化进 Runbook 和预案,变成条件反射而不是现场判断。
这两件事合起来,就是事故指挥(Incident Command)要干的事。
2.3 顺带说清楚:IC 不是"技术最强的那个人"
这是落地时最容易错的一点。很多团队把"事故负责人"默认给了最资深的那个工程师,结果反而更糟。
原因很简单:指挥和技术排查需要的大脑不是同一个。技术排查要沉浸、要专注、要长时间不被打断地读一个 trace;指挥要不停地被打断、不停地收口信息、不停地做小决定。一个人同时干这两件事,结果是两头都做不好——他会因为专注于某个技术细节而错过群里那条"支付也挂了"的消息。
所以第一条规矩:
IC 尽量不碰键盘。IC 一旦开始排查,就不是 IC 了。
如果团队小到没有第二个人可用,让 IC 兼排查,但要在复盘里明确记下"这次事故缺 IC"。这个记录不是批评,是让人看见缺口。
三、事故里的三个角色,加一个可选角色
不需要复杂。实战里最小可用的分工只有三个角色,人手充足时加第四个。
3.1 角色对照表
| 角色 | 全称 | 一句话职责 | 谁适合 | 不做什么 |
|---|---|---|---|---|
| IC | Incident Commander | 决定做什么、不做什么、什么时候停 | 有授权、能拒绝别人、不沉迷细节的人 | 不排查、不敲命令、不写代码 |
| OL | Ops Lead | 把 IC 的决定变成实际操作,带技术排查 | 最懂这套系统的人(通常是服务 owner) | 不对外沟通、不决定优先级 |
| CL | Comms Lead | 对内广播状态、对外发公告、挡住无关问询 | 表达清楚、能顶住压力的人(PM / 值班经理 / TAM 均可) | 不判断技术、不改系统 |
| Scribe | 记录员(可选) | 记时间线和决策日志,供复盘用 | 任何能打字的人 | 不参与决策 |
四个角色里,只有 IC 是必须由人长期担任的。OL 和 CL 可以在事故过程中交接,Scribe 可以由机器干(后面有一节讲怎么干)。
3.2 IC 到底在做什么
把 IC 的工作拆成五个动作:
- 开场(第一个 3 分钟):宣布事故开始、声明自己是 IC、给出现状一句话、先定严重度、分配 OL 和 CL
- 收口(持续):所有信息向 IC 汇总,所有决定从 IC 发出。群里任何人提出的方案,IC 必须给一个明确的回应——"好,做"或者"不做,先做 X"或者"待定,5 分钟后我们再看"。沉默是最贵的回应
- 排序(持续):同时可能有三件事可做,IC 的工作是说"只做这一件"
- 守时(持续):设定下一个状态更新时间(通常 15 分钟),到点必须更新一次,哪怕没有进展
- 收尾:宣布缓解、宣布结束、指定复盘负责人、发起复盘
第 2 条和第 4 条是最容易被省略的,也是最关键的。
3.3 一个可以直接抄的 IC 开场模板
事故最初的 3 分钟决定了后面 90 分钟的形状。IC 应该在群里发出这样一条消息,然后把它置顶:
🚨 事故启动 · INC-2026-1106-01
我是 IC:@张三(这起事故由我决策)
严重度:P1(按下单成功率 7% 定级,见定级树)
现象:下单接口 P99 220ms → 3s,错误率 0.2% → 7%,20:02 开始
影响面:下单主链路,估算影响 40% 下单请求,暂无数据损坏迹象
当前判断:未明,正在确认是否与 19:50 的发布相关
角色:
- IC:@张三
- Ops Lead:@李四(负责订单链路技术排查与操作)
- Comms Lead:@王五(对内外沟通,所有对外消息由她出)
节奏:
- 每 15 分钟一次状态更新,下一次 20:22
- 任何操作在执行前必须先在本群说一句"我要做 X,预计 Y 分钟",得到我的确认
- 需要跨团队支援,找 @王五,不要在群里 @ 所有人
事故群:本群
看板:https://grafana.internal/d/incident-order这条消息里的每一行都有作用,值得逐条解释:
| 内容 | 为什么必须有 |
|---|---|
| "我是 IC" | 一句话消灭"谁说了算"的歧义。没有这句,后面所有决定都要先讨论一次权限 |
| 严重度 | 决定通知范围、响应时限、是否需要拉高管。它决定了谁会来,而来的人决定了后面的复杂度 |
| 现象 + 影响面 | 给所有被拉进来的人同一个起点。缺少这段,每个人都要自己查一遍 |
| 角色 | 让每个人知道该找谁。尤其 CL——没有 CL,IC 会被问询淹没 |
| 更新节奏 + 下一次时间 | 把"不定时的焦虑"变成"定时的等待"。这是整条模板里最被低估的一行 |
| 操作前先说 | 拦住第 1 节那种"四个方向并行"。它不阻止行动,只要求行动被看见 |
关于最后一条,有个常见的反对意见:"事故里还要先申请,不嫌慢吗?"——注意它要求的是"说一句",不是"审批"。在群里写"我要回滚订单服务,约 3 分钟"的成本是 5 秒,而一次方向冲突的代价是 30 分钟。第 1 节那个回滚错服务的案例,就是因为没人说过"我要回滚哪个"。
3.4 交接:24 小时的事故一定有换班
跨夜的事故必然要换人。交接做不好,等于把前面两小时重新走一遍。
交接只需要回答四个问题,用一条消息发完:
🔄 IC 交接 · INC-2026-1106-01 · 20:00
1. 现在系统是什么状态(已验证的事实,不含推测)
订单服务已回滚到 v2.31.4,错误率 7% → 0.4%,P99 仍 1.2s(基线 220ms)
2. 已经排除的方向(避免接手的人重走)
DB 连接数正常(62%),缓存命中率已恢复 94%,网络无丢包
3. 当前最可能的假设 + 下一步要验证什么
怀疑回滚后旧版本的慢查询导致 P99 未恢复,下一步:比对 v2.31.4 的 SQL 执行计划
4. 还没做的决定(挂着的)
- 是否对下单入口限流(我倾向做,但还没拍板)
- 是否发第二条对外公告(第一条 20:40 已发)第 2 条"已经排除的方向"是交接里最值钱、也最常被漏掉的一段。没有它,接手的人会从零开始,而通常他会走到同一个死胡同里。
第 4 条同样重要:挂着的决定如果没有交接,会在换班后静默消失——没人做,也没人记得曾经要做。
3.5 一个反直觉的原则:IC 不追求"最快"
IC 的考核指标是"事故的整体代价",不是"恢复得最快"。这两者在某些时刻是对立的。
举个具体的:一次故障已经止血成功(错误率回到基线),但根因没找到。这时候有两种选择——立刻宣布结束去睡觉,或者再多花 20 分钟确认根因并写完公告。
一个好的 IC 会选后者,因为:
- 根因不明的事故,会有 30% 概率在 2 小时内复发。复发时所有上下文都散了,恢复时间是第一次的 1.5 倍
- 对外公告如果写得含糊,客服第二天会被问爆
这也是为什么 IC 需要一个"结束词"的清单(见第九节),而不是"看着差不多了"。
四、定级:事故不是靠感觉分大小的
事故严重度(Severity)和第 13 篇的告警级别(P0-P4)不是一回事,虽然它们经常用同一套字符串。两者的区别必须讲清楚,否则落地时一定会混:
| 告警级别(第 13 篇) | 事故等级(本文) | |
|---|---|---|
| 对象 | 一条告警规则 | 一次正在发生的事件 |
| 谁定 | 规则里的 label,事先定好 | IC 现场判定,可随事态调整 |
| 可变性 | 不可变 | 可升可降 |
| 作用 | 决定通知通道与响应时限 | 决定谁必须进场、对外口径、复盘等级 |
| 粒度 | 按指标 | 按用户影响 |
一条 P0 告警可能对应 P3 事故(误报或单实例);一次 P3 事故也可能因为迟迟不恢复被升到 P1(时间也是一种影响)。
4.1 定级只用两个维度
正常定级有四个维度(影响、紧迫性、可自动化、责任边界),但那是告警分级用的。事故定级简化成两个,因为它必须在 3 分钟内完成:
- 影响面:多少用户、多深、有没有数据后果
- 趋势:在恶化、平稳、还是在缓解
| 等级 | 影响面 | 典型例子 | 谁必须在场 |
|---|---|---|---|
| SEV-1 | 核心链路整体不可用,或数据丢失/损坏,或资损 | 支付全挂、订单库数据错乱、对账差账 | IC + OL + CL + 业务负责人 + 技术一号位,24 小时内出初步复盘 |
| SEV-2 | 核心链路部分受损,或有可用降级方案但未生效 | 下单成功率掉 5%,单地域故障 | IC + OL + CL,48 小时内复盘 |
| SEV-3 | 非核心服务受损,或用户可绕过 | 商品详情页某个模块 500 | IC + OL,一周内复盘(可合并) |
| SEV-4 | 无用户感知,仅内部指标异常 | 某个异步任务延迟上涨 | 不启动 IC,按告警处理 |
两个必须写进规矩的细节:
第一,定级宁可高,降级要留痕。 高估的代价是多来几个人(多几个人比少几个人便宜得多),低估的代价是没人管。但降级必须留下记录:severity_change: SEV-2 → SEV-3 at 21:10 by @张三,理由:影响面确认为 3% 且可用降级绕过。没有这条记录的降级,在复盘里会被当成"掩盖"。
第二,SEV-1 和 SEV-2 的分界线是"有没有数据后果",不是"多严重"。 技术上恢复得再快,只要发生了数据损坏或资损,就必须是 SEV-1——因为它需要后续的对账、补偿、法务、客服动作,这些都不在技术链路里。
4.2 定级决定通知矩阵
事故等级真正的用途,是自动决定"谁被叫起来"。这张表建议直接抄进值班系统,让定级动作触发通知,而不是靠 IC 手动一个个拉人。
| 等级 | 立即(自动) | 15 分钟未缓解(自动) | 30 分钟未缓解(自动) |
|---|---|---|---|
| SEV-1 | IC 备选池、服务 owner、CL 池、值班经理 | 业务负责人、客服主管、公关接口人 | 技术一号位、CEO(由值班经理判断) |
| SEV-2 | IC 备选池、服务 owner、CL 池 | 值班经理 | 业务负责人 |
| SEV-3 | 服务 owner、同组 on-call | 值班经理(仅 IM) | — |
| SEV-4 | 记入看板 | — | — |
这张表落地时有三个工程细节:
- IC 备选池要轮值,和 on-call 一样排班。没有 IC 备选池,事故来了只能"现场找一个最资深的人",等于没做
- "15 分钟未缓解"要靠计时器,不能靠人记。 用一条定时任务扫事故记录表,到点自动升级通知,而不是等 IC 想起来
- 通知里必须带事故上下文,不能只发一句"SEV-1 事故"。被叫醒的人第一件事是找群,找群的时间里事故还在恶化。通知模板至少带:事故编号、现象一句话、事故群链接、当前 IC 是谁
第 3 条是那种"看起来很小、实际很管用"的改动。一次 SEV-1 深夜升级,被叫的人如果能在 30 秒内进群、看到置顶的状态更新,参与效率比"先问发生了什么"高一个量级。
4.3 定级的一棵树,不是一张表
现场需要 3 分钟内定级,所以给一棵判定树,比给一张表更快:
① 有数据丢失/损坏、资损、或安全泄露?
└─ 是 → SEV-1(无论技术是否已恢复)
└─ 否 → ②
② 核心链路(下单/支付/登录)整体不可用,且有 >5% 用户受影响?
└─ 是 → SEV-1
└─ 否 → ③
③ 核心链路部分受损(成功率 <95% 或 P99 超基线 3 倍),或影响面 >1% 用户?
└─ 是 → SEV-2
└─ 否 → ④
④ 非核心服务受损,或核心链路轻微劣化但用户可绕过?
└─ 是 → SEV-3
└─ 否 → SEV-4
⑤ 定完之后立刻问一句:趋势是在恶化吗?
└─ 是 → 升一级(最多到 SEV-1)
└─ 否 → 保持第 ⑤ 步是很多人会漏的。趋势比绝对值重要:一个 3% 的错误率如果每 5 分钟翻倍,它比 10% 但平稳的错误率更危险。这也是为什么定级要复核——事故开始 15 分钟后必须再看一眼,因为这个时刻的判据已经从"多严重"变成"是不是还在恶化"。
五、角色怎么落地:一个 IC 的 20 分钟
前面都是原则。这一节把 IC 的实际操作摊开,从 20:02 到 20:22,逐分钟。
5.1 前 3 分钟:确认 + 宣布
20:02 收到告警。先做第 8 篇那个 30 秒验证(指标、Pod、事件、日志四项)。
20:03 确认是真的。此时不要开始深挖原因——那是 OL 的事。
现在只做三件事:
1) 估算影响面(从哪里看?下单成功率、下游报错、客服投诉量)
2) 按定级树定一个等级
3) 在事故群发出第 3.3 节那条开场消息
20:05 开场消息已发。置顶。此时你是 IC。一个具体的坑:开场消息不要等"搞清楚原因"再发。 很多 IC 会想"我连原因都不知道,发什么",于是拖到 20:15 才宣布,这 10 分钟里所有人都在各自为战。开场消息里"当前判断:未明"是完全可接受的答案。
5.2 第 3~10 分钟:分派第一个方向
20:05 给 OL 一个明确的第一个任务,而不是"你去查一下"。
✅ "李四,先用 3 分钟确认三件事:19:50 的发布是否全量、DB 连接数、下游依赖的错误率。查完回来报。"
❌ "李四你先排查下原因。"(他会从系统最顶层开始一层层查,花 20 分钟)
20:08 CL 已就位,让她先发一条对内广播(不是对外),告知"已有人负责,正在排查"。
对内广播的作用是止血焦虑,不是传达技术细节。
20:10 群里开始有人 @ 你。此时要做的是把问询挡给 CL:
"所有进展看本群置顶,问题找 @王五。"这条话术整场只发一次。第 3-10 分钟里,IC 做的最重要的事是把"排查"变成"验证一个假设"。前者是开放的,后者是收敛的。两者差别很大:开放式排查的平均时长是 20-40 分钟,验证一个明确假设通常是 3-5 分钟。
5.3 第 10~20 分钟:做第一个决定
到 20:15 左右,通常会有 2~3 个方向被提出。IC 的工作是选一个,并明确拒绝其他的:
20:15 OL 回报:DB 连接数 88%,19:50 的发布是 C2 级别,已全量 12 分钟。
20:16 IC 决定:先回滚订单服务(不是网关),理由是连接数是唯一在恶化的资源,
且发布刚全量。给 OL 明确的执行指令:
"李四,回滚订单服务到 v2.31.3,用 rollback-point 清单,预计 4 分钟。
回滚前先在群里说一句开始。"
20:17 同时明确拒绝另外两个方向,并给出理由(这一步不能省):
- "扩容停掉,已经 80 副本了,瓶颈不在算力。"
- "缓存先不动,回滚完如果 P99 还高再看。"
20:20 状态更新(第 2 次):
"已决定回滚订单服务,20:17 开始,预计 20:21 完成。错误率 9%,还在上升。
下一个更新点 20:35,除非出现变化。"第 20:17 那一步,即"明确拒绝并给理由",是 IC 最反人性、也最关键的动作。原因是:没被拒绝的方向不会消失,它会自己在后台跑起来。 那个 98 分钟的案例里,四个方向之所以并行,就是因为没有人说过"这个先别做"。
5.4 IC 的三句万能话术
事故里 IC 需要说的话其实就那么几句,背下来:
| 场景 | 话术 |
|---|---|
| 有人提出一个方案,但你还没准备好决定 | "收到,先记着,等 X 结束我们再评估。"(给出明确的复核时点,而不是"再看看") |
| 有人在你没被告知的情况下改了配置 | "谁在 20:15 之后动过网关配置?现在告诉我改了什么。"(问事实,不问责任;事后复盘才谈责任) |
| 有人问"现在怎么样了"(第十次) | "看置顶。20:35 会有下一次更新。"(统一指向,不重复回答) |
再加一句给 OL 的:"如果 5 分钟内没有进展,回来告诉我,我们换方向。" 这句话的作用是授权 OL 放弃——没有这句,OL 会死磕一个方向 40 分钟,因为他觉得"还没查完不能说没查出来"。
六、决策权:谁能拍板回滚
之前讨论了"该回滚还是该继续",那是技术判断,这一篇要讲另一半——谁有权按那个按钮。
现实中的困境通常是这样的:回滚会丢一部分已写入的数据,或者会让正在进行的活动中断。执行的人不敢拍,于是去找 leader,leader 说"你判断吧",于是又回到了执行的人身上,时间过去了 20 分钟。
6.1 一份决策权清单
把常见的事故决策列出来,事先指定"谁可以单独拍板、谁必须两个人一致、谁必须上报"。这是一份应该由技术一号位签字的东西,不是 SRE 自己写的。
| 决策 | 可单独拍板 | 需要双方确认 | 必须上报 |
|---|---|---|---|
| 扩容副本 / 提资源 | 值班 or OL | — | — |
| 开启降级开关(预设好的) | 值班 or OL | — | — |
| 回滚一次 C1/C2 变更 | 值班 or OL | — | — |
| 回滚一次 C3 变更(含 schema) | — | IC + 服务 owner | — |
| 回滚一次 C4 变更(不可逆) | — | — | IC + 业务负责人 |
| 限流 / 关闭某个入口 | IC | OL(评估影响) | — |
| 主动丢弃部分请求(如排队超过 X) | IC | 业务负责人 | — |
| 停掉一个正在跑的对账 / 结算任务 | — | IC + 财务接口人 | — |
| 对外发布公告 | — | IC + CL | 公关(SEV-1) |
| 宣布事故结束 | IC | OL | — |
三列的设计逻辑:
- "可单独拍板"的东西,都是可逆的、有止损边界的。 扩容、开降级、回滚 C1/C2,这些做错了也能马上再做回来,所以不该让任何人等
- "双方确认"的东西,是会影响别人的。 回滚 C3 会牵扯数据兼容,限流会影响业务
- "必须上报"的东西,是不可逆或者有外部后果的。 C4、停结算、发公告
6.2 把清单变成机器的判断
清单最容易的失效方式是"写完了放在 Confluence 里"。落地要把它变成发布系统和事故工具的一部分:
# incident-decide.sh —— 事故工具里的一个按钮
# 用法:./incident-decide.sh "rollback order-api v2.31.3"
# 作用:读变更级别 → 判断需要谁确认 → 生成待确认清单并 @ 相应角色
set -euo pipefail
INC="${INC_ID:?需要设置 INC_ID}" # 事故编号,如 INC-2026-1106-01
ACTION="$1"
# 1) 变更级别从发布流水线里读,不由人填
CASE_ID=$(echo "$ACTION" | grep -oE '\b(v[0-9]+\.[0-9]+\.[0-9]+)\b' || true)
CLASS=$(curl -s "https://deploy.internal/api/releases/$CASE_ID" | jq -r '.change_class')
# 2) 级别 → 需要的确认方
case "$CLASS" in
C1|C2) REQUIRED="IC" ;;
C3) REQUIRED="IC,service-owner" ;;
C4) REQUIRED="IC,business-owner,tech-lead" ;;
*) echo "未知变更级别,按 C4 处理"; REQUIRED="IC,business-owner,tech-lead" ;;
esac
# 3) 生成确认请求(不阻塞,发出即可,人回复 approve 才算)
for role in ${REQUIRED//,/ }; do
./incident-bot approve-request \
--incident "$INC" --role "$role" \
--action "$ACTION" --class "$CLASS"
done
echo "[$INC] 动作: $ACTION | 变更级别: $CLASS | 待确认: $REQUIRED"这段脚本真正解决的不是"审批",而是把"该问谁"变成一个不需要现场思考的动作。事故里最贵的不是做错决定,而是花 10 分钟确认"这事该问谁"。
6.3 一条必须明确的授权红线
有一件事比清单更根本:必须有人拥有"压倒性授权"。
意思是,事故里必须有一个人可以在不做任何解释的情况下让任何操作停下来,包括业务方的操作。这个人通常是 IC(或者 IC 之上的值班经理)。没有这条红线,就会出现"业务方坚持要开某个入口、技术方说会崩、两边僵持 30 分钟"的局面。
对应的书面约定通常长这样:
在 SEV-1/SEV-2 事故期间,IC 有权:
1. 停止任何非事故相关的变更、发布、批量任务、DDL
2. 关闭或限流任何入口,包括营收入口
3. 拒绝任何非事故处理所需的操作请求
4. 要求任何人在 5 分钟内响应事故群的消息
IC 的这些决定不需要事先请示,但必须在事故群里留下一条记录,
并且在事后复盘里接受审查。追责只针对"没有记录",不针对"决定的正确性"。最后那句是关键。如果不把"决定的正确性"从追责范围里拿出去,没有人敢用这个授权——授权必须配豁免,否则就等于没有授权。
七、升级:跨团队怎么说才不撕破脸
事故里最耗时的跨团队动作不是技术协作,是让人相信这件事跟他有关。
常见的失败样子:你在群里 @ 了 DBA 团队的人,说"我们下单慢了,你看下数据库",对方回"我们 DB 监控正常",然后对话结束,各自都不动了。你等了 15 分钟再问,对方说"我刚才看了,没问题"。
问题不在技术,在请求的形状。上面那句话没有给任何可执行的东西:没有具体的指标、没有具体的时刻、没有"我需要你做什么"、没有回执要求。
7.1 升级请求的四段结构
一个有效的升级请求,必须包含四段,缺一不可:
【升级请求】INC-2026-1106-01 · 从 SRE 升级到 DBA 团队
1. 事实(不是判断)
20:02 起,订单库 threads_running 从 12 涨到 180,
threads_connected 62% → 88%,同一时段 order-api 的 P99 从 220ms 涨到 3s。
2. 我们已经做了什么(避免对方重复)
已扩容订单服务至 80 副本(无效)、已回滚 v2.31.3(错误率下降但 P99 未恢复)。
3. 需要你们做什么(具体、单件、有完成判定)
帮我们确认 20:00-20:20 之间是否有异常慢查询,以及 19:50 那次发布新增的
SQL 是否走了全表扫描。判定标准:给出 top5 慢查询及其执行计划。
4. 时限与回执
请在 10 分钟内(20:40 前)回一句"在看了"或"这个不是我们这边"。
如果 20:45 还没有结论,我会把你这条链路当成嫌疑,同时上报值班经理。四段的作用:
- 第 1 段用事实,不用判断。说"数据库有问题"是判断,会引发争论;说"threads_running 从 12 到 180"是事实,无法争论
- 第 2 段是很多人会漏的,也是最有效的:它证明你在请人帮忙之前已经自己努力过了,对方的抵触会小很多
- 第 3 段要求"单件 + 有判定标准"。这是因为跨团队协作里,模糊的请求会被无限期推迟
- 第 4 段给出时限和回执要求,并且明确说明超时的后果。这一条是让请求真的被受理的关键
7.2 三个升级的反面案例
反面一:把判断当事实说。 "你们的库把我们拖垮了" —— 对方第一反应是防御,之后的 20 分钟都在证明"不是我们的库"。改为事实陈述,10 秒就能开始干活。
反面二:一次要求五件事。 "帮忙看下慢查询、连接数、锁等待、主从延迟、磁盘 IO" —— 对方会挑最容易的做,然后回"正常"。一次只要一件事,拿到结果再要下一件。
反面三:@ 所有人。 群里 @ 所有人看起来是"通知到位",实际效果是"没有人觉得是自己的责任"。永远 @ 一个具体的人,如果不知道是谁,@ 对方的 on-call 机器人或者值班人。
7.3 升级的时机,不要靠感觉
什么算"该升级了"?给三个可以量化的触发条件:
| 触发 | 说明 |
|---|---|
| 15 分钟无缓解 | 从 IC 宣布事故起算,纯计时 |
| 出现跨服务/跨团队嫌疑 | 一旦怀疑的不是自己家,立刻升级,不要等确认 |
| 需要不可逆决策 | 需要 C4 回滚、停业务、丢请求时,提前升级,不要等到要做的那一刻 |
第二条值得强调:怀疑也要升级。 很多团队的惯性是"先自己查清楚是不是别人的问题,再去麻烦人家",这个习惯会吃掉 15~30 分钟。正确的做法是"我有理由怀疑,同时我自己继续查",两条线并行。
八、对外:四种状态页模板
对内的沟通靠群和置顶,对外的沟通只能靠 CL,而且必须用固定模板。原因是:事故里临时组织语言,写出来的东西一定包含三类问题——推测被当成事实、时间点含糊、下次更新没有承诺。
对外口径的底线三条:
- 只说已验证的事实和已承诺的动作,不说推测。写下"疑似",就等着被追问三轮
- 必须有时间。发布时刻、影响时段、下一次更新时刻,三个时间缺一个,用户和客服就会自己编
- 不能空泛地道歉,也不能解释细节。用户要的是"能不能用""什么时候好""我的钱有没有事"
8.1 四种模板
模板 A:事故中(首次公告,用于影响面已确认)
[故障通知] 部分用户下单可能失败(更新于 20:40)
影响范围:20:02 起,部分用户在下单时可能遇到失败或超时,影响面约 40% 的下单请求。
支付环节不受影响,已支付未完成的订单会在恢复后自动补偿。
我们正在做的事:已完成回滚,错误率正在下降,正在确认最终原因。
下一次更新:21:10(或状态变化时立即更新)模板 B:事故中(进展更新,用于 15 分钟一次的节奏)
[进展更新 21:10] 下单成功率已恢复至 96%
20:40 之后的变化:回滚完成,错误率从 7% 降到 0.8%;
下单响应时间仍高于平时(约 3 倍),正在处理。
用户侧建议:如遇下单失败,请稍后重试,不要重复提交。
数据影响:暂无订单数据异常,已支付的订单不受影响。
下一次更新:21:40(或状态变化时立即更新)模板 C:已缓解(恢复公告)
[已恢复 21:40] 下单服务已恢复正常
影响时段:20:02 — 21:40,共 98 分钟。
影响内容:约 40% 的下单请求在高峰时段失败;支付未受影响。
当前状态:下单成功率 99.9%,响应时间已回到正常水平。
后续:我们会在 3 个工作日内发布故障复盘,对受影响的订单做逐单核对。
如有疑问请联系客服。模板 D:SEV-1 事故的高管/客户专用版
[客户通知 · 需一对一发送]
尊敬的 XX:
今晚 20:02 - 21:40,我们的下单服务出现故障,贵司在此时段内的订单可能出现失败。
我们已定位并修复,目前服务正常。
需要您关注的:请协助核对此时段内的订单数量与资金流水,如发现差异请直接回复本邮件。
我们已安排专人(联系人:XXX,电话 XXX)在 24 小时内响应任何相关问题。
我们会在 3 个工作日内提供正式复盘报告。第 4 个模板单独存在的原因是:SEV-1 的客户沟通不能走群发。 群发公告的对象是"所有用户",而需要一对一沟通的客户通常不到 20 个,他们需要的是核对和赔偿路径,不是道歉。
8.2 CL 的定位:一个挡箭牌
CL 这个角色在很多团队里最终活成了"发公告的人",那就浪费了。
CL 真正的作用是替 IC 挡住所有非技术问询。有 CL 和有 CL 的差别,在事故里非常直观:
- 没有 CL:技术负责人 5 分钟内会被 @ 8 次,其中 6 次是"现在怎么样了""要不要发公告""客服那边怎么回"
- 有 CL:这 8 次全部落到 CL 那里,技术负责人只收到一条"现在是 XX 状态,我已按口径对外,你继续"
所以 CL 的选人不该是"谁有空",应该是**谁能扛得住压力、谁敢说"这个我现在不能回答"**的人。PM、值班经理、TAM、客服主管都可以,只要他在事故期间有对外发言的授权。
8.3 一个容易漏掉的对外对象:客服
SEV-1/SEV-2 事故里,第一个接触到用户抱怨的不是工程师,是客服。如果客服不知道发生了什么,他们会对每一个打进来的用户给出不同的解释,而这些解释会被截图传播,形成第二个舆情。
所以 CL 在发第一条对外公告之前,应该先给客服发一条内部话术:
[客服话术 · 20:45 生效]
当前情况:下单服务故障,已在恢复中,预计 21:40 前恢复。
用户来电统一回复:
1. 承认事实:"是的,我们目前下单服务有故障,工程师正在处理。"
2. 给时间:"预计 40 分钟内恢复。"
3. 给动作:"如您已支付但订单未生成,我们会在恢复后自动补单,无需重复支付。"
4. 不给承诺:"请不要相信任何非官方渠道的说明。"
不要说的:不要猜测原因、不要承诺赔偿、不要给具体的恢复时刻(只给"预计 40 分钟内")。
需要升级的:涉及资金差异、投诉要求赔偿的,转给 @王五。这份话术的价值不在于"公关",在于减少客服和工程师之间的互相打断。一次 SEV-1 里,工程师被打断 20 次去解释同一件事,损失的时间比技术排查本身还多。
九、结束:怎么知道一次事故真的结束了
第 1 节那个案例的结束方式是"群里逐渐安静"。这种方式的问题是:没人知道它结束了,于是第二天有人来问"昨晚那个事怎么样了",再花半小时解释。
结束必须有明确动作、明确判据、明确的人宣布。
9.1 结束的四个条件
四个必须同时成立:
- SLI 回到基线区间(不是"好了点",是回到基线。具体到指标:错误率 < 基线 1.5 倍,P99 < 基线 2 倍,持续 15 分钟)
- 所有被改动的配置/版本已被记录,且知道自己现在跑的是什么。这一条挡的是"回滚了一半"的状态
- 没有悬着的不可逆操作(数据修复、补单、对账任务是否已启动)
- 对外公告已收敛(最后一条公告已发出,客服话术已更新)
第 2 条最容易被忽略,代价也最大:事故中随手改的超时、临时摘掉的健康检查、临时放宽的限流阈值,如果不记录,会在系统里长期留着,成为下一次事故的伏笔。
9.2 一份事故记录的 schema
事故结束时要产出的不是一篇文档,是一条结构化记录。第 10 篇给过复盘会议模板,这里要的是可供工具读取的那一层——因为它要喂给度量、喂给 Runbook、喂给训练。
# incidents/2026/INC-2026-1106-01.yaml
id: INC-2026-1106-01
severity: SEV-2
severity_changes:
- {at: "2026-11-06T20:05:00+08:00", from: null, to: SEV-2, by: zhang_san}
- {at: "2026-11-06T20:20:00+08:00", from: SEV-2, to: SEV-1, by: zhang_san,
reason: "支付成功率同步下降,确认为跨链路"}
roles:
ic: [zhang_san]
ol: [li_si, zhao_liu] # 有交接,按时间顺序
cl: [wang_wu]
scribe: incident-bot # 由机器人记录
timeline:
- {at: "20:02", event: "alert_fired", source: alertmanager, detail: "OrderApiHighLatency"}
- {at: "20:05", event: "incident_declared", by: zhang_san}
- {at: "20:17", event: "action_started", by: li_si, detail: "rollback order-api v2.31.3"}
- {at: "20:24", event: "action_started", by: zhao_liu, detail: "rollback gateway",
note: "与 IC 指令不一致,20:47 才发现"} # 这类记录是复盘的核心素材
- {at: "21:40", event: "incident_resolved", by: zhang_san}
decisions:
- {at: "20:16", decision: "回滚订单服务而非扩容", by: zhang_san, rejected: ["扩容至 120 副本", "清缓存"]}
detection: {alert_to_ack: 60, ack_to_declare: 120, declare_to_first_action: 660}
escalations:
- {at: "20:22", from: sre, to: dba, reason: "连接数异常", response_s: 540}
user_impact:
duration_min: 98
estimated_affected_requests: 41200
data_loss: false
financial_impact: false
actions_outstanding: # 事故结束但没做完的事
- {item: "临时放宽的网关限流阈值恢复", owner: li_si, due: "2026-11-07"}
- {item: "核对 20:02-21:40 订单与支付流水差异", owner: fin_interface, due: "2026-11-08"}几个字段值得单独说:
severity_changes:记录定级的每一次变化和理由。没有理由的变更不允许提交(用 CI 校验)timeline.note:上面那条"与 IC 指令不一致"的记录,是复盘里最有价值的一条。事故记录不是给人看的证明书,是给自己看的事实decisions.rejected:记录"否决了什么",这比记录"做了什么"更有信息量。它告诉你这套系统在当时的约束下排除了哪些可能detection三个间隔:这是第 10 节度量的直接输入actions_outstanding:事故结束 ≠ 事情做完。这个字段不填,事故就会悬着
9.3 谁写这条记录
不该由人来写。事故群的每条消息都是时间戳的,机器人完全可以自动抽出 alert_fired、incident_declared、action_started、escalation。人只需要补三块:decisions、severity_changes、user_impact。
所以落地时最省力的做法是:事故群里的固定句式被机器人识别。这反过来要求 IC 在群里用固定格式说话(第 3.2 节那几句),两者是同一件事的两面。
十、MTTR 不是一个数,是四个
所有团队都在报 MTTR。但它是个平均值,而平均值在事故里几乎总是骗人的,这里是同一个道理。
10.1 拆开来看
把一次事故从发生到恢复拆成四段:
故障注入 → [MTTD] → 有人知道 → [MTTA] → 有人到场 → [MTTK] → 完成止血 → [MTTR-R] → 恢复
t0 t1 t2 t3 t4
MTTD = t1 - t0 检测时间(detection):故障发生到有人被告知
MTTA = t2 - t1 响应时间(acknowledgment):被告知到开始动手
MTTK = t3 - t2 止血时间(kill):开始动手到影响停止扩大
MTTR-R = t4 - t3 恢复时间(recovery):止血到完全恢复
MTTR = t4 - t0四段对应四个完全不同的改进动作,混在一起报 MTTR 的团队,永远不知道该改哪个。
| 段 | 主要受什么影响 | 改法 | 归属 |
|---|---|---|---|
| MTTD | 告警覆盖率、告警延迟、for 时长、是否有人报障才发现 | 补告警、降延迟、改燃烧率 | 可观测性 |
| MTTA | 通知触达、on-call 是否在线、是否知道该干什么 | 路由、电话触达、Runbook 一键打开 | 值班体系 |
| MTTK | 有没有可用的止血手段、决策是否卡住 | 降级开关、回滚点、决策权 | 工程 + 组织 |
| MTTR-R | 恢复是否需要重放数据、缓存重建、下游追赶 | 容量 + 数据修复方案 | 架构 |
10.2 一个更重要的数:MTTK 占 MTTR 的比例
单独看任何一段都没有意义,比值才有。
经验上,一次"组织上健康"的事故,四个数字大致是这个形状(以 60 分钟为例):
| 段 | 健康区间 | 说明 |
|---|---|---|
| MTTD | < 5 分钟 | 超过 5 分钟说明靠人报障,那时影响面已经翻倍 |
| MTTA | < 3 分钟 | 超过 5 分钟通常是通知链路有问题,不是人懒 |
| MTTK | < 20 分钟 | 这是事故管理唯一真正能大幅改善的一段 |
| MTTR-R | 剩余时间 | 长尾恢复通常是架构问题,短期改不了 |
如果 MTTK 占了 MTTR 的一半以上(最常见的情况是 60%~80%),说明问题几乎肯定不在技术上,而在决策排队上——正是本文第二节那个模型。
10.3 从事故记录里算出这四个数
#!/usr/bin/env python3
# incidents/metrics.py —— 从事故记录算 MTTD/MTTA/MTTK/MTTR-R
# 用途:月度复盘和季度趋势,输出按严重度分组的分布(不是均值)
import json, pathlib, statistics
from collections import defaultdict
def load_incidents(root="incidents"):
for p in pathlib.Path(root).rglob("*.json"):
yield json.loads(p.read_text())
def phase_seconds(inc):
# detection / acknowledgment 间隔在记录里是直接写好的(秒)
d = inc["detection"]
return {
"mttd": d["alert_to_ack"], # t0 → t1
"mtta": d["ack_to_declare"], # t1 → t2
"mttk": d["declare_to_first_action"], # t2 → t3(近似:首次有效止血动作)
"recovery": (inc["user_impact"]["duration_min"] * 60)
- d["alert_to_ack"] - d["ack_to_declare"] - d["declare_to_first_action"],
}
def summarize(root="incidents"):
buckets = defaultdict(lambda: defaultdict(list))
for inc in load_incidents(root):
ph = phase_seconds(inc)
sev = inc["severity"]
for k, v in ph.items():
buckets[sev][k].append(v)
buckets[sev]["mttk_share"].append(ph["mttk"] / max(sum(ph.values()), 1))
for sev in sorted(buckets):
print(f"\n=== {sev} ===")
for k, vals in buckets[sev].items():
vals = sorted(vals)
p50 = statistics.median(vals)
p90 = vals[int(len(vals) * 0.9) - 1] if len(vals) > 1 else vals[0]
# 报 P50/P90,不报均值:一次 6 小时的异常事故能把均值拉偏一倍
print(f"{k:12s} n={len(vals):3d} P50={p50:7.1f} P90={p90:7.1f}")
if __name__ == "__main__":
summarize()两条使用纪律:
- 按严重度分组,报 P50 和 P90,不报均值。 SEV-1 一年可能只有 3 次,均值的统计意义为零
- 趋势比绝对值重要。 MTTK 从 22 分钟降到 14 分钟,比"我们现在 MTTK 是 14 分钟"更有说服力
10.4 别用 MTTR 考核人
这一点必须单独说,因为它毁掉的团队比它改善的多。
只要 MTTR 进入个人考核,会发生三件必然的事:
- 事故不再被记录。 那些"差不多算事故但也没那么大"的事件会被悄悄处理掉,不做记录。它们的模式会在半年后以更大的规模重现
- 止血动作变得激进。 为了快,选择"重启一下看看"这类不解释的操作,把可诊断性破坏掉。可能出现回滚"成功"了,故障没停
- 根因分析不再深挖。 因为复盘耗时,不如赶紧结单
正确的用法是:MTTR 用来找改进项,不用来评价人。 每个季度看清楚四段里哪一段最长,然后只改那一段——这比全员盯着一个总时间有效得多。
十一、度量:事故管理做得好不好,看这五个数
除 MTTK 之外,还需要几个数来衡量"组织在事故里的表现",因为它们比时间更难被伪造成好看的数。
| 指标 | 定义 | 参考目标 | 为什么是它 |
|---|---|---|---|
| IC 就位率 | 有明确 IC 宣布的事故 / 总事故数 | > 95%(SEV-1/2 必须 100%) | 最容易掉的一个动作,也是其他一切的前提 |
| 决策滞后 | 从"方案提出"到"IC 回应"的时长 P90 | < 3 分钟 | 直接度量决策吞吐,不掺技术因素 |
| 并行操作数 | 同一时刻未经 IC 确认的操作数 | = 0 | 挡住第 1 节那种四方向并行 |
| 升级响应时长 | 从跨团队升级到对方首次回执的时长 P50 | < 10 分钟 | 度量组织间的实际协作能力,而不是文档里写的协作流程 |
| 复发性事故占比 | 与过去 12 个月某次事故根因相同的事故数 / 总事故数 | < 10% | 唯一能反映"复盘是否真的改了东西"的指标 |
第五个指标最值得盯。它挡的是一种很普遍的现象:复盘写得很好,改进项列了 12 条,实际做完 3 条,下次同样的事再来一遍。
实现上它可以全自动:复盘记录里有 root_cause_tag 字段,新事故结单时和过去 12 个月的 tag 做匹配,命中就自动打上 recurring: true。这个字段一旦开始被统计,复盘质量会自己变好——因为每个人在写复盘时都会想"这个 tag 会不会在半年后再次出现"。
十二、二十项事故准备自检清单
这一份对应专栏介绍里那句"W3 上线前 Checklist 的事故准备项并入第 22 篇"。按季度过一遍,20 项:
角色与授权
- IC 有独立排班表,和 on-call 一样轮值,且有备选池
- CL 同样有排班池,且事故期间有对外发言授权
- 每个服务有一份《决策权清单》,写明哪些动作可单独拍板、哪些需要双方确认、哪些必须上报
- 《决策权清单》由技术一号位签字,不是 SRE 自拟
- "IC 压倒性授权"已写进书面约定,且明确"追责只针对没记录,不针对决定的正确性"
- 事故群有一个固定的机器人,能识别固定句式并记录时间线
定级与通知
- 事故定级树已公布,3 分钟内可以走完
- 定级到通知矩阵的映射已配置在值班系统里,是自动触发的
- "15 分钟未缓解自动升级"由计时任务执行,不靠人记
- 升级通知里带事故上下文(编号、现象、群链接、当前 IC),不只是"SEV-1 事故"
- 定级变更必须写理由,且用 CI 或表单校验,不许空
流程与模板
- IC 开场模板、状态更新模板、交接模板 已固化(可直接复制,不用现编)
- 对外状态页有四种模板:首次公告、进展更新、恢复公告、SEV-1 客户一对一版
- 客服话术模板已和客服团队对齐,SEV-1/2 时会主动推送
- 升级请求的四段结构(事实/已做/请求/时限)已在团队内训过至少一次
- 结束判据四条已写进流程(SLI 回基线、改动已记录、无悬挂不可逆操作、公告已收敛)
记录与改进
- 每次 SEV-1/2 事故产出一条结构化记录(YAML/JSON),字段齐(见 9.2)
decisions.rejected字段有在填——记录否决了什么- MTTD / MTTA / MTTK / MTTR-R 按月按级别算 P50 与 P90,不报均值
- 复发性事故占比在季度复盘里被讨论,且
root_cause_tag已落在记录里
十三、从零开始的四阶段路线
阶段一(第 1~2 周):把"谁来管"这件事定下来
不做工具,先改人。
- 定 3~4 个 IC 候选人,明确"IC 不碰键盘"
- 写一份《决策权清单》草稿,让技术一号位签字
- 在应急群里固定一条开场模板(第 3.3 节),要求所有人照着发
- 产出:一条被真的用过的开场模板,和一份签字的清单。只要开场模板被用过三次,事故的形状就会变
这一段成本极低,是整篇里性价比最高的。
阶段二(第 3~6 周):加 CL,加定级
- 从 PM / 值班经理 / 客服主管里选 2~3 个 CL,做一次 1 小时培训(讲四段升级结构、四种模板、能说什么不能说什么)
- 把定级树和通知矩阵配置进值班系统,先只做 SEV-1/2 两档
- 把"15 分钟未缓解自动升级"的计时任务上线
- 产出:SEV-1/2 事故里 CL 100% 到位
这一段的阻力通常来自 CL 的定位——很多人会认为"让 PM 管沟通"是形式主义。解法是给一个可量化的目标:让技术负责人在事故里的被打断次数下降一半。 这个数字一旦被看到,反对就消失了。
阶段三(第 2~3 月):让记录自动生成
- 事故机器人上线:识别固定句式,自动写
timeline - 事故记录模板(9.2 的 schema)落库,SEV-1/2 强制填写
- 四段度量脚本接进月度看板
- 产出:一条能自动生成 70% 内容的事故记录
阶段四(第 4~6 月):进复盘,进训练
- 把复发性事故占比变成季度复盘固定议题
- 每季度一次"只演练指挥"的演练:不发真实故障,只给一个模拟事故,让 IC 从 20:02 演到宣布结束(第 20 篇的 Game Day 有个变体,成本极低,一个下午能跑三轮)
- IC 轮值满一个季度后做一次集体复盘:什么决定最卡、什么信息最难拿到
- 产出:一支能在 3 分钟内就位的 IC 队伍
十四、和前面几篇的关系
- 第 8 篇(on-call 生存指南):那篇是单人的 5 分钟止血法,这一篇是"当 5 分钟止血不够时,组织怎么接上"。第 8 篇里那条群通知模板,就是本文 CL 的雏形,但只做了"通知",没做"挡住问询"
- 第 10 篇(错误预算耗尽):那篇的五步应急是"预算烧穿后的处置流程",本文给它补上"谁是这个流程的指挥"
- 第 13 篇(告警分级):本文第 4 节专门区分了"告警级别"和"事故等级"。前者的字段是
severity,后者的字段建议叫incident_severity,两者混用是落地时最常见的坑 - 第 17 篇(Dashboard 设计):事故时所有人盯的是同一块面板,那块面板就是第 17 篇说的"一块面板一个主角"。本文的看板链接之所以能出现在开场模板里,前提是有那么一块面板
- 第 18 篇(Runbook):Runbook 解决"操作怎么做",本文解决"谁决定做哪个、什么时候做"。两者的接缝在 5.2 节那句"给 OL 一个明确的第一个任务"
- 第 19 篇(变更安全体系):回滚的技术判断在那里,回滚的决策权在这里。两篇合起来才算完整——第 19 篇告诉你"回滚可能救不了你",本文告诉你"谁有权按下那个按钮,以及按错了怎么免责"
- 第 20 篇(混沌工程与 Game Day):Game Day 演练的是 Runbook 和 on-call,本文的 IC 演练是它的变体——只演练指挥不动系统。两者的反差很有价值:Game Day 常常发现打不通的电话,IC 演练常常发现问不到的人
- 第 21 篇(容量规划):容量不足是最常见的事故来源之一,而它的应对(限流、降级、排队)恰好是本文决策清单里最需要 IC 拍板的那几项。限流这件事需要有人担责,而那个人不该是最懂技术的人
- 第 23 篇(数据库 SRE):数据层事故是 IC 最难的一类——不可逆、需要跨团队(DBA / 财务 / 法务)、恢复时长远超技术恢复。那篇把恢复时间拆成七段并给出实测口径,也把"谁有权决定恢复到哪个时间点"这个问题交回本文的决策权清单
- 第 24 篇(AI 排障的真实边界):本文第 9.3 节那个"机器人生成时间线"是 AI 在事故链路里最稳的落点——它不判断,只记录。这条线留给第 24 篇展开
写在最后
如果这篇只带走一句:
事故不是被处理好的,是被组织好的。
技术排查能力决定了下限,指挥能力决定了上限。同一个团队、同一套系统,有没有 IC,98 分钟可能是 35 分钟——差的不是技术,是那 60 多分钟花在互相确认上的时间。
如果只带走一个动作:
下一次事故,在群里发一条开场消息:"我是 IC,这是现状,这是谁在干什么,下一次更新是几点。"
这条消息只需要 1 分钟。但它会立刻改变两件事:所有人知道该看哪儿,以及所有人知道该找谁。这两件事,比任何排查技巧都更早地决定了事故的走向。
本文开头那个 98 分钟的设计里,如果 20:05 有人发了这条开场消息,20:24 就不会回滚错服务——因为"D 要回滚网关"这句话会被看见,而 IC 会说"不是那个,是订单服务"。那 23 分钟,就是本文的全部意义。
本文是 SRE 实战系列第 22 篇,第二循环第二组第一篇。前面二十一篇讲的是"系统和个人的动作",从这一篇开始讲"组织的动作"。下一篇(第 23 篇)转向数据层:数据库 SRE——备份没演练过,就等于没有备份。