完善异机备份告警与同日恢复点取代(07-27-automatic-offhost-backup)
ci-e2e / e2e (push) Waiting to run
ci-minimal / check-minimal (push) Waiting to run

这个提交包含在:
2026-07-27 13:54:16 +08:00
父节点 2526052e73
当前提交 a0006f07b8
共修改 15 个文件,包含 882 行新增和 105 行删除
+23 -6
查看文件
@@ -226,29 +226,46 @@ manifest 必须至少记录一个真实来源标识:活动 `/ready` 有构建
整批复制到获批的异机/异盘位置并记录保留周期;不能只复制 DB 或附件中的一份。
#### 每日自动异机备份(当前仅本地实现,远端启用为 EXTERNAL_GATED)
#### 每日自动异机备份(可靠启用闭环仍为 EXTERNAL_GATED)
- 每日 02:30 由生产主机解析唯一活动 Compose project 和真实 DB/附件挂载,生成同批三文件,ARM 校验并原子归档成功后才写成功状态和执行源端轮转。
- 生产保留 7 个自动日批次;ARM 保留 7 个日批次和 4 个周检查点。07:10 watchdog 检查最近 24 小时异机成功记录;季度 timer 只发送人工影子恢复提醒。
- 生产保留 7 个自动日批次;ARM 保留 7 个日批次和 4 个周检查点。07:10 源端 watchdog 检查最近 24 小时异机成功记录;ARM 独立 dead-man 检查最近 26 小时归档,生产整机或 OA server 停止时仍能告警;季度 timer 只发送人工影子恢复提醒。
- ARM 使用专用密码锁定账号和 forced-command key:rsync 只能写 inbox,不能读取/删除 archive,也没有普通 shell。生产固定专用 key 和人工核对的 known-hosts,禁止 `accept-new`。
- 失败告警只包含阶段、状态、finding 数和类别建议。同一失败降噪;首次失败、状态恶化、新 finding 和每季度首次恢复提醒才发送。
- 同一 UTC 日重试使用新 run id 时,ARM 必须先完整校验候选,再原子取代当日旧批次并重建该周硬链接检查点;坏候选或发布失败不得触碰原有效批次。
- 生产与 ARM 都直接使用独立企微运维群机器人,不依赖 OA server、数据库或个人工号。Webhook 正文只由用户手工写入两台主机的 root-only `0600` 文件;仓库、普通 `.env`、systemd unit、日志和聊天只能出现受控文件路径,不能出现 URL 或 key。
- 失败告警只包含阶段、状态、finding 数和类别建议;原始命令诊断只保留在源端状态目录的 `0600` 最近失败日志。同一失败降噪;首次失败、状态恶化、新 finding、恢复通知和每季度首次恢复提醒才发送。新部署构建与最近恢复点 build SHA 不同只记 finding,缺失、损坏、超龄、未来时间或 Compose project 不同仍硬失败。
创建机器人后,由用户分别登录两台主机,在 root shell 中用静默输入把完整 Webhook 写入受控路径;不要把值作为命令参数、环境文件内容或聊天消息交给 Codex:
```bash
umask 077
read -rsp 'WeCom operations robot Webhook: ' HYB_OA_ALERT_WEBHOOK
printf '\n'
install -o root -g root -m 0600 /dev/null <controlled-webhook-file>
printf '%s\n' "$HYB_OA_ALERT_WEBHOOK" > <controlled-webhook-file>
unset HYB_OA_ALERT_WEBHOOK
```
安装顺序必须是:先获批 ARM 端安装 rsync、受限身份和 receiver,用合成批次验证;再获批生产端安装 units,手工完成首个真实批次;最后显式执行生产安装器的 `enable`。两个安装器默认 `install` 均不代表已启用,不得把本地合同通过写成远端完成。
```bash
# ARM:参数从受控配置取得;install 会写账号/authorized_keys/receiver,需单独批准。
BACKUP_ROOT=<absolute-backup-root> PUBLIC_KEY_FILE=<dedicated-public-key> \
RECEIVER_SOURCE=<repo-receiver-script> \
RECEIVER_SOURCE=<repo-receiver-script> ALERT_WEBHOOK_FILE=<controlled-webhook-file> \
ALERT_HELPER_SOURCE=<repo-group-alert-helper> DEADMAN_SOURCE=<repo-deadman-script> \
bash deploy/trialrun/install-offhost-backup-receiver.sh install
# 生产:先只安装 disabled units;路径、项目、构建和 SSH 身份必须绑定活动实例。
<explicit-project-data-build-and-ssh-env> \
<explicit-project-data-build-ssh-and-webhook-file-env> \
bash deploy/trialrun/install-automatic-backup-timer.sh install
# 首批真实备份、ARM 哈希和脱敏告警均经单独确认后,才允许启用三组 timer。
<same-arm-explicit-env> bash deploy/trialrun/install-offhost-backup-receiver.sh enable
<same-explicit-env> bash deploy/trialrun/install-automatic-backup-timer.sh enable
```
`BACKUP_RUN_AT` 使用生产主机本地时区,ARM 归档按 UTC 日分桶。调整执行时刻的当天可能出现预期空窗;改动后必须人工确认次日 UTC 分桶、下一次 daily 运行和 26 小时 dead-man 窗口。真实告警验收必须真正停止一次源端 timer 或故意让一次传输失败,再确认 ARM 首次告警、重复降噪和恢复通知;只测试机器人能发消息不算通过。
卸载只撤销 timer/receiver 授权,保留源端批次、状态、ARM archive 和凭证文件;专用账号/密钥的最终销毁另走获批清理。
### 1.5.2 本地隔离恢复演练
@@ -284,7 +301,7 @@ RESTORE_SCOPE=shadow \
`restore-db.sh` 和 `restore-storage.sh` 默认只接受 `shadow`。影子库名必须包含独立的 `shadow`、`drill` 或 `restore_test` 标记;数据库确认不匹配、库名不安全、附件目录非空、目标为默认试运行附件目录或归档路径危险时均在写入前退出。
真实格式影子恢复的验收不仅是 `/ready`:恢复后的 `_prisma_migrations` 成功项必须等于冻结基线(当前预期 58),预先列出的关键业务表计数必须逐表一致,附件解包后的普通文件数必须与活动源目录一致。任一项不一致即判定批次不可作为切换回滚点。
真实格式影子恢复的验收不仅是 `/ready`:恢复后的 `_prisma_migrations` 成功项必须等于执行前现场冻结基线(当前生产证据为 61 项;本地 62 项仅表示下一次部署预计新增 1 项),预先列出的关键业务表计数必须逐表一致,附件解包后的普通文件数必须与活动源目录一致。任一项不一致即判定批次不可作为切换回滚点。
### 1.5.3 生产切换前冻结记录