概述
凌晨两点,你被电话吵醒。监控大屏一片红,核心交易链路 P99 延迟飙到 8 秒,上游服务开始超时熔断,客服群里用户截图已经刷屏了。你一边远程连 VPN,一边脑子里飞速转——这场景上次演练时见过吗?预案里有没有覆盖?切换步骤还记得吗?
如果你这时候还在翻 wiki 找文档,那说明一件事:你的预案只是写了,没练过。
故障预案不是写完就完事的文档。它是一套需要反复演练、不断修正的应急肌肉记忆。就像消防队不会只在纸上画逃生路线——他们会点真火,拉真警报,让人在浓烟里跑。SRE 的故障演练也是同一个道理:不逼团队在接近真实的故障场景里做决策,到了真正出事的时候,你永远不知道谁会卡壳。
这篇文聊聊怎么把故障预案从"写给别人看的文档"变成"团队真正能执行的作战手册",以及怎么设计演练体系让团队保持手感。
故障预案的本质:不是文档,是决策树
预案要解决什么问题
很多人把故障预案理解成一份操作手册——“如果 A 挂了,执行步骤 1-2-3”。这没错,但太浅了。真正有用的预案是一棵决策树,帮值班人员在高压环境下快速做对三件事:
- 判断故障等级——这事值不值得半夜叫人?叫到哪一级?
- 选择止损路径——先切流量、先回滚、还是先扩容?
- 确定沟通节奏——谁对外发声、多久同步一次、什么时候升级
我见过太多预案写得像产品说明书,事无巨细地列了 50 个步骤,值班同学在故障现场根本来不及看。好的预案应该短、狠、准——能在 30 秒内定位到对应的处置方案,3 分钟内开始执行止损。
预案体系的三层结构
| 层级 | 内容 | 目标读者 | 更新频率 |
|---|---|---|---|
| L1 应急卡片 | 单服务故障的快速处置步骤(≤10 步) | 值班 On-Call | 每次演练后 |
| L2 灾备预案 | 跨服务故障的切换方案与回滚流程 | SRE 团队 | 每季度 |
| L3 业务连续性预案 | 机房级故障的全面接管方案 | SRE + 业务方 | 每半年 |
L1 应急卡片是日常用得最多的。它不是 wiki 上的长文,而是一张可以打印出来贴在工位上的卡片。格式很简单:
# [服务名] 应急卡片
## 故障特征
- 核心指标:P99 延迟 > 500ms 或 错误率 > 1%
- 典型告警:service_latency_p99_critical / service_error_rate_high
## 快速止血(按优先级)
1. 确认是否正在发布 → 是则立即回滚
2. 检查下游依赖状态 → [依赖面板链接]
3. 切换流量到备用集群 → [切换脚本链接]
4. 通知业务方降级非核心功能 → [降级开关列表]
## 升级条件
- 5 分钟内未止血 → 升级到 SRE Lead
- 影响交易链路 → 立即升级到 P0 事故
## 联系人
- 服务负责人:@xxx
- DBA:@yyy
- 业务方:@zzz
这张卡片的核心设计原则是:每一步都是可执行的原子操作,不需要思考。值班同学拿到卡片,从头往下做就行。
预案的生命周期管理
预案不是写完就完了。它有自己的生命周期:
编写 → 评审 → 演练验证 → 修正 → 归档 → 定期复审 → 更新 → 再演练
最关键的一环是演练验证。没经过演练的预案,等于没有预案。我在实际工作中遇到过好几次——预案写得漂漂亮亮,真演练的时候发现切换脚本根本跑不通,备份的数据是三个月前的,灾备集群的证书早过期了。这些问题不演练根本发现不了。
演练体系设计:三种模式,三个层次
演练模式对比
| 模式 | 成本 | 真实度 | 风险 | 适用场景 |
|---|---|---|---|---|
| 桌面推演 | 低 | 低 | 零风险 | 预案评审、新成员培训 |
| 红蓝对抗 | 中 | 中 | 可控 | 流程验证、团队协作 |
| 混沌注入 | 高 | 高 | 较高 | 系统韧性验证、自动化兜底 |
三种模式不是替代关系,而是递进关系。新预案先用桌面推演过一遍逻辑,再用红蓝对抗验证流程,最后用混沌注入检验系统真实的抗压能力。
桌面推演:低成本的逻辑验证
桌面推演就是一群人坐在一起,主持人抛场景,参与者按预案描述自己会做什么。听起来很 low,但实际上是性价比最高的演练方式。
适合场景:
- 新预案刚写完,检查逻辑有没有漏洞
- 新成员入职,快速了解应急流程
- 组织架构调整后,确认角色分工是否还成立
操作方法:
主持人准备 3-5 个故障场景,每个场景包含触发条件、影响范围和约束条件。参与者不碰真实系统,只在白板或共享文档上描述自己的操作步骤。
场景示例:
- 触发条件:周五 17:30,订单服务 P99 延迟从 80ms 飙到 2s
- 影响范围:下单接口超时率 30%,支付回调延迟
- 约束条件:不能全量切流(备用集群容量只够 50%)
- 附加干扰:值班同学正在处理另一个 P2 告警
桌面推演的核心价值不在操作本身,而在暴露预案的盲区。比如:
- 两个服务同时出故障,先救谁?
- 回滚需要 DBA 审批,但 DBA 下班了怎么办?
- 降级开关的业务影响谁来评估?
这些问题在纸面上不容易发现,坐在一起走一遍流程就全暴露了。
红蓝对抗:中程实战
红蓝对抗比桌面推演更接近真实。红队负责制造故障(在测试环境),蓝队按照预案处置。全程计时,观察蓝队的响应速度和决策质量。
适合场景:
- 灾备切换流程验证
- 多团队协作的故障响应
- 值班同学的技能考核
关键设计要点:
环境隔离:必须在独立的测试环境进行,绝不能碰生产。如果资源不够,至少用一个隔离的 namespace 模拟。
故障注入可控:红队注入的故障必须能快速恢复。比如 kill 进程、网络延迟、磁盘填满——这些都能秒级回滚。别搞删库这种不可逆操作。
全程记录:蓝队的每个操作、每次沟通、每个决策都要有时间戳记录。演练结束后用这些数据算 MTTR 各阶段耗时。
#!/usr/bin/env python3
"""
红蓝对抗演练记录器
记录蓝队每个操作的时间戳,用于事后分析 MTTR 各阶段耗时
"""
import json
import time
from datetime import datetime
from pathlib import Path
class DrillRecorder:
def __init__(self, scenario_name: str):
self.scenario = scenario_name
self.start_time = time.time()
self.events = []
self.phases = {
"detect": None, # 故障发现时间
"acknowledge": None, # 值班响应时间
"diagnose": None, # 定位原因时间
"mitigate": None, # 止损执行时间
"resolve": None, # 完全恢复时间
}
def log(self, phase: str, action: str, operator: str, detail: str = ""):
"""记录一个演练事件"""
elapsed = round(time.time() - self.start_time, 1)
event = {
"timestamp": datetime.now().isoformat(),
"elapsed_sec": elapsed,
"phase": phase,
"action": action,
"operator": operator,
"detail": detail,
}
self.events.append(event)
if phase in self.phases and self.phases[phase] is None:
self.phases[phase] = elapsed
print(f"[{elapsed:>7.1f}s] [{phase}] {operator}: {action}")
if detail:
print(f" └─ {detail}")
def summary(self) -> dict:
"""生成演练复盘报告"""
report = {
"scenario": self.scenario,
"total_mttr": round(time.time() - self.start_time, 1),
"phases": {},
"events_count": len(self.events),
}
prev = 0
for phase, t in self.phases.items():
if t is not None:
report["phases"][f"{phase}_elapsed"] = t
report["phases"][f"{phase}_delta"] = round(t - prev, 1)
prev = t
return report
def save(self, path: str = None):
"""保存演练记录到文件"""
if path is None:
path = f"drill-{self.scenario}-{int(self.start_time)}.json"
Path(path).write_text(
json.dumps({"summary": self.summary(), "events": self.events},
ensure_ascii=False, indent=2)
)
print(f"\n演练记录已保存: {path}")
# 使用示例
if __name__ == "__main__":
drill = DrillRecorder("订单服务延迟飙升")
drill.log("detect", "告警触发", "Prometheus", "P99延迟 2.1s 超过阈值 500ms")
drill.log("acknowledge", "值班响应", "oncall-zhang", "确认告警,开始排查")
drill.log("diagnose", "定位原因", "oncall-zhang", "Redis 连接池耗尽导致缓存命中率下降")
drill.log("mitigate", "执行止损", "oncall-zhang", "重启 Redis 从节点,切换读流量")
drill.log("resolve", "完全恢复", "oncall-zhang", "P99 恢复至 85ms")
drill.save()
这套记录器的核心价值是把 MTTR 拆成五个阶段——发现、响应、定位、止损、恢复——每个阶段单独计时。演练复盘时,你一看就知道团队卡在哪个环节。
混沌注入:生产环境的终极考验
混沌注入是最硬核的演练方式——直接在生产环境注入故障,看系统和团队能不能扛住。听起来疯狂,但 Google、Netflix 这些公司早就这么干了。Netflix 的 Chaos Monkey 每天随机杀生产环境的实例,已经跑了十几年。
为什么要在生产环境做:
测试环境的流量模式、数据量、网络拓扑和生产差太远。你在测试环境演练一百次,不如在生产环境跑一次暴露的问题多。当然,前提是你的系统已经有足够的高可用保障——自动故障转移、弹性伸缩、熔断降级这些机制得先到位。
主流混沌工程工具对比:
| 工具 | 维护方 | 故障类型 | K8s 集成 | 学习成本 |
|---|---|---|---|---|
| Chaos Mesh | CNCF | 网络/POD/IO/时间/内核 | 原生 CRD | 中 |
| ChaosBlade | 阿里 | CPU/网络/磁盘/进程/JVM | Operator | 中 |
| Litmus | CNCF | 网络/POD/IO | 原生 CRD | 高 |
| Pumba | 开源 | 网络/POD | Docker 级 | 低 |
我个人推荐 Chaos Mesh。原因很简单:CNCF 毕业、社区活跃、CRD 原生集成、Dashboard 可视化。你在 K8s 里创建一个 Chaos 资源就像创建一个 Deployment 一样自然。
Chaos Mesh 故障注入示例:
# 网络延迟注入:给订单服务的 Pod 注入 200ms 网络延迟
apiVersion: chaos-mesh.org/v1alpha1
kind: NetworkChaos
metadata:
name: order-service-network-delay
namespace: chaos-testing
spec:
action: delay # 故障类型:延迟
mode: all # 影响所有匹配的 Pod
selector:
namespaces:
- production
labelSelectors:
"app.kubernetes.io/name": "order-service"
delay:
latency: "200ms" # 延迟 200ms
correlation: "0" # 延迟不相关性
jitter: "50ms" # 抖动 50ms,模拟真实网络
direction: to # 出站流量
target:
selector:
namespaces:
- production
labelSelectors:
"app.kubernetes.io/name": "payment-service"
mode: all
duration: "5m" # 持续 5 分钟
scheduler:
cron: "@once"
# Pod 故障注入:随机 kill 订单服务的 30% Pod
apiVersion: chaos-mesh.org/v1alpha1
kind: PodChaos
metadata:
name: order-service-pod-kill
namespace: chaos-testing
spec:
action: pod-kill # 故障类型:杀 Pod
mode: fixed-percent # 按比例
value: "30" # 30% 的 Pod
selector:
namespaces:
- production
labelSelectors:
"app.kubernetes.io/name": "order-service"
duration: "0" # 立即执行
scheduler:
cron: "@once"
混沌演练的安全边界:
生产环境搞混沌工程,最怕的是玩脱了。设好安全边界:
爆炸半径控制:从小范围开始。先杀 1 个 Pod,确认自动恢复正常,再逐步扩大到 5%、10%、30%。
自动熔断机制:设定硬性指标——比如错误率超过 2% 或延迟超过 800ms,自动停止演练并恢复。
时间窗口:选在业务低峰期。别在双十一前夜搞混沌演练,这不是勇敢,是莽撞。
一键回滚:所有注入的故障必须能一键清除。Chaos Mesh 的
duration字段就是兜底——到时间自动恢复。但你还得准备手动兜底方案。
#!/bin/bash
# 混沌演练一键中止脚本
# 用法:./chaos-abort.sh
set -euo pipefail
NAMESPACE="chaos-testing"
DRY_RUN="${1:-false}"
echo "=== 混沌演练紧急中止 ==="
echo "时间: $(date '+%Y-%m-%d %H:%M:%S')"
echo ""
# 列出所有正在执行的混沌实验
EXPERIMENTS=$(kubectl get networkchaos,podchaos,iochaos,stresschaos \
-n "$NAMESPACE" \
-o custom-columns=NAME:.metadata.name,KIND:.kind \
--no-headers 2>/dev/null || true)
if [ -z "$EXPERIMENTS" ]; then
echo "当前没有正在执行的混沌实验。"
exit 0
fi
echo "正在执行的混沌实验:"
echo "$EXPERIMENTS"
echo ""
if [ "$DRY_RUN" = "true" ]; then
echo "[DRY-RUN] 将删除以上所有混沌实验资源。"
exit 0
fi
# 删除所有混沌实验资源
kubectl delete networkchaos,podchaos,iochaos,stresschaos \
-n "$NAMESPACE" --all
echo ""
echo "所有混沌实验已清除。等待 30 秒让系统自动恢复..."
sleep 30
echo "=== 系统状态检查 ==="
kubectl get pods -n production --field-selector=status.phase!=Running
echo ""
echo "如果以上有异常 Pod,请手动检查。"
MTTR 分解:用数据驱动演练改进
MTTR 的五个阶段
MTTR(Mean Time To Recovery)不是一个大数字,而是五个阶段的组合。拆开看才知道哪里是瓶颈:
| 阶段 | 含义 | 典型瓶颈 | 改进手段 |
|---|---|---|---|
| MTTD | 平均发现时间 | 告警延迟、漏报 | 优化告警规则、补全覆盖 |
| MTTA | 平均响应时间 | On-Call 响应慢 | 通知渠道优化、值班排班 |
| MTTD-i | 平均定位时间 | 排查没有方向 | 可观测性建设、诊断工具 |
| MTTF | 平均止损时间 | 止损操作复杂 | 自动化切换脚本、降级开关 |
| MTTR-v | 平均验证时间 | 不敢确认恢复 | 自动化健康检查、全链路压测 |
故障发生 ─→ 发现 ─→ 响应 ─→ 定位 ─→ 止损 ─→ 验证 ─→ 恢复
MTTD MTTA MTTD-i MTTF MTTR-v
|_______________________ MTTR ___________________________|
用演练数据定位瓶颈
每次演练后,用前面那套记录器产出的数据做分析。举个例子:
演练场景:订单服务延迟飙升
───────────────────────────────────
发现(detect): 12.3s ✓ 告警及时
响应(acknowledge): 38.7s ⚠ On-Call 响应偏慢(在处理另一个告警)
定位(diagnose): 156.2s ✗ 花了2分钟才确认是Redis问题
止损(mitigate): 203.1s ⚠ 手动切换耗时过长
验证(resolve): 45.0s ✓ 自动化健康检查有效
───────────────────────────────────
总 MTTR: 455.3s (7.6分钟)
这个数据一眼就能看出——定位阶段是最大瓶颈,花了快 3 分钟才找到根因。下一步改进方向就是加强 Redis 的监控覆盖和告警关联,让值班同学能在 30 秒内看到"Redis 连接池耗尽"这个信息。
MTTR 优化策略矩阵
| 瓶颈阶段 | 优化方向 | 具体措施 | 预期效果 |
|---|---|---|---|
| MTTD | 告警覆盖 | 补充业务 SLI 告警、用户体感监控 | 发现时间 < 30s |
| MTTA | 通知机制 | 多渠道通知(电话+IM+短信)、值班备份机制 | 响应时间 < 60s |
| MTTD-i | 诊断工具 | 自动化根因分析、告警关联、依赖拓扑 | 定位时间 < 120s |
| MTTF | 自动化止损 | 一键切换脚本、自动回滚、降级开关 | 止损时间 < 60s |
| MTTR-v | 自动验证 | 全链路探活、业务指标回归 | 验证时间 < 30s |
理论上的 MTTR 目标是把每个阶段都压到分钟级。但实际上不同故障类型的天花板不同——数据库主从切换的止损时间不可能和重启一个 Pod 一样快。关键是对每种故障类型设定合理的 MTTR 目标,然后持续逼近。
演练文化建设:让团队从"怕出事"到"敢演练"
最大障碍不是技术,是心理
推行故障演练时,最常见的阻力不是技术问题,而是团队的心理障碍:
- 开发团队:“生产环境搞演练?出了事谁负责?”
- 业务团队:“演练影响线上用户怎么办?”
- 管理层:“有必要吗?我们又不是 Google。”
这些担忧都是合理的。应对方式不是讲大道理,而是用数据说话——先在测试环境做小规模演练,拿到 MTTR 数据后展示改进效果,逐步建立信心。
渐进式推行路线
| 阶段 | 时间 | 目标 | 做法 |
|---|---|---|---|
| 第一阶段 | 1-2 月 | 建立基础 | 桌面推演 + 测试环境红蓝对抗 |
| 第二阶段 | 3-4 月 | 流程跑通 | 生产环境读流量验证、自动止损演练 |
| 第三阶段 | 5-6 月 | 常态化 | 定期混沌注入、Game Day |
| 第四阶段 | 7-12 月 | 自动化 | 混沌工程平台化、无人值守演练 |
第一阶段别碰生产环境。先把桌面推演和测试环境演练做扎实,把预案的漏洞补完,把团队的协作流程磨合好。到了第二阶段,可以开始在生产环境做一些"温和"的演练——比如验证只读副本能不能扛住流量、自动扩容能不能及时触发。第三阶段才是真正的混沌注入,但爆炸半径一定要控制好。
Game Day:把演练变成团队习惯
Game Day 是 Netflix 发明的概念——定期组织一次集中的故障演练日,全团队参与,模拟多故障并发场景。
Game Day 设计要点:
场景设计要狠:别只搞单点故障。设计多故障并发的场景——“数据库主节点挂了 + 网络分区 + 值班同学在开会”,这种复合场景才能真正考验团队。
观察员机制:安排专人做观察员,不参与处置,只记录团队的行为——谁先发现的问题、谁做了关键决策、沟通链路有没有断。
无指责复盘:演练复盘的核心不是"谁做错了",而是"系统哪里设计得不够好,让人容易做错"。这一点必须从文化上确立。
改进项跟踪:每次演练产出的改进项必须有 owner 和 deadline,下次演练前先检查上次改进项的完成情况。
# Game Day 复盘模板
## 基本信息
- 日期:2026-07-16
- 场景:订单服务 + 支付服务双故障
- 参与人员:8 人(蓝队 5 人 + 红队 2 人 + 观察员 1 人)
- 总耗时:47 分钟
## MTTR 分解
| 阶段 | 耗时 | 评价 | 备注 |
|------|------|------|------|
| 发现 | 15s | ✓ | 告警及时 |
| 响应 | 52s | ✓ | On-Call 30s 内上线 |
| 定位 | 8min | ⚠ | 同时排查两个服务,缺乏优先级判断 |
| 止损 | 12min | ✗ | 手动切换脚本失败,改用备用方案 |
| 验证 | 3min | ✓ | 自动化探活有效 |
| **总 MTTR** | **24min** | **需改进** | 目标 < 15min |
## 发现的问题
1. [P0] 切换脚本 `switch-traffic-v2.sh` 在新环境未验证,缺少依赖
- Owner:@devops-li / Deadline:2026-07-23
2. [P1] 双故障场景没有明确的优先级判断标准
- Owner:@sre-lead / Deadline:2026-07-20
3. [P2] 通讯链路过长:值班 → SRE Lead → 业务方 → 管理层,4 级传递耗时 5 分钟
- Owner:@sre-lead / Deadline:2026-07-30
## 做得好的地方
- On-Call 响应迅速,30 秒内确认告警
- 自动化探活脚本有效,止损后 3 分钟内确认恢复
- 观察员记录详实,复盘有据可查
预案与演练的工程化管理
预案即代码
预案不应该散落在 wiki、Confluence 或个人笔记里。它应该是结构化的、版本化的、可执行的。
推荐用 Git 仓库管理预案,目录结构如下:
incident-playbook/
├── README.md
├── L1-cards/ # 应急卡片
│ ├── order-service.md
│ ├── payment-service.md
│ └── redis-cluster.md
├── L2-dr/ # 灾备预案
│ ├── multi-region-failover.md
│ └── database-disaster-recovery.md
├── L3-bcp/ # 业务连续性预案
│ ├── datacenter-loss.md
│ └── ransomware-response.md
├── drill-records/ # 演练记录
│ ├── 2026-07-16-gameday.md
│ └── 2026-07-01-desktop-drill.md
└── scripts/ # 自动化脚本
├── traffic-switch.sh
├── chaos-abort.sh
└── health-check.sh
每次演练后提交 PR 更新预案,就像维护代码一样。这样你就有完整的变更历史——谁在什么时候改了什么、为什么改。
自动化演练调度
当团队规模和系统复杂度到了一定阶段,手动组织演练的成本会很高。可以考虑用 CI/CD 流水线自动触发演练:
# GitLab CI 配置示例:每周自动执行一次小范围混沌实验
chaos-drill-weekly:
stage: test
image: bitnami/kubectl:latest
schedule:
- cron: "0 2 * * 1" # 每周一凌晨 2 点
script:
- # 1. 确认系统健康基线
- kubectl get pods -n production --field-selector=status.phase!=Running | tee /tmp/unhealthy-pods.txt
- if [ -s /tmp/unhealthy-pods.txt ]; then echo "系统存在不健康 Pod,取消演练"; exit 1; fi
- # 2. 注入小范围故障(杀 1 个非核心 Pod)
- kubectl apply -f chaos-experiments/low-risk/pod-kill-non-critical.yaml
- # 3. 等待 5 分钟
- sleep 300
- # 4. 检查自动恢复
- kubectl get pods -n production --field-selector=status.phase!=Running | tee /tmp/post-drill.txt
- if [ -s /tmp/post-drill.txt ]; then echo "演练后存在异常 Pod,需要人工介入"; exit 1; fi
- # 5. 清理混沌实验
- kubectl delete -f chaos-experiments/low-risk/pod-kill-non-critical.yaml
- # 6. 发送演练报告
- |
cat << EOF | mail -s "[混沌演练] 周例行报告" sre-team@company.com
演练时间:$(date)
演练内容:随机杀 1 个非核心 Pod
结果:通过(自动恢复正常)
下一轮计划:周五 Game Day 全场景演练
EOF
only:
- schedules
这种自动化演练适合低风险的验证场景——比如确认 Pod 自动调度是否正常、HPA 是否能及时扩容。高风险的混沌实验还是得人工组织。
常见误区与避坑指南
误区一:演练就是搞破坏
很多人把混沌演练理解为"在生产环境搞破坏"。这是误解。混沌工程的核心理念不是破坏,而是验证系统是否如你预期的那样具备恢复能力。
如果你注入一个故障,系统没自动恢复——这说明你的高可用机制有漏洞,这是有价值发现。如果你注入一个故障,系统自动恢复了——这验证了你的设计是有效的,同样有价值。两种结果都是收获。
怕的是什么都不做,等到真正的故障来了一脸懵。
误区二:预案越详细越好
见过一些团队写的预案,一个服务的应急预案写了 30 页 Word。从背景介绍到架构图到每条 SQL 怎么写,面面俱到。
这种预案在故障现场根本没用。值班同学在高压环境下,注意力窗口极短。你给他 30 页文档,他一行都看不进去。
好的预案应该遵循"10 步以内"原则——核心操作步骤不超过 10 步,每步不超过 2 行。如果超过,说明这个预案太复杂,需要拆分或自动化。
误区三:只演练不改进
演练的价值不在演练本身,而在演练后的改进。每次演练必须产出明确的改进项,有 owner、有 deadline、有验收标准。
我见过一些团队每月搞演练,但每次发现的问题都一样——切换脚本还是跑不通、告警还是不够关联、值班同学还是不知道该找谁。这说明演练变成了走过场。
改进项跟踪可以用很简单的方式做:
## 改进项看板
### 待处理
| ID | 优先级 | 描述 | Owner | Deadline | 状态 |
|----|--------|------|-------|----------|------|
| 001 | P0 | 切换脚本 v2 在新环境未验证 | @devops-li | 07-23 | 进行中 |
| 002 | P1 | 双故障优先级判断标准缺失 | @sre-lead | 07-20 | 待开始 |
### 已完成
| ID | 优先级 | 描述 | Owner | 完成日期 | 验证方式 |
|----|--------|------|-------|----------|---------|
| 000 | P0 | Redis 连接池监控告警缺失 | @sre-wang | 07-10 | 07-15 演练验证 |
误区四:只搞技术不搞沟通
故障演练最容易忽视的是沟通环节。技术问题好定位,沟通问题才是真正的隐性成本——“值班同学花了 5 分钟才找到业务方的联系方式”、“升级流程不明确,SRE Lead 不确定要不要通知管理层”。
这些沟通问题在桌面推演阶段就应该暴露并解决。预案里必须包含明确的沟通矩阵:
P0 故障沟通矩阵:
├── 0-1min: On-Call 确认故障 → 通知 SRE Lead
├── 1-3min: SRE Lead 评估影响 → 通知业务方负责人
├── 3-5min: 业务方评估用户影响 → 决定是否通知管理层
├── 5-15min: 每 5 分钟同步进展 → 发到事故群
└── 恢复后: 24h 内提交事故复盘报告
总结
故障预案和演练体系是 SRE 团队的核心竞争力之一。记住几个关键点:
预案层面,三层结构(应急卡片/灾备预案/业务连续性预案)覆盖从单服务到机房级故障。每张应急卡片控制在 10 步以内,让值班同学不用思考就能执行。预案用 Git 管理,版本化、可追溯。
演练层面,三种模式(桌面推演/红蓝对抗/混沌注入)递进使用。桌面推演验证逻辑、红蓝对抗验证流程、混沌注入验证系统韧性。每次演练都用记录器分解 MTTR 五阶段,用数据驱动改进。
文化层面,渐进式推行——先测试环境、后生产环境,先小范围、后大范围。Game Day 定期组织,无指责复盘,改进项有 owner 有 deadline。
自动化层面,低风险演练用 CI/CD 调度自动执行,高风险演练人工组织但流程标准化。预案即代码,脚本即工具,一切可版本化、可回溯。
最后一句话:不演练的预案等于没有预案。别等到凌晨两点出事的时候才发现你的预案是废纸。
参考资料与致谢
本文在撰写过程中参考了以下资料,感谢原作者的贡献:
- 企业级全链路SRE稳定性工程体系建设方案 — 腾讯云开发者社区,提供了 SRE 韧性工程体系的全景视角和故障全生命周期管理框架
- 技术决策没有正确解,只有可承担的代价:用27次SRE故障复盘反推职业选择逻辑 — CSDN,提供了美图公司故障全生命周期管理实践和 MTTR 细分指标体系
- 混沌工程在核心业务系统中的实施与容错验证 — Book118,提供了 ChaosBlade 工具的实战落地经验和混沌工程文化推行方法
- K3s - 轻量级 Kubernetes — Rancher 官方文档,混沌工程工具选型对比参考