下载工作台
企业级架构运维

跟练三:跨机房切换与 Postmortem

试读上半部分 · 解锁后可读全文

第 18 章 · 跟练三:跨机房切换与 Postmortem

类型:灾备演练工作坊(约 3~4 小时)

场景:贤紫社区电商 同城双活 —— 机房 AZ-A 网络中断,执行流量切换、数据库 Failover 与业务恢复。

前置:ch12 多活容灾、ch13 SLO/RTO、ch09 容量(B 机房扩容)、ch14 ADR/Runbook 模板。

环境要求(二选一):

方案说明
推荐两套小紫云计算集群 cluster-a / cluster-b,外置 MySQL 主从
最小单集群两 Namespace + 模拟 GSLB 权重(教学降级)

产出物(内嵌本章):

  1. DR 演练执行记录表(§18.4)
  2. 完整 Postmortem(§18.7 模板 + §18.8 示范)
  3. ADR-018 Failover 自动化(§18.9)
  4. Failover ConfigMap 与 Runbook(§18.5~18.6)

18.0 演练目标与成功标准

指标目标值测量方式
RTO< 15 min(业务恢复下单)演练记录表时间戳
RPO< 30 s(异步复制)主从延迟 + 停写窗口
下单成功率恢复后 ≥99.9%Prometheus SLI
数据一致性无重复扣款、无丢单对账脚本
文档Postmortem + ADR 归档ch14 目录

18.1 拓扑与常态配置(给定)

机房K8s 集群常态流量权重order-api 副本MySQL 角色
AZ-Acluster-a60%6 × 2C4G主库 (write)
AZ-Bcluster-b40%4 × 2C4G从库 (async replica)

入口api.shop.example.com → GSLB 加权轮询 + 健康检查(HTTP /health,间隔 10s,失败 3 次摘流)

数据复制:MySQL 异步复制,常态延迟 0.3~2s;极端 <10s

交叉引用

  • ch12 §12.2 同城双活架构
  • ch16 大促 6 副本 —— 本演练 B 机房仅 4 副本,切流后 容量不足 为已知风险
  • ch13:RTO 超标消耗可用性预算
  • ch17:若叠加缓存击穿,切流后更难恢复
flowchart TB
  DNS[GSLB api.shop.example.com]
  DNS -->|60%| A[cluster-a AZ-A]
  DNS -->|40%| B[cluster-b AZ-B]
  subgraph AZ-A
    A --> OA[order-api ×6]
    OA --> MYA[(MySQL 主)]
  end
  subgraph AZ-B
    B --> OB[order-api ×4]
    OB --> MYB[(MySQL 从)]
  end
  MYA -.->|async replication| MYB

18.2 故障注入剧本(仅授权测试环境)

严禁在未授权生产环境执行。演练前完成变更窗口审批与回滚预案。

剧本 A:AZ-A 入口网络中断(推荐)

# 模拟:在 AZ-A 边界防火墙 DROP GSLB 探针与业务流量
# 或由网络组执行「A 机房上联中断」沙盘

剧本 B:仅 scale order-api(最小环境)

kubectl config use-context cluster-a
kubectl scale deployment/order-api -n prod-app --replicas=0

剧本 C:MySQL 主库不可达(进阶)

# 阻断 A 机房应用到 MySQL 主的 3306(保留复制专线观察延迟)

本次标准剧本:A 失联 45 分钟(预计恢复不确定)→ 决策 B 从库 promote + DNS 100% B


18.3 决策树(演练中必须书面勾选)

AZ-A 不可达
│
├─ 预计恢复 < 30min 且 从库延迟 < 10s 且 主库内网仍可达写
│     → DNS 100% B;写仍指向 A 主(若网络路径 OK)
│     → 风险:A 完全失联时不可选
│
├─ 预计恢复 > 30min 或 主从断连或 从库延迟 > 30s
│     → 停写 2min → promote B 从库 → 应用 DSN 切 B → DNS 100% B
│     → 公告:短暂维护
│
└─ A、B 均不可用
      → ch12 §12.5 异地冷备;RPO 升至最近备份点

本次演练路径:第二分支(promote + 停写 + DSN 切换)

决策点你的选择理由
是否只切流量不切写?A 完全失联,写落 A 导致脑裂
是否 promote?恢复时间未知 >45min
停写窗口?2 min等从库追平 + 避免双写分叉

18.4 操作 Runbook 与执行记录表

# DR Runbook · AZ-A 失联 → AZ-B 接管

## 前置检查
- [ ] 确认 P1 事故,War room 建立(企业微信/钉钉群)
- [ ] 确认非误报:A 健康检查失败、多地域用户反馈
- [ ] 通知:客服公告模板、管理层报备

## 执行步骤(填写「实际时间」列)

| 步骤 | 操作 | 负责人 | 计划 T+ | 实际时间 | 结果 | 证据 |
|------|------|--------|---------|----------|------|------|
| 1 | 宣布 P1,冻结非紧急发布(ch13) | 事故指挥 | 0 | | | |
| 2 | GSLB A 权重 → 0 | 网络/SRE | 3min | | | 控制台截图 |
| 3 | 观察 B 错误率、P99、CPU | SRE | 5min | | | Grafana |
| 4 | `SHOW REPLICA STATUS\G` 查延迟 | DBA | 6min | | | 延迟 ___s |
| 5 | **业务降级**:关秒杀/推荐(ch16 ADR-016-03) | 开发 | 8min | | | |
| 6 | **停写**:order-api 只读模式 ConfigMap | 开发 | 10min | | | |
| 7 | DBA promote B 为新主 | DBA | 12min | | | binlog 位点 |
| 8 | 应用 DSN 切 B(§18.5 ConfigMap) | SRE | 14min | | | rollout 完成 |
| 9 | 核心冒烟:下单/支付回调 | QA | 15min | | | 用例 #1-5 |
| 10 | 对外公告恢复 | 客服 | 20min | | | |
| 11 | A 恢复后反向同步评估 | DBA | — | | | 另案 |

## 冒烟用例
| # | 用例 | 期望 |
|---|------|------|
| 1 | POST /api/v1/orders | 201 |
| 2 | GET /api/v1/orders/{id} | 200 |
| 3 | 支付回调模拟 | success |
| 4 | 健康检查 | 200 |
| 5 | 跨 AZ 延迟 P99 | <500ms |

18.5 Failover ConfigMap 一键切换(ADR-018 核心)

设计目标:消除 DSN 变更 人工审批 5min 延误(上次演练教训)。

# db-endpoint-config.yaml — 常态
apiVersion: v1
kind: ConfigMap
metadata:
  name: db-endpoint
  namespace: prod-app
data:
  DB_HOST: "mysql-master.az-a.internal"
  DB_READ_HOST: "mysql-replica.az-b.internal"
  DB_FAILOVER_MODE: "normal"   # normal | readonly | failover-b

---
# 停写阶段 apply
# kubectl patch configmap db-endpoint --type merge -p '{"data":{"DB_FAILOVER_MODE":"readonly"}}'
# 应用滚动重启:kubectl rollout restart deploy/order-api -n prod-app

---
# Failover 后 apply(promote 完成后)
# data:
#   DB_HOST: "mysql-master.az-b.internal"
#   DB_READ_HOST: "mysql-master.az-b.internal"
#   DB_FAILOVER_MODE: "failover-b"

Django settings 读取

# settings/prod.py 片段
FAILOVER = os.environ.get("DB_FAILOVER_MODE", "normal")
if FAILOVER == "readonly":
    MIDDLEWARE.insert(0, "apps.core.middleware.ReadOnlyModeMiddleware")

DATABASES = {
    "default": {
        "HOST": os.environ["DB_HOST"],
        # ...
    }
}

ReadOnlyModeMiddleware:对 POST/PUT/PATCH/DELETE 返回 503,Retry-After: 120


18.6 小紫云计算操作速查

步骤小紫控制台 / kubectl
切集群上下文kubectl config use-context cluster-b
更新 ConfigMap应用部署 → 配置 → db-endpoint 编辑
滚动重启应用部署 → order-api → 重启
扩容 B副本 4→6(ch09 公式:承接 100% 流量)
观察监控告警 → order-api RED + MySQL

B 机房扩容计算(引用 ch09)

原 B 承担 40% 流量 ×4 副本 → 每副本 10% 全站
切 100% 后需 10× 单副本承载 → 至少 10 副本理论
教学决议:4→6 + HPA max 10,接受短暂 CPU 88%(见 §18.8)

18.7 演练实测数据(教学样本,填入你的实测)

指标切流前切流中峰值 (T+5~8)恢复后 (T+20)
下单成功率99.97%94.2%99.92%
order-api P99180ms890ms220ms
B 集群 CPU45%88%62%
MySQL 主从延迟0.5s断连N/A(已 promote)
5xx 率0.03%5.8%0.08%

未达标项

指标目标实际主因
RTO15min20minDSN ConfigMap 变更需人工审批 5min
RPO30s~0(停写 2min 内)promote 前停写有效

以下内容需解锁后阅读

试读已结束。解锁本章 ¥5.00,或开通年度会员畅读全部教程。
年度会员 ¥199.00/年; 小紫 AI 工作台有效会员 ¥99.00/年

正文仅在服务端鉴权后下发,未付费无法获取下半部分内容。