Files
vps-manager/docs/migration-runbook.md
gouki cc8c91ddd9 feat(migration): 换宿主平台迁移能力(打包/恢复脚本 + runbook)
make_migration_bundle:sqlite backup API 在线快照(无需停服)打包 data/(两库+escrow+backups)与 .env,含逐文件 sha256 清单与版本/commit 元信息。restore_migration_bundle:sha256 逐文件校验(篡改即拒绝)、目标机原状态留 pre-restore 备份、兼容旧版 Python 的 tar 解包路径校验,并打印 serve/agent/CORS/源机下线等手工步骤。docs/migration-runbook.md:迁移面清单、可选预处理(agent 去 IP 化)、七步迁移、十项验收、回滚与灾难恢复。本地隔离演练验证:密码解密一致、2FA 出码可用、escrow+RESTORE_KEY 可解回 MASTER_KEY、篡改包被拦截。
2026-09-05 23:20:45 +00:00

134 lines
6.3 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 迁移 Runbookvps-manager 换宿主平台
适用场景:当前宿主为 cc1Tailscale `100.89.0.11`HTTPS `https://dify.taile5765c.ts.net`);
选定长期平台后按本手册完成整体搬迁。目标:**数据零丢失、密文零重录、服务可回滚**。
---
## 1. 迁移面:什么要搬,什么能重建
| 类别 | 内容 | 迁移方式 |
|---|---|---|
| **唯一状态(必须搬)** | `data/assets.db``data/metrics.db``data/master_key.escrow``data/backups/``.env` | `make_migration_bundle.py` 打包 → `restore_migration_bundle.py` 恢复 |
| 可重建(不搬) | 代码 | 新机 `git clone` Gitea 仓库(`setup.sh` 自动) |
| 可重建(不搬) | Python venv 与依赖 | `setup.sh``pip install -r requirements.txt` |
| 可重建(不搬) | systemd units 与全部 timer | `deploy/*.service\|timer` 在仓库内,`setup.sh` 安装 |
| 可重建(不搬) | Tailscale HTTPS serve | 一条 `tailscale serve --bg --https=443 http://127.0.0.1:8000` |
| **机器外状态** | 离线 `RESTORE_KEY` 抄本、Gitea deploy key、S3 凭据 | 手工:RESTORE_KEY 抄本随身;新机需可访问 GiteaSSH key 或 HTTPS+token |
关键不变量:**`.env` 里的 `MASTER_KEY` 必须原样搬过去**——库内所有密文(平台账号
api_config、凭据密码、2FA secret)都用它加密。MASTER_KEY 不变,密文即可直接读,无需重录。
---
## 2. 迁移前可选预处理(让将来迁移更省事)
一次性做完,之后换平台时 agent 与访问地址都无需改动:
1. **agent 地址去 IP 化**:把各被管 VPS 的 `/etc/vps-agent.env`
`VPS_MANAGER_URL=http://100.89.0.11:8000` 改为 MagicDNS 名,例如
`http://dify.<tailnet>.ts.net:8000`;迁移时新设备沿用同名 `dify` 即可零改动。
```bash
sed -i 's#http://100.89.0.11:8000#http://<新地址>:8000#' /etc/vps-agent.env && systemctl restart vps-agent
```
2. **建立 MASTER_KEY 托管**(若尚未做):`python scripts/setup_key_escrow.py`
RESTORE_KEY 抄到 2~3 个离线位置——迁移途中 `.env` 出意外时这是唯一后路。
3. 确认备份 timer 正常:`systemctl list-timers | grep vps-`,且 `data/backups/escrow/` 有托管档。
---
## 3. 迁移步骤
### 3.1 源机(cc1)打包
```bash
ssh cc1
cd /opt/vps-manager
.venv/bin/python scripts/make_migration_bundle.py /tmp/vps-bundle.tar.gz
```
记录输出的**包校验和**与文件数。打包用 sqlite backup API 在线快照,**无需停服**。
### 3.2 传输
仅用 `scp` 或加密介质直传新机,不经第三方网盘/聊天工具:
```bash
scp /tmp/vps-bundle.tar.gz root@<新机>:/tmp/
```
### 3.3 新机部署基座
```bash
sudo tailscale up # 入网;建议设备名沿用 dify(保持 HTTPS URL 不变)
git clone <Gitea仓库> /tmp/vps-manager-setup && cd /tmp/vps-manager-setup
sudo bash deploy/setup.sh # 依赖/venv/units/.env(临时)/服务启动
```
`setup.sh` 生成的临时 `.env` 会在 3.4 被包内 `.env` 覆盖(含真实 MASTER_KEY)。
### 3.4 停服并恢复状态
```bash
cd /opt/vps-manager
sudo systemctl stop vps-manager vps-manager-update.timer
sudo .venv/bin/python scripts/restore_migration_bundle.py /tmp/vps-bundle.tar.gz
sudo systemctl start vps-manager
```
脚本逐文件校验 sha256(不匹配即拒绝),并把新机原有 `data/`、`.env` 备份为
`*.pre-restore-<ts>` 以便回滚。
### 3.5 访问入口
```bash
sudo tailscale serve --bg --https=443 http://127.0.0.1:8000
sudo tailscale status # 确认 https://<设备名>.<tailnet>.ts.net
```
若 URL 与旧的不同:把新域名加入 `/opt/vps-manager/.env` 的 `CORS_ORIGINS`
`sudo systemctl restart vps-manager`,并更新浏览器书签 / 手机 PWA。
### 3.6 agent 与通知
- 各被管 VPS:若未做 §2.1 预处理,逐台改 `VPS_MANAGER_URL` 指向新机并 `systemctl restart vps-agent`
- 通知渠道(Telegram/SMTP)配置随 `.env` 迁移,无需改动,可用一次「立即同步」验证送达。
### 3.7 源机(cc1)下线 —— 防双写双通知
确认新机运行正常**至少一个同步/备份周期**后:
```bash
ssh cc1
sudo systemctl disable --now vps-manager vps-manager-update.timer \
vps-backup-assets.timer vps-backup-metrics.timer vps-sync-ai.timer vps-renewal-check.timer
sudo rm -f /tmp/vps-bundle.tar.gz # 迁移包等同最高机密,用完即删
```
新机侧同样删除 `/tmp/vps-bundle.tar.gz`。
---
## 4. 验收清单(迁移后逐项打勾)
- [ ] `curl https://<新地址>/health` 返回 `status=ok`version/commit 与源机一致
- [ ] 前端各视图数据完整:资产、订阅、域名、平台账号、监控、AI
- [ ] **凭据可解密**`/api/credentials/{id}/password` 能解出明文(抽查 1~2 条)
- [ ] **2FA 动态码正确**`/api/credentials/{id}/otp` 出的码能在目标站点通过验证
- [ ] 平台账号 api_config 可解密:一次手动同步成功(验证 API 凭据未失效)
- [ ] `systemctl list-timers | grep vps-` 五个 timer 全部 enabled 且有下次触发时间
- [ ] 监控数据开始进新机(`metrics.db` 有 agent 上报的新指标)
- [ ] 备份 timer 跑过一次:`data/backups/assets/`、`data/backups/escrow/` 有新文件(含 S3
- [ ] 源机所有 timer 已 disable、服务已 stop(防双写)
- [ ] 源机与新机上的迁移包均已删除
---
## 5. 回滚
新机异常时(源机尚未下线):
```bash
sudo systemctl stop vps-manager
cd /opt/vps-manager && sudo mv data data.bad && sudo mv data.pre-restore-<ts> data
sudo mv .env .env.bad && sudo mv .env.pre-restore-<ts> .env
sudo systemctl start vps-manager
```
源机已下线的极端情况:在源机重新 `systemctl enable --now` 各服务与 timer 即可
(其数据未被改动,打包过程只读)。
---
## 6. 灾难恢复(与迁移不同的场景)
- **仅 `.env`/MASTER_KEY 丢失**(库还在):`python scripts/recover_master_key.py --key <RESTORE_KEY> --write`
- **整机丢失**(只有备份):新机 `setup.sh` → 从 S3/离线取最新 `data/backups/assets/assets_*.db`
与 `metrics_*.db` 放回 `data/` → 取 `data/backups/escrow/master_key.escrow` 放回 `data/` →
用离线 RESTORE_KEY 解出 MASTER_KEY 写入 `.env` → 起服验证解密。
- **RESTORE_KEY 也丢**:无解,密文不可恢复(这正是 §2.2 要求多处离线抄本的原因)。