文件
hyb-oa/.trellis/tasks/08-23-isolated-backup-restore-drill/implement.md
T

15 KiB

隔离备份恢复演练实施计划

执行前门禁

  • 确认任务已在用户审阅最终规划摘要后由 task.py start 进入 in_progress。
  • 只读确认本地 main、当前生产构建事实、来源/当前 migration 集合和现有恢复脚本;现场两构建均为 65 且目录无变化,但后续执行不硬编码数量。
  • 只读确认生产公网 /ready 正常;不读取生产数据库或执行生产写入。
  • 连接 ARM 后只读核对 receiver 配置有效、最新正式日批次身份、dead-man 状态、Docker 可用性和磁盘余量;不列出完整目录或原始 manifest。
  • 磁盘安全预算至少满足“批次总字节数 5 倍 + 2 GiB”;不足则 PAUSE。
  • 若当前账号没有对正式归档只读和本轮临时区/Docker 的最小权限,停止并给用户一条窄权限操作步骤;不得申请通用 root shell 或扩大既有健康监控 sudo 入口。

有序执行清单

  1. 生成不含主机信息的本轮 run id,记录 UTC 开始时间和生产前置 /ready 状态。
  2. 在 ARM 正式日归档中选择 manifest 时间最新的 complete 批次,验证三文件、字节数、SHA-256 与 24 小时 RPO 门禁;在本地仓库确认来源提交是当前构建祖先或相同,并单独记录是否严格相等。
  3. 创建权限 0700 的本轮临时工作区;启动 RTO 计时,复制整个批次并对临时副本重新执行同一校验。
  4. 创建无宿主端口、无业务网络、无持久 volume 的一次性 PostgreSQL 16 容器;数据库名含 drill,凭据随机且仅存在本轮进程环境。
  5. 使用 scripts/restore-db.sh 的 shadow 合同恢复数据库;任何警告、确认不匹配或 restore 非零立即停止后续验证并进入清理。
  6. 从来源提交动态冻结 migration 名称集合;通过错误即停的只读 SQL 验证恢复库成功且未回滚的 migration 集合精确匹配来源、无失败/未知项,并静态记录来源与当前构建的 migration 差异;随后验证固定 12 类关键表并记录行数。
  7. 使用 scripts/restore-storage.sh 恢复附件到本轮空目录;比较归档与解包普通文件数。
  8. 在影子库中聚合本地附件引用,以不输出路径的校验程序验证路径安全、文件存在及适用的大小/SHA-256;记录引用总数和不一致数。
  9. 所有数据检查通过时停止 RTO 计时;按 manifest 时间计算 RPO,记录当前发布到首个同源恢复点的可用滞后,形成区分“原始数据可恢复性”和“当前 schema 升级状态”的 GO/PAUSE/NO_GO 脱敏结论。
  10. 清理本轮容器、临时网络(若有)、临时附件目录、临时批次副本和临时凭据;确认无本轮残留。
  11. 再次只读检查生产公网 /ready 和 ARM dead-man/健康 timer 状态,确认演练没有影响在线生产与现有监控。
  12. 将证据写入当前 task,并在既有事实源补充当前状态;不提交任何真实备份、日志或业务内容。

Attempt 1 执行状态

  • 最新 ARM 候选在 select_latest_batch 阶段因 manifest build_mismatch 失败关闭;复制、容器创建、数据库恢复和附件恢复均未开始。
  • 失败后的只读复核确认:公网 /ready 与生产自动备份 unit 的构建短标识一致,两个生产侧事实均为 7b86ffb3;因此当前不是计划事实源过期或备份 unit 冻结构建漂移。
  • ARM 一次性 root 入口和本轮 runtime 均为零残留,两个监控 timer 仍为 active;本轮 5 个普通用户暂存文件已按 SHA-256 精确匹配并删除,未宽泛删除目录。
  • Attempt 1 保持 NO_GO。在当时取得最新 ARM manifest 的脱敏构建短标识、run id 配对关系和 manifest 摘要一致性前,不进入第 3 步及之后的恢复流程。

Attempt 1 后只读调查清单

  1. 生成固定无参数的 root-owned ARM 只读身份探针和最小 sudoers,只允许读取 receiver 路径配置及最新正式日归档。
  2. 本地执行 bash -n、候选 SHA-256、sudoers candidate visudo(环境可用时)和禁止写入/恢复关键字人工复核。
  3. 上传到 ARM 普通用户私有暂存目录;由用户输入 sudo 密码安装固定入口,安装前再次核对全部候选 SHA-256 和系统 sudoers 语法。
  4. 运行一次固定入口,记录构建短标识、批次年龄、manifest 配对、产物聚合完整性和入口自清理状态。
  5. 对照公网 /ready 与生产自动备份 unit 的当前短标识:相等时记录同源恢复点已形成;不相等时在本地仓库核对是否为当前构建祖先,祖先且完整/新鲜仍可形成原始恢复重试方案,未知或非祖先才 NO_GO。
  6. 按 SHA-256 精确删除普通用户暂存文件;不得删除 ARM 正式归档或宽泛清理目录。

只读调查执行结果

  • 探针候选通过本地/ARM SHA-256、Bash 语法和 ARM sudoers candidate 校验。
  • 固定入口只读读取最新正式批次并自删除;未复制、解包、启动容器或恢复数据。
  • 最新批次 ec682565 在 24 小时内,run id/目录配对及 DB/附件大小、SHA-256 均通过,但与当前 7b86ffb3 不匹配。
  • 只读对时确认自动备份合同刷新晚于最新批次生成;finding 收敛为“发布后尚未产生新构建恢复点”。
  • root 入口残留为 0,3 个普通用户候选及暂存目录已按精确身份清理,两个 ARM 监控 timer 仍 active。
  • 08-24 最新自然批次只读探针通过:来源与当前构建相同、年龄不超过 24 小时、run id 配对及 DB/附件 SHA-256 均通过;临时入口和暂存零残留。当前可提交恢复重试审批,尚未获得恢复授权。

Attempt 2 恢复重试结果

  • 用户批准后安装并执行动态 migration 集合版固定恢复入口。
  • ARM 正式归档最新批次首次门禁通过,并开始复制到本轮私有临时区。
  • 临时副本复检因 runner 错把正式源目录 basename 身份规则应用到固定名 batch 而触发 batch_identity;失败阶段为 create_isolated_runtime,数据库容器和恢复均未开始。
  • runtime、root 入口、6 个普通用户候选和暂存目录均精确清理为零残留,两个 ARM 监控 timer 保持 active。
  • 修订 runner:正式源目录继续强制 basename/run id 配对;临时副本 manifest run id 重绑到源侧已验证 run id,并继续校验完整内容合同及源/副本元数据精确一致。补齐 2 正例 + 3 负例后重新执行只读身份探针,实际恢复另行批准。

Attempt 3 准备批准边界

  • 用户已批准修复 runner,并重新执行 08-25 最新正式批次只读身份探针。
  • 回归已覆盖:正式源通过、同内容改名副本通过、内容篡改副本失败、源 basename/run id 错配失败、副本 run id 与源侧已验证 run id 不同失败。
  • Attempt 3 未复用 Attempt 2 新鲜度:08-25 最新批次年龄约 6 小时 11 分,来源与当前构建相同,run id 配对、大小与 SHA-256 均通过。
  • 用户复核探针后明确批准 Attempt 3 实际恢复,范围仅限 ARM 隔离影子恢复。

Attempt 3 恢复结果与停止点

  • 修复后的候选在本地和 ARM 通过 SHA-256、Bash 与 sudoers 语法校验后安装;正式归档门禁和临时副本源侧身份重绑通过。
  • runner 在 restore_database 阶段因 restore-db.sh 返回非零而以 database_restore_failed 失败关闭;migration、12 类关键表、附件恢复与关联验证均未执行。
  • 一次性容器、本轮 runtime、临时 root 入口和普通用户候选均精确清理为零残留;ARM 两个监控 timer 保持 active,生产演练后 /ready 仍为相同构建的 PASS。
  • 在任何 Attempt 4 前修复失败诊断合同:分别记录 dump 列表校验与实际 restore 的脱敏阶段码,并确保失败清理前只保留安全聚合诊断;本地回归已通过,远端探针和实际恢复仍需重新批准。

Attempt 4 前的修复清单

  1. runner 在调用 restore-db.sh 前,以同一 PostgreSQL 16 容器对临时副本执行独立 pg_restore --list,记录 dump_list=PASS|FAIL|NOT_RUN。
  2. 独立确认影子目标数据库名与连接状态,记录 target_confirmation=PASS|FAIL|NOT_RUN;不输出数据库名或连接信息。
  3. 仅在前两项通过后执行实际 restore,记录 restore_apply=PASS|FAIL|NOT_RUN;失败类别区分为 dump_list_failed、restore_target_confirmation_failed 或 restore_apply_failed_after_list_pass。
  4. 原始 stderr 只进入本轮 0600 私有日志并随 runtime 删除;最终输出只含 allowlist 阶段状态和既有脱敏聚合。
  5. 补三条阶段负例、敏感输出负例和每条失败路径零残留回归;通过后重新执行最新批次身份探针,再请求 Attempt 4 实际恢复批准。

Attempt 3 后本地修复结果

  • dump_list、target_confirmation、restore_apply 固定输出 PASS/FAIL/NOT_RUN,并增加执行路径、脚本/pg_restore 退出码、对象错误计数与稳定类别。
  • 诊断在清理前输出;注入 apply 退出码 23 后,调用方在 runtime/access 清理完成后仍可读取阶段状态和退出码。
  • partial-success 规则保持失败关闭:任何 pg_restore 非零均为 FAIL/NO_GO,不设置可忽略对象错误白名单。
  • PostgreSQL 16 custom dump 直接驱动真实 scripts/restore-db.sh 的 Docker stdin 与 host-file 两条路径,均恢复成功。
  • dump-list 失败、目标确认失败、apply 失败及敏感输出负例通过;既有成对备份/恢复回归和自动异机备份合同通过。
  • 用户另行批准并完成 Attempt 4 前最新批次只读探针:构建 SAME、年龄约 7 小时 24 分、run id/产物完整性与 migration 集合门禁通过,入口及暂存零残留;尚未执行实际恢复。

Attempt 4 前的持久化门禁

  1. 将已验证的 runner 和附件校验器从个人 workspace 收敛为 scripts/qa/ 下的跟踪文件,不改变恢复边界或生产脚本。
  2. 将八类 runner 回归收敛为可独立执行的 run-offhost-shadow-restore-runner-contract.sh,直接驱动真实 restore-db.sh 双路径。
  3. 合同脚本必须机械验证三阶段诊断、执行路径、两个退出码、非零失败关闭、源/副本身份、dynamic migration、敏感输出和清理顺序;对候选生成稳定 SHA-256。
  4. 运行新合同、既有成对备份/恢复、自动异机备份 74 项、Trellis 治理与敏感扫描;全部通过后才允许把跟踪文件打包为 Attempt 4 候选。
  5. 本阶段仅本地修改与测试;新批次探针结果和持久化门禁均通过后,再单独申请 Attempt 4 实际恢复。

Attempt 4 前持久化门禁结果

  • runner 已收敛为 scripts/qa/offhost-shadow-restore-runner.template.sh;现场路径、账号、容器/数据库命名、timer、构建和 migration 指纹均为经严格校验的私有渲染字段。
  • 附件关联校验器已收敛为 scripts/qa/validate-offhost-shadow-restore-attachments.py。
  • scripts/qa/run-offhost-shadow-restore-runner-contract.sh 可独立复跑并提供 render 模式;相同私有配置生成确定性候选 SHA-256,shell 注入在输出候选前失败关闭。
  • 源/副本身份、内容篡改、dynamic migration、附件安全、三阶段诊断、真实 restore 双路径、NOT_RUN、退出码、清理顺序和敏感输出合同全部通过。
  • 新增三个文件由 check-sensitive-content.mjs 定向扫描,3/3 文件、finding 0;本地测试容器残留 0。
  • .gitignore 增加 /.restore-drill-private/ 专用目录;合同分别证明仓库内未忽略的私有 JSON、未忽略的现场候选均在创建输出前失败,ignored 输入/输出正例通过。
  • 私有原始日志保留唯一初始化截断,五个固定阶段均以标记分隔并追加写;静态合同拒绝后续阶段再次截断。
  • 完整复跑 runner 合同、既有隔离备份/恢复演练和自动异机备份 74 项;task、Trellis、process、默认/定向敏感扫描及空白门禁通过,测试容器和本地私有路径残留均为 0。
  • Attempt 4 最新批次只读探针重新执行并通过;候选已由提交 5ec7db1 的跟踪模板确定性渲染,完整合同、依赖钉住和 SHA-256 复核通过。
  • Attempt 4 候选尚未上传或安装;实际恢复须在本次探针和候选证据审查后另行取得明确批准。

验证命令类别

  • 本地/仓库:bash -n 覆盖备份、DB 恢复、附件恢复和 receiver 脚本;只读统计 migration 目录。
  • ARM 前置:receiver validate-config、systemd 公共状态、Docker 版本/容量、候选 manifest 脱敏校验。
  • 恢复输入:sha256sum、manifest schema/文件集/字节数校验、pg_restore --list、tar -tzf/tar -tvzf。
  • DB 验证:psql -v ON_ERROR_STOP=1 的 migration 名称/状态集合和固定表计数查询;本地用 Git 比较来源/当前 migration 目录,不执行 migrate。
  • 附件验证:find 普通文件计数及不输出路径的 Python 只读校验;只输出引用数和 mismatch 数。
  • 任务门禁:
    • python3 ./.trellis/scripts/task.py validate .trellis/tasks/08-23-isolated-backup-restore-drill
    • node scripts/qa/check-trellis-governance.mjs
    • node scripts/qa/check-process-contract.mjs --baseline --json
    • node scripts/qa/audit-docs.mjs --check-registry
    • node scripts/qa/check-sensitive-content.mjs
    • git diff --check

具体远端命令在执行阶段依据 ARM 已安装的 root-owned receiver 配置生成,不把真实地址、账号、目录或凭据写入本计划。

停止条件

  • 最新批次不存在、超过 24 小时、manifest 或 SHA-256 不一致,或来源提交未知/不是当前构建祖先或相同。仅“不严格相等”不再单独停止。
  • 权限、Docker、PostgreSQL 16 或磁盘预算不满足。
  • 命令目标可能指向腾讯云生产、ARM 正式归档、receiver 管理目录或既有容器/网络。
  • DB restore、migration、固定表、附件安全、文件引用、哈希/大小任一检查失败。
  • 生产 /ready 在演练期间异常,或现有 ARM dead-man/健康监控状态改变。
  • 清理对象身份不明确。此时保留现场并报告,不使用宽泛删除命令。

变更与证据文件边界

  • 允许写入:当前 Trellis task、既有 OPS7 Runbook/人工试运行报告中的脱敏状态摘要、本轮 ARM 私有临时区和一次性 Docker 对象。
  • 禁止写入:产品代码、migration、生产/ARM 正式配置、生产 DB/附件、ARM 正式归档、企业微信。
  • 计划新增的 task 内证据:research/restore-drill-evidence.md;只在实施实际发生后创建。

审查与收口

  • 本任务涉及真实备份数据和远端运维边界。执行完成后做一次只读复核,重点确认:生产零变更、归档未修改、RTO/RPO 口径正确、附件验证没有漏项、敏感内容未进入证据、临时对象已清理。
  • 若现有脚本需要修改,回到规划阶段补充设计和新的批准;不得在远端临时放宽 shadow/路径/确认门禁。
  • 门禁通过后用中文 commit 引用 08-23-isolated-backup-restore-drill,是否推送仍按用户当时授权执行。