---
id: 005
title: 备份 / 迁移 Chaos Engineering 演练（防腐烂）
status: closed
labels: [infra, chaos-engineering, sre, reliability]
assignee: team-lead
created_at: 2026-04-23
closed_at: 2026-04-23
---

# #005 备份 / 迁移 Chaos Engineering 演练（防腐烂）

## 动机

老板原话："我最怕的是做了这个东西然后过了非常长的时间，当我突然用到的时候，发现它根本就不起作用了。"

业界行话：**Chaos Engineering**（Netflix 鼻祖）/ **Game Day**（AWS 命名）/ **Disaster Recovery Drills**。核心：**定期故意触发稀有事件，让链路每天都跑，防止静默失效**。

当前 xiangqin 的"静默失效风险"：

1. **OSS 备份 cron** 在 epsilon 02:00 daily 跑，但**没人知道它今晚是否真跑了**（只有某天真灾难时拉不到备份才发现它 3 个月前就挂了）
2. **scripts/restore.sh** 恢复脚本写完后**再也没人跑过**，sqlite schema / aliyun cli 更新可能让它失效
3. **scripts/provision.sh** 装机脚本只在 alpha / epsilon 初次装机各跑过一次，再也没跑过——哪天要换机时可能 apt 包名变了 / uv 装法变了 / nginx 配置语法更新，脚本就废了

## 方案：4 个 milestone，增量实施

### M1. Backup freshness check（每小时）

**一句话**：每小时问一次 OSS "最新备份是多久以前的"，>26h 就徽章红。

- GHA workflow：`.github/workflows/chaos-backup-freshness.yml`
- cron: `0 * * * *`（每小时 0 分）
- ssh epsilon → `aliyun oss ls oss://agentaily-backup-xiangqin-prod/daily/ | tail -5` 拿最新对象 + 时间戳
- 计算距今时差，> 26h → workflow fail + 发通知
- 徽章 JSON：`docs/assets/badges/backup-fresh.json`（由 workflow 生成，CF Pages 托管给 shields.io 读）

**成本**：GHA 免费 runner + ssh epsilon（秒级）

**覆盖**：cron 是否在跑 / OSS 凭证是否过期 / 备份脚本是否挂

### M2. Backup integrity check（每日）

**一句话**：每天把最新备份拉下来打开看看"没坏吧"。

- GHA workflow：`.github/workflows/chaos-backup-integrity.yml`
- cron: `0 3 * * *`（每日 03:00，即 epsilon cron 跑完后 1 小时）
- ssh epsilon → 下载最新 `xiangqin-YYYYMMDD.db.gz` → gunzip → `sqlite3 xxx "PRAGMA integrity_check"` + 检查核心表 `users / messages / envelope_balance` 行数
- 任一项异常 → fail

**成本**：0（在 epsilon 本地解压验证）

**覆盖**：备份文件**能读**且 schema 完整

### M3. Restore drill on alpha（每周）

**一句话**：每周在 alpha 整套还原一遍，"真能恢复回来吗"。

- GHA workflow：`.github/workflows/chaos-restore-drill.yml`
- cron: `0 4 * * 0`（每周日 04:00）
- ssh alpha → `scripts/restore.sh --dry-run --source=oss://...latest`（restore.sh 要先支持 `--target-path` 参数避开覆盖 alpha 自己 db）
- restore 完 → `xq --endpoint http://127.0.0.1 health` → 验证 /users count 合理
- 跑完清理 `/tmp/restore-drill-*.db`

**成本**：alpha 资源（CPU 1 秒 + 100M 磁盘临时）

**覆盖**：restore 工具链端到端能恢复

### M4. Migration drill（每月）

**一句话**：每月开一台按量付费机器跑一遍装机 + 部署再关掉，"换机器流程还能跑吗"。

- GHA workflow：`.github/workflows/chaos-migration-drill.yml`
- cron: `0 5 1 * *`（每月 1 号 05:00）+ workflow_dispatch 手动触发
- aliyun CLI CreateInstance 按量付费 ECS（ecs.t6-c1m1.large 1C1G ~¥0.1-0.2/h）→ wait ready → scp SSH key → `bash scripts/provision.sh <new-ip>` → `bash scripts/deploy.sh <new-ip>` → curl /health 验证 → DeleteInstance
- 整个流程 < 15 分钟 + ¥1 以内
- 失败不 delete 保留机器让人 debug

**成本**：每次 ≤ ¥1，每月 ≤ ¥12/年

**覆盖**：provision.sh 全新机器能跑通

## 徽章（所有 milestone 共用）

`README.md` 顶部一排：

```markdown
[![ci](badge)] [![backup-fresh](badge)] [![restore-drill](badge)] [![migration-drill](badge)]
```

徽章 JSON 由 workflow 写到 `docs/assets/badges/*.json`，CF Pages 托管，shields.io 动态读取渲染颜色（绿/黄/红 + 文案 "2h ago" / "failed 3d ago"）。

## 实施顺序

M1 → M2 → M3 → M4。每个完成后 commit + push + README 加徽章 + 验证 workflow 能跑通。

## 不做

- **真·Chaos Monkey**（随机 kill 进程）：两人团队不需要，M4 已覆盖关键失效场景
- **Terraform / Pulumi**：引入 IaC 栈成本高，provision.sh 幂等脚本够用
- **跨云演练**：只在阿里云内做，不跨 AWS / GCP

## 进度

- [x] M1 backup freshness（`.github/workflows/chaos-backup-freshness.yml`，每小时 cron，手动触发过 1 次绿）
- [x] M2 backup integrity（`.github/workflows/chaos-backup-integrity.yml`，每日 cron UTC 05:00，手动触发过 1 次绿）
- [x] M3 restore drill（`.github/workflows/chaos-restore-drill.yml`，每周日 cron，**GHA runner 异地演练**——比 alpha 更严，手动触发过 1 次绿：users=4 / tables=6 / integrity ok）
- [x] M4 migration drill（`.github/workflows/chaos-migration-drill.yml`，每月 1 号 cron + dispatch，**真开按量 ECS + provision + deploy + smoke + destroy，2026-04-23 实测第二次全绿**；第一次因 SSH idle timeout 挂在 deploy.sh，加 ServerAliveInterval 修好；cleanup always 保证没留浪费机器）

## 追加 M5（性能监控）—— issue #006 跟踪

虽然不在本 issue 原始 4 milestone，但和 chaos engineering "防腐烂" 紧密：

- [x] M5 metrics daily（FastAPI middleware + aggregate_metrics.py + chaos-metrics-daily.yml + README 徽章）
- 见 [issue #006](./006-performance-monitoring.md)

## 关闭

2026-04-23 23:30-01:30 一次性完成全部 4 个 milestone。端到端故障已在 M4 第一次 drill 中暴露（SSH keepalive）并修复，证明 chaos engineering 已经发挥防腐作用（假想场景：半年后真灾难时发现修起来）。
