线上事故在所难免,但同样的故障反复出现才是真正的问题。技术复盘(Postmortem)是团队从故障中学习的核心机制;而「无指责复盘(Blameless Postmortem)」强调对事不对人,把每一次事故变成可被检索、可被复用的团队资产。本文给出一套可直接落地的四步法与模板。
很多团队把复盘开成了「追责会」:谁写的代码、谁没看监控、谁半夜没响应。结果下次没人敢说实话,根因被掩盖,故障换张皮再来一次。复盘的价值从来不是定位「责任人」,而是定位「系统为什么会允许这个错误发生」。
一、为什么大多数复盘最终无效
复盘失效通常有三条主线。其一是甩锅文化:一旦会议变成追责,当事人会本能地隐瞒细节、推卸关联,时间线和根因都被粉饰。其二是没有行动项 or 行动项是口号:写一句「加强监控」「提高意识」,既不责任人、也不验收标准,等于没写。其三是复盘文档写完即归档,躺在 Wiki 里无人检索,半年后同样的坑又踩一遍。
要打破这个循环,关键是把复盘从「人的评价」转向「系统的改进」,并让产物可被机器和消费。
二、无指责复盘的三个核心原则
| 维度 | 传统复盘 | 无指责复盘 |
| 出发点 | 谁犯了错 | 系统为何允许错误发生 |
| 氛围 | 防御、辩解 | 坦诚、心理安全 |
| 产出 | 处罚 / 警示 | 可验证的行动项 + 知识沉淀 |
| 复看 | 一次性归档 | 纳入事故库持续检索 |
无指责不等于无责任。责任人依然要参与复盘,但目的是还原事实而非定罪。Google SRE 的复盘文化核心就是:把「人」从因果链里摘出来,问「在什么条件下,任何理性的人都会做出同样的选择」,从而改掉那个条件本身。
三、可落地的复盘四步法
1. 还原时间线(Timeline)
第一时间拉出客观时间线:告警何时触发、谁先响应、做了哪些操作、何时恢复。时间线只写事实,不写判断。推荐使用统一时区(UTC 或统一的本地时区),并把监控截图、日志链接附上,避免事后记忆偏差。
2. 定位根因(5 Whys / 故障树)
用「连续追问五个为什么」逼近根因,或用故障树把触发条件逐层展开。注意:根因应当是「可以消除的条件」,而不是「某人疏忽」。例如根因写成「发布流程缺少灰度开关」比「张三没灰度」更有建设性。
3. 提炼可验证的行动项(Action Items)
每条行动项必须满足:有唯一负责人、有截止时间、有可验收的产出(如「为订单服务上线自动回滚脚本」)。禁止出现「加强」「注意」这类无法验收的词。行动项要进入跟踪系统,并在下个迭代复盘时回看是否关闭。
4. 沉淀为组织记忆
把复盘文档标准化后写入事故库,打上标签(服务 / 故障类型 / 严重级),让后来者能搜到。最好的状态是:新人入职先读一遍事故库,等于经历了团队三年踩过的坑。
四、一份可直接套用的复盘模板
下面是一份 Markdown 复盘骨架,复制即可用。把每次事故都填进同一个结构,长期积累就是团队最值钱的知识资产。
# 事故复盘:<一句话标题>
## 元数据
- 发生时间(UTC):
- 恢复时间(UTC):
- 影响范围:<用户/接口/区域>
- 严重级别:SEV1 / SEV2 / SEV3
- 值班 / 响应人:
## 时间线(只写事实)
| 时间(UTC) | 事件 | 来源 |
|-----------|------|------|
| 02:14 | 告警:订单错误率突增 | Prometheus |
| 02:21 | 定位到缓存批量失效 | 日志 |
| 02:38 | 切流 + 预热缓存,恢复 | 操作记录 |
## 根因分析
- 直接原因:
- 深层原因(5 Whys):
1. 为什么?
2. 为什么?
- 故障树要点:
## 行动项(必须可验证)
- [ ] <负责人> <截止日> <可验收产出>
- [ ] <负责人> <截止日> <可验收产出>
## 经验沉淀
- 这次学到什么?
- 哪些监控 / 预案应当提前建设?
五、把复盘自动化:用 CI 自动开复盘工单
事故响应一忙起来,复盘常被忘。可以约定:任何 SEV1/SEV2 告警触发后,由 CI 自动创建一篇复盘 Issue 骨架,强制跟进。下面是一段 GitHub Actions 工作流片段,在手动触发或告警 webhook 后自动开单。
name: open-postmortem
on:
workflow_dispatch:
inputs:
title:
description: "事故标题"
required: true
sev:
description: "严重级别 SEV1/SEV2/SEV3"
required: true
jobs:
open:
runs-on: ubuntu-latest
steps:
- name: 创建复盘 Issue 骨架
uses: actions/github-script@v7
with:
script: |
const title = `复盘: ${{ inputs.title }} [${{ inputs.sev }}]`;
await github.rest.issues.create({
owner: context.repo.owner,
repo: context.repo.repo,
title,
body: "## 时间线\n| 时间 | 事件 | 来源 |\n|---|---|---|\n\n## 根因\n\n## 行动项\n- [ ] ",
labels: ["postmortem", "${{ inputs.sev }}"]
});
六、五个常见的复盘反模式
| 反模式 | 表现 | 后果 |
| 甩锅会 | 聚焦谁的责任 | 信息失真、隐瞒 |
| 口号式行动项 | 加强监控 / 提高意识 | 无法验收,原样复发 |
| 一次性归档 | 写完即沉底 | 知识不可检索 |
| 只谈技术不谈流程 | 忽视组织/流程缺陷 | 根因未消除 |
| 无跟进回看 | 行动项无人盯 | 改进落空 |
七、案例:一次 swap 抖动引发的连锁反应
我们曾遇到一台 ECS 因内存 swap 导致响应骤降,上游超时雪崩,最终牵连多个依赖服务。复盘没有去追责谁没看内存曲线,而是把根因定位到「缺乏 swap 水位告警 + 缺少自动限流」,并落下了可验证的行动项。完整的排查过程见《内存 swap 导致性能骤降排查实战》。
类似的,Nginx 504 超时往往不是 Nginx 本身的问题,而是后端处理时间超过代理超时阈值,排查链路见《Nginx 504 Gateway Timeout 排查实战》;而 502 多半与上游进程崩溃或连接池耗尽相关,可参考《Nginx 502 排查》。把每篇踩坑文都沉淀进事故库,复盘时就能快速关联同类根因。
八、复盘不是终点:建立团队的组织记忆
单个复盘的价值有限,持续运转的复盘机制才有复利。建议每周固定一个「事故回顾」短会,回看未关闭的行动项,并把新复盘链接进《GitHub Actions 自动化》这类基础设施文章,让知识与工具互相引用。再配合死锁、资源争用等底层机理的理解(如《死锁排查实战》),团队对故障的「肌肉记忆」会越来越强。
技术复盘的本质,是把一次痛苦的经历,翻译成系统不会重蹈覆辙的护栏。无指责让真话浮出来,四步法让改进可验证,自动化让机制不依赖个人自觉。当你的事故库厚到新人读一遍就能避坑时,复盘就真正变成了团队资产。




