运维篇 · 04 · 故障复盘
本章目标
让每次故障成为团队成长的养分,而不是找人背锅的会议。
一、复盘的原则(最关键)
1.1 对事不对人(Blameless Culture)
❌ 反例:
"这个故障是张三写的代码有 Bug 导致的,以后张三要更小心。"
✅ 正例:
"这个 Bug 能溜到线上,说明:代码评审没覆盖到、测试没覆盖到、灰度没发现。 我们需要改进这三个环节的检查清单。"
为什么:
- 任何一个"个人失误"背后都是流程漏洞
- 如果怪人,大家会隐瞒故障,问题反而更严重
- 追责文化会杀死主动改进的意愿
1.2 复盘的目的
✅ 是:
- 找出系统性问题
- 改进流程和工具
- 沉淀知识
- 降低未来故障概率
❌ 不是:
- 找替罪羊
- 考核绩效
- 拿来追溯责任
二、复盘流程
故障结束 ──► 48h 内:初稿 ──► 召开复盘会 ──► 确定改进项 ──► 跟踪执行
2.1 时机
- 24-48 小时内召开:记忆清晰
- 不超过 1 周:否则细节遗忘
- 参与者:IC、Tech Lead、当班人员、相关模块 owner
2.2 复盘会议议程(60 分钟)
| 时间 | 环节 | 产出 |
|---|---|---|
| 5 min | 开场与规则(对事不对人) | - |
| 10 min | 故障时间线(客观还原) | 时间线图 |
| 15 min | 根因分析(5 Why) | 根因清单 |
| 15 min | 改进措施讨论 | Action Item |
| 10 min | 分工与时限 | 责任人 + deadline |
| 5 min | 总结 | 会议纪要 |
2.3 复盘报告模板
# 故障复盘: SEV-1 订单服务不可用 (2026-04-15)
## 摘要
**严重度**: SEV-1
**持续时间**: 1 小时 15 分钟
**影响范围**: 全站下单不可用,影响约 15,000 用户
**业务影响**: 估算损失 ¥50,000
**根本原因**: 数据库连接池配置错误,导致新版本启动后耗尽连接
## 时间线
| 时间 | 事件 | 责任人 |
|------|------|-------|
| 2026-04-15 10:00 | 开始发布 v1.2.1 | @ops |
| 10:15 | 灰度 10% | @ops |
| 10:30 | 监控告警: API 5xx 飙升 | 监控 |
| 10:32 | 值班人确认故障 | @alice |
| 10:35 | 故障群创建,指定 IC | @alice |
| 10:45 | 初步定位到新版本 | @bob |
| 11:00 | 决定回滚 | @ic |
| 11:15 | 回滚完成 | @ops |
| 11:30 | 服务恢复 | - |
| 11:45 | 持续观察稳定,故障结束 | @alice |
## 详细经过
### 发布阶段
v1.2.1 修复了一个支付相关 bug,按流程灰度发布...
### 故障触发
灰度 10% 后,新版本实例的数据库连接池很快打满...
### 处置过程
@bob 首先怀疑是代码 Bug,查看最近变更...
@alice 作为 IC 决定先回滚,后定位根因...
## 根因分析(5 Why)
**问题**: 为什么订单服务不可用?
- **Why 1**: API 5xx 率飙升
- **Why 2**: 数据库连接不可用
- **Why 3**: 连接池耗尽
- **Why 4**: 新版本连接池配置从 50 改成 5
- **Why 5**: CR 时改了默认值,但没人注意到
**根因**: 配置变更**没有专项评审机制**,关键参数变更易被忽视。
## 做得好的地方
- ✅ 监控及时告警(2 分钟内)
- ✅ 值班人响应快(5 分钟内)
- ✅ IC 决策果断(15 分钟决定回滚)
- ✅ 通报节奏好,对外沟通及时
## 做得不好的地方
- ❌ 代码评审漏掉了连接池配置变更
- ❌ 灰度 10% 的观察窗口太短(15 分钟就扩大)
- ❌ 没有针对"配置变更"的自动化检查
- ❌ Runbook 里没有"连接池耗尽"这个场景
## 改进项(Action Items)
| # | 改进项 | 负责人 | 截止日期 | 跟踪 |
|---|-------|-------|---------|------|
| 1 | 关键配置变更加"双人评审"流程 | @tech-lead | 2026-04-22 | 🟡 进行中 |
| 2 | 灰度延长到 30 分钟最小观察 | @ops-lead | 2026-04-20 | ✅ 完成 |
| 3 | 加 CI 检查: 关键配置变更必须 trigger warning | @platform | 2026-05-01 | 🟡 |
| 4 | 补充 Runbook: 数据库连接池耗尽处置 | @alice | 2026-04-18 | ✅ |
| 5 | 加监控: 连接池使用率实时告警 | @bob | 2026-04-20 | 🟡 |
## 度量与教训
### 响应时间
- 发现到确认: 2 min (目标 5 min) ✅
- 确认到决策: 25 min (目标 15 min) ❌ 需改进
- 决策到恢复: 30 min (目标 30 min) ✅
### 关键教训
1. **配置和代码是同等重要的资产** — 要有同样的评审和审计机制
2. **灰度时长不能只看比例** — 时间维度也要考虑
3. **现象→动作→根因 是迭代过程** — 先恢复再定位是对的
三、5 Why 分析法
3.1 核心思路
连续问 5 次"为什么",挖到根本原因。
3.2 示例
问题: 网站打不开
Why 1: 为什么打不开?
→ Web 服务器返回 502
Why 2: 为什么返回 502?
→ 应用进程崩溃
Why 3: 为什么应用进程崩溃?
→ OOM(内存溢出)
Why 4: 为什么 OOM?
→ 某个请求加载了整张百万级大表到内存
Why 5: 为什么会这样?
→ 代码里用了 findAll() 而不是分页查询
根因: 缺少对"无边界查询"的代码检查规则
改进: lint 规则加 findAll() 警告
3.3 5 Why 的常见误区
- 停得太早:停在"Bug"这种结论上
- 只看技术:忽略流程、组织、文化
- 分支太散:没有聚焦到一个主要链路
- 找人背锅:第 5 个 Why 变成"张三不小心"
四、根因分类
4.1 技术类
- 代码 Bug
- 配置错误
- 容量不足
- 依赖故障
- 数据异常
4.2 流程类
- 评审不严
- 测试覆盖不足
- 发布流程漏洞
- 监控告警缺失
- Runbook 不完善
4.3 组织类
- 职责不清
- 沟通不畅
- 知识断层
- 培训不够
- 值班制度漏洞
4.4 文化类
- 追责文化导致隐瞒
- 压力大导致短视
- 追求上线速度牺牲质量
好的复盘应该覆盖所有 4 类,不能只停在技术层。
五、改进项(Action Item)的管理
5.1 AI 的 SMART 原则
每个改进项必须:
- Specific: 具体(不是"加强监控",而是"加 XXX 告警规则")
- Measurable: 可衡量(可以验证是否完成)
- Assignable: 有责任人
- Realistic: 可实现
- Time-bound: 有截止日期
5.2 跟踪机制
- 录入 Issue 系统
- 关联到团队周会
- 每周 review 一次进度
- 逾期升级
5.3 完成率
- 目标:> 90% 按时完成
- 低于 80% 要分析原因
六、知识沉淀
6.1 故障数据库
所有故障复盘报告汇总到一个知识库:
docs/
└── postmortems/
├── README.md ← 索引
├── 2026/
│ ├── 04-15-order-down.md
│ ├── 04-20-payment-timeout.md
│ └── ...
├── 2025/
└── lessons-learned.md ← 经验提炼
6.2 跨故障的模式分析
每季度/每年做一次:
- 故障按类型分布(哪类最多?)
- 根因分布(流程 / 技术 / 组织?)
- MTTR 趋势(有没有在提升?)
- 改进项完成率
- 重复故障情况(是否同类问题反复发生)
6.3 Runbook 更新
每个故障后必须问:
- 这个场景 runbook 里有吗?
- Runbook 里的步骤是否起到了帮助?
- 需要新增 / 更新什么?
七、故障度量指标
7.1 事后指标
| 指标 | 定义 | 目标 |
|---|---|---|
| MTTD | Mean Time To Detect(发现时间) | 短 |
| MTTR | Mean Time To Recover(恢复时间) | 短 |
| MTBF | Mean Time Between Failures | 长 |
| 改进项完成率 | 按期完成 / 总数 | > 90% |
| 重复故障率 | 同类问题再次发生 | < 5% |
7.2 用这些指标做什么
- 看团队趋势(变好还是变差)
- 横向对比(不同服务)
- 决定投资方向(哪里最需要加强)