11 / 13
恢复与灾备
恢复套件、独立恢复(无元数据库)与三类密钥事故
常规恢复:恢复套件
AGE_IDENTITY_FILE=/path/identity.txt \
PGPASSWORD='<目标库密码>' \
sh restore-job7.sh 'postgresql://user@host:5432/restored?sslmode=require' backup-job7.dump.age脚本顺序:校验密文 SHA-256(不匹配立即退出 1,不做任何解密)→ 拒绝非空目标库
→ age 解密到临时明文 → pg_restore → 在 manifest 表数已知时做相等检查并输出。
注意恢复失败可能留下部分写入,需清理目标库后重试。目标库需事先创建(createdb)。
manifest 的 backupId 是实例内标识(job-N),不是桶内对象键(对象键使用备份 UUID)。
Supabase 的备份
这类备份的套件使用 supabase 模式恢复:需要一台带有 Supabase 扩展的服务器、一个新建的空库和超级用户连接,套件会在写入任何内容之前检查这三项。环境变量 SUPACOVE_PROFILE=generic 或 supabase 可以覆盖套件的默认值(旧名 SUPABACKUP_PROFILE 仍然有效)。完整步骤:备份 Supabase 数据库。
独立恢复:不依赖本实例
实例完全丢失时,恢复只需要:
- 对象存储(或暂存盘)中的
*.dump.age与同名*.manifest.json; - 离线保存的 age 私钥;
- 恢复套件脚本,或按 manifest 中的哈希与元数据手工执行 age 解密 +
pg_restore(灾备手册场景 3 的完整命令)。
恢复主机需要自备工具:套件会检查 age、pg_restore、psql 与 sha256sum(或 shasum),缺失即退出 3——服务镜像内置的是 age 算法库,不是恢复环境的 CLI。
manifest 是自描述的:备份标识(job-N,实例内 ID)、服务器版本、表数、SHA-256、
recipient 指纹;桶内对象键使用的备份 UUID 记录在实例元数据库中。
元数据库(SQLite)不是恢复的前提。
密钥事故(四种情形)
主密钥文件缺失,age 私钥还在
备份密文仍可解密。实例会生成一把新主密钥继续运行——已保存的连接串与 目的地凭据将无法读取,需要重新注册数据库与目的地。先尝试从备份找回原密钥, 再考虑重建。
主密钥文件损坏/权限不合规/被替换
损坏与权限不合规时启动被拒绝(fail closed),需先修复文件。若密钥内容已被 换掉(格式合法但值不同),实例可以启动,但旧凭据全部解密失败——等同于丢失; 重建时需同时重录数据库连接串、目的地凭据并重新绑定。
age 私钥丢失,主密钥还在
已有密文不可恢复。恢复未来保护的正确路径是:把旧实例的数据目录与
recipient 记录作为审计证据保全(不要再使用),在新实例(空的
recipient 配置)上执行 age init、离线保管并 age verify 校验新私钥、
重新注册数据库与目的地、恢复调度,并做一次新备份+恢复验证。注意
age init 不是轮换接口:已有 recipient 的实例会直接拒绝;若把旧
recipient 写回新实例(灾备手册场景 1 的做法仅适用于旧私钥仍可用),
新实例会继续产生无人能解密的备份。
两者都丢
已有备份不可恢复。立即:停用旧实例调度、保全数据目录与桶内对象(审计需要)、
在新实例完成 age init + 目的地凭据 + 数据库重新注册,让保护链重新开始。
与仓库灾备手册的场景对应
命令级完整流程在仓库
docs/disaster-recovery.md
(开发文档;仓库尚无 remote 时按本地路径 docs/disaster-recovery.md 打开)。场景索引:
- 场景 1(SQLite 损坏/数据卷丢失)→ 以空数据目录启动新实例、bootstrap、 (旧 age 私钥仍可用时)写回原 recipient、重新注册数据库/目的地与调度; 备份密文不依赖元数据库,历史数据恢复路径同场景 3。
- 场景 2(主密钥不可用:缺失/损坏/被替换)→ 本页“密钥事故”前两步。
- 场景 3(整台实例丢失,只剩桶内密文 + 离线 age 私钥)→ 本页“独立恢复”,
对象键为
<prefix>backups/<备份UUID>.dump.age。 - 场景 4(升级失败/迁移中断)→ pre-migrate 快照回滚、隔离旧 WAL/SHM、 启动旧版本二进制。
最后更新