应急预案与故障演练
应急预案与故障演练
核心目标:在故障真正发生之前,通过预案体系和实战演练,让团队具备”肌肉记忆”级的应急响应能力,做到”平时多流汗,战时少出血”。
概述
应急预案(Incident Response Plan)是预定义的故障处理流程和角色分工体系;故障演练(Incident Drill/Game Day)是通过模拟真实故障场景,验证预案有效性、锻炼团队响应能力的实践方法。两者构成”准备 → 验证 → 改进”的闭环。
flowchart TB
subgraph 预案体系
A[故障场景库] --> B[应急预案模板]
B --> C[角色分工矩阵]
C --> D[升级流程]
D --> E[沟通模板]
end
subgraph 演练体系
F[演练计划] --> G[场景设计]
G --> H[执行演练]
H --> I[观察记录]
I --> J[复盘改进]
end
E --> F
J --> A
应急预案体系
1. 故障场景库
按故障域分类建立场景库,每个场景定义触发条件、影响范围、预案编号。
| 故障域 | 场景 | 严重度 | 预案编号 |
|---|---|---|---|
| 网络 | DNS 解析故障 | SEV2 | IR-NET-001 |
| 网络 | 核心交换机故障 | SEV1 | IR-NET-002 |
| 计算 | K8s 控制面不可用 | SEV1 | IR-K8S-001 |
| 计算 | 节点池大规模离线 | SEV1 | IR-K8S-002 |
| 存储 | PV 不可挂载 | SEV2 | IR-STO-001 |
| 存储 | 数据库主节点宕机 | SEV1 | IR-DB-001 |
| 应用 | 核心API 5xx率飙升 | SEV1 | IR-APP-001 |
| 应用 | 消息队列积压 | SEV2 | IR-MQ-001 |
| 安全 | DDoS 攻击 | SEV1 | IR-SEC-001 |
| 安全 | 数据泄露 | SEV0 | IR-SEC-002 |
| 第三方 | 云厂商Region故障 | SEV1 | IR-CLOUD-001 |
| 变更 | 发布导致线上故障 | SEV1 | IR-CHG-001 |
2. 应急预案模板
# 应急预案: IR-DB-001 — 数据库主节点宕机
## 基本信息
- **预案编号**: IR-DB-001
- **故障级别**: SEV1
- **故障域**: 存储/数据库
- **影响范围**: 全部依赖该DB的服务
- **RTO目标**: 5分钟
- **RPO目标**: 0(零数据丢失)
## 触发条件(任一满足)
1. MySQL 主节点健康检查连续失败 3 次
2. VIP 漂移失败
3. 应用层报 "Connection refused" 错误率 > 50%
## 角色分工
| 角色 | 负责人 | 职责 | 联系方式 |
|------|--------|------|---------|
| IC (指挥) | 值班SRE | 统筹协调、决策 | 电话/钉钉 |
| DBA | DBA值班 | 执行故障切换 | 电话/钉钉 |
| Dev | 服务Owner | 应用层降级/重试 | 电话/钉钉 |
| Comms | 技术支持 | 内/外部沟通 | 电话/钉钉 |
## 响应流程
### T+0: 检测确认
- [ ] 确认告警有效性(排除误报)
- [ ] 初始定级(SEV1)
- [ ] 拉起应急群
### T+1min: 初步响应
- [ ] IC 到岗,接管指挥权
- [ ] DBA 确认主节点状态
- [ ] 判断是否需要故障切换
### T+2min: 执行故障切换
- [ ] 确认备节点数据同步状态(Seconds_Behind_Master < 5)
- [ ] 执行提升备节点为主节点
- [ ] 验证新主节点读写正常
- [ ] 更新 VIP 指向
### T+5min: 服务恢复
- [ ] 应用层连接恢复
- [ ] 核心指标回归正常
- [ ] 取消告警
### T+10min: 稳定确认
- [ ] 持续观察5分钟无异常
- [ ] 解散应急群
- [ ] 通知相关方
## 升级路径
1. T+2min 未恢复 → 升级到 SRE 主管
2. T+5min 未恢复 → 升级到技术总监
3. T+10min 未恢复 → 启动灾备切换流程
## 沟通模板
### 内部通报
> 【SEV1 故障】数据库主节点宕机
> 时间: 2026-08-03 10:30:00
> 影响: 全站核心交易不可用
> 当前状态: 正在执行故障切换
> 负责人: @IC
> 下次更新: 10分钟内
### 外部通报(如需)
> 服务异常通知
> 我们检测到部分服务出现异常,技术团队正在紧急处理。
> 预计恢复时间: XX:XX
> 对您造成的不便深表歉意。
## 回退方案
- 故障切换后旧主节点不可重新提升为从节点(binlog可能不一致)
- 需要从新主节点重建从节点(耗时约30分钟)
3. 角色分工矩阵(RACI)
flowchart LR
IC[Incident Commander<br/>故障指挥官] -->|指挥| OPS[Ops Lead<br/>运维执行]
IC -->|协调| DEV[Dev Lead<br/>开发支持]
IC -->|通报| COMMS[Comms Lead<br/>沟通协调]
IC -->|升级| MGMT[Management<br/>管理层]
OPS -->|执行动作| SYS[系统/基础设施]
DEV -->|代码层处理| APP[应用/服务]
COMMS -->|对内| INTERNAL[内部团队]
COMMS -->|对外| EXTERNAL[客户/用户]
| 角色 | 职责 | RACI | 关键能力要求 |
|---|---|---|---|
| IC (Incident Commander) | 统筹指挥、决策、资源协调 | R/A | 冷静、决策力、全局视野 |
| Ops Lead | 执行基础设施层操作 | R | K8s/网络/DB 操作能力 |
| Dev Lead | 应用层诊断和修复 | R | 代码熟悉、快速定位 |
| Comms Lead | 对内外沟通 | R | 简洁清晰、时间管理 |
| Scribe | 记录时间线和决策 | R | 速记、结构化能力 |
| Management | 资源支持和升级决策 | C/I | 业务影响判断 |
故障演练体系
1. 演练分级
| 级别 | 名称 | 范围 | 参与方 | 频率 | 风险 |
|---|---|---|---|---|---|
| L1 | 桌面推演 | 纸面/讨论 | SRE团队 | 每月 | 无 |
| L2 | 红蓝对抗-测试环境 | 测试环境 | SRE+Dev | 每季度 | 低 |
| L3 | 红蓝对抗-预发环境 | 预发环境 | SRE+Dev+DBA | 每季度 | 中 |
| L4 | Game Day-生产环境 | 生产环境(受限) | 全员 | 每半年 | 中高 |
| L5 | 混沌工程自动注入 | 生产环境 | 自动化 | 持续 | 可控 |
2. 红蓝对抗演练
flowchart TB
subgraph 红队(攻击方)
R1[设计故障场景] --> R2[注入故障]
R2 --> R3[观察蓝队响应]
R3 --> R4[增加复杂度]
end
subgraph 蓝队(防守方)
B1[接收告警] --> B2[响应判断]
B2 --> B3[执行恢复]
B3 --> B4[验证恢复]
end
subgraph 裁判组
J1[记录时间线] --> J2[评估响应质量]
J2 --> J3[记录问题]
end
R2 --> B1
B4 --> R3
R2 -.-> J1
B3 -.-> J1
红蓝对抗流程模板:
"""红蓝对抗演练管理器"""
from dataclasses import dataclass, field
from enum import Enum
from typing import Optional
import time
class DrillPhase(Enum):
PLANNING = "planning"
BRIEFING = "briefing" # 演练前对齐
EXECUTION = "execution"
OBSERVATION = "observation"
RECOVERY = "recovery"
DEBRIEF = "debrief" # 复盘
class FaultType(Enum):
POD_KILL = "pod_kill"
NETWORK_DELAY = "network_delay"
NETWORK_PARTITION = "network_partition"
CPU_STRESS = "cpu_stress"
MEM_STRESS = "mem_stress"
DISK_FILL = "disk_fill"
DNS_ERROR = "dns_error"
DB_FAILover = "db_failover"
DEPENDENCY_DOWN = "dependency_down"
@dataclass
class FaultScenario:
"""故障场景定义"""
scenario_id: str
name: str
fault_type: FaultType
target: str # 目标资源
params: dict # 故障参数
expected_impact: str # 预期影响
expected_mttr: int # 预期MTTR(秒)
success_criteria: str # 成功标准
safety_guardrails: list[str] # 安全护栏
@dataclass
class TimelineEntry:
timestamp: float
actor: str # red / blue / judge
event: str
detail: str = ""
@dataclass
class DrillResult:
drill_name: str
scenarios: list[FaultScenario]
timeline: list[TimelineEntry] = field(default_factory=list)
actual_mttr: int = 0
success: bool = False
issues_found: list[str] = field(default_factory=list)
action_items: list[str] = field(default_factory=list)
participants: dict[str, list[str]] = field(default_factory=dict)
class RedBlueDrill:
"""红蓝对抗演练管理器"""
def __init__(self, name: str, scenarios: list[FaultScenario]):
self.name = name
self.scenarios = scenarios
self.result = DrillResult(drill_name=name, scenarios=scenarios)
self.current_phase = DrillPhase.PLANNING
self.drill_start_time = 0
def start(self):
"""开始演练"""
self.current_phase = DrillPhase.EXECUTION
self.drill_start_time = time.time()
self._log("judge", "演练开始", f"共 {len(self.scenarios)} 个场景")
def inject_fault(self, scenario: FaultScenario):
"""红队注入故障"""
self.current_phase = DrillPhase.EXECUTION
self._log("red", f"注入故障: {scenario.name}",
f"目标: {scenario.target}, 类型: {scenario.fault_type.value}")
# 实际执行 ChaosMesh / kubectl 命令
# self._execute_chaos(scenario)
def record_blue_response(self, event: str, detail: str = ""):
"""记录蓝队响应"""
self._log("blue", event, detail)
def record_recovery(self, scenario: FaultScenario):
"""记录恢复"""
self.current_phase = DrillPhase.RECOVERY
actual_mttr = int(time.time() - self.drill_start_time)
self.result.actual_mttr = actual_mttr
self.result.success = actual_mttr <= scenario.expected_mttr
self._log("blue", f"恢复完成: {scenario.name}",
f"实际MTTR: {actual_mttr}s, 预期: {scenario.expected_mttr}s")
def add_issue(self, issue: str):
"""记录发现的问题"""
self.result.issues_found.append(issue)
self._log("judge", f"发现问题: {issue}")
def add_action_item(self, item: str):
"""记录改进项"""
self.result.action_items.append(item)
def finish(self) -> DrillResult:
"""结束演练"""
self.current_phase = DrillPhase.DEBRIEF
self._log("judge", "演练结束",
f"MTTR: {self.result.actual_mttr}s, "
f"问题数: {len(self.result.issues_found)}")
return self.result
def _log(self, actor: str, event: str, detail: str = ""):
entry = TimelineEntry(
timestamp=time.time(),
actor=actor,
event=event,
detail=detail,
)
self.result.timeline.append(entry)
# ====== 预定义演练场景 ======
drill_scenarios = [
FaultScenario(
scenario_id="DRILL-001",
name="核心API Pod被随机杀死",
fault_type=FaultType.POD_KILL,
target="deployment/core-api",
params={"kill_ratio": 0.3},
expected_impact="30%实例下线,短暂5xx",
expected_mttr=60,
success_criteria="HPA自动扩容恢复,5xx率<1%持续3分钟",
safety_guardrails=["min_available=70%", "max_kill=30%"],
),
FaultScenario(
scenario_id="DRILL-002",
name="数据库主节点故障",
fault_type=FaultType.DB_FAILover,
target="mysql-primary",
params={},
expected_impact="写入中断,读取可能受影响",
expected_mttr=300,
success_criteria="故障切换完成,读写恢复",
safety_guardrails=["验证备节点同步状态", "回滚方案就绪"],
),
FaultScenario(
scenario_id="DRILL-003",
name="网络延迟注入",
fault_type=FaultType.NETWORK_DELAY,
target="namespace/payment",
params={"latency_ms": 500, "duration": "5m"},
expected_impact="支付链路P99延迟飙升",
expected_mttr=120,
success_criteria="降级策略生效,超时熔断触发",
safety_guardrails=["仅影响payment namespace", "5分钟自动恢复"],
),
]
3. Game Day 组织流程
gantt
title Game Day 全流程时间线(以1天为例)
dateFormat HH:mm
axisFormat %H:%M
section 准备阶段
场景设计 :a1, 09:00, 30m
风险评估 :a2, 09:30, 20m
安全护栏配置 :a3, 09:50, 20m
对齐说明会 :a4, 10:10, 20m
section 执行阶段
场景1: PodKill :b1, 10:30, 30m
场景2: 网络故障 :b2, 11:00, 30m
场景3: DB故障切换 :b3, 11:30, 45m
午休 :break, 12:15, 45m
场景4: 级联故障 :b4, 13:00, 60m
场景5: 安全事件 :b5, 14:00, 45m
section 复盘阶段
数据收集 :c1, 14:45, 15m
全员复盘 :c2, 15:00, 60m
Action Items :c3, 16:00, 30m
报告输出 :c4, 16:30, 30m
Game Day Checklist:
| 阶段 | 检查项 | 负责人 | 状态 |
|---|---|---|---|
| 准备 | 场景设计完成并评审 | 红队队长 | ☐ |
| 准备 | 安全护栏配置并验证 | SRE | ☐ |
| 准备 | 回滚方案确认 | 蓝队队长 | ☐ |
| 准备 | 监控大盘就绪 | 监控负责人 | ☐ |
| 准备 | 参与人员通知到位 | PM | ☐ |
| 准备 | 变更冻结窗口已通知 | Comms | ☐ |
| 执行 | 演练前对齐会完成 | IC | ☐ |
| 执行 | 每个场景执行记录 | 裁判组 | ☐ |
| 执行 | 安全护栏有效 | SRE | ☐ |
| 执行 | 紧急中止机制就绪 | IC | ☐ |
| 复盘 | 时间线还原 | Scribe | ☐ |
| 复盘 | MTTR/MTTD统计 | 数据分析 | ☐ |
| 复盘 | 问题清单整理 | 裁判组 | ☐ |
| 复盘 | Action Items分配 | IC | ☐ |
| 复盘 | 改进跟踪计划 | PM | ☐ |
4. 混沌工程自动注入
与 混沌工程与故障注入 结合,将部分低风险演练自动化。
# ChaosMesh 定期自动注入实验
apiVersion: chaos-mesh.org/v1alpha1
kind: Schedule
metadata:
name: weekly-pod-kill-drill
namespace: chaos-testing
spec:
schedule: "0 10 * * 1" # 每周一10点
historyLimit: 5
concurrencyPolicy: Forbid
type: Workflow
workflow:
templates:
- name: automated-drill
templateType: Serial
deadline: 300s
task:
- name: inject-pod-kill
templateType: PodChaos
deadline: 60s
podchaos:
selector:
namespaces: [staging]
labelSelectors:
app: non-critical-api
mode: fixed-percent
value: "20"
action: pod-kill
gracePeriod: 0
演练效果度量
关键指标
| 指标 | 含义 | 目标 | 测量方法 |
|---|---|---|---|
| MTTD(演练) | 演练中检测时间 | <1min | 故障注入→首次告警 |
| MTTA(演练) | 演练中响应时间 | <2min | 告警→IC接管 |
| MTTR(演练) | 演练中恢复时间 | <场景目标 | 故障注入→恢复确认 |
| 预案覆盖率 | 有预案的故障占比 | >80% | 有预案场景/总场景 |
| 预案有效率 | 预案指导成功恢复占比 | >90% | 预案有效/预案使用 |
| 发现问题数 | 每次演练发现的新问题 | — | 记录统计 |
| Action Items 闭环率 | 演练改进项完成率 | >80% | 已完成/总改进项 |
演练成熟度模型
| 级别 | 特征 | 典型表现 |
|---|---|---|
| L1 | 无演练 | 全靠线上真实故障”练兵” |
| L2 | 桌面推演 | 定期讨论故障场景,无实操 |
| L3 | 测试环境演练 | 在非生产环境模拟故障 |
| L4 | 预发环境红蓝对抗 | 定期红蓝对抗,有度量 |
| L5 | 生产环境Game Day | 生产环境受控演练+混沌工程持续注入 |
常见坑点
| 坑点 | 现象 | 解决方案 |
|---|---|---|
| 演练变事故 | 演练引发真实故障 | 安全护栏+紧急中止机制 |
| 只练不练 | 演练后不改进 | Action Items 闭环跟踪 |
| 场景单一 | 只练简单场景 | 渐进复杂度,引入级联故障 |
| 知己知彼 | 蓝队提前知道场景 | 红队保密,随机时间注入 |
| 过度依赖预案 | 遇到预案外故障无所适从 | 培养通用故障处理能力 |
| 演练疲劳 | 频率过高团队抵触 | 合理频率+轮换参与 |
关联知识
- SRE 稳定性工程总览 — 应急预案是稳定性工程的核心准备
- 故障生命周期管理 — 演练验证故障生命周期的每个环节
- On-Call 体系与值班机制 — 演练直接锻炼 On-Call 能力
- 混沌工程与故障注入 — 混沌工程是自动化的故障演练
- 灾备与容灾方案 — 灾备切换需要通过演练验证
- 无指责复盘与故障分析方法论 — 演练复盘遵循无指责原则
- 故障自愈与自动恢复 — 演练验证自愈系统有效性
- 变更管理全流程 — 变更冻结期与演练窗口协调
参考资源
- 《SRE: Google 运维解密》— 第18章 “Slack” + Game Day 实践
- 《Hands-On Chaos Engineering” — 故障演练工程化
- ChaosMesh 文档 — Automated Chaos Experiments
- Netflix Chaos Monkey — 生产环境混沌工程先驱
- AWS Well-Architected Framework — Disaster Recovery & Game Days
学习时间
约 4-6 小时(含组织一次完整的桌面推演)
状态
- 理解应急预案体系的组成部分
- 能编写标准化的应急预案模板
- 掌握红蓝对抗演练的流程
- 能设计 Game Day 全流程
- 理解演练效果度量指标
- 实际组织一次红蓝对抗演练
- 搭建混沌工程自动注入流水线