# 数据库备份与恢复 生产数据库(PostgreSQL,Compose 容器)每日备份到阿里云 OSS,与图片存储共用 OSS 基础设施,但使用独立的 `backups/` 前缀与(建议)独立的 RAM 凭证。 ## 备份内容与存放 | 内容 | 方式 | OSS key 前缀 | | --- | --- | --- | | PostgreSQL 全库 | `pg_dump -Fc`(custom 格式,自带压缩,MVCC 快照无需停机) | `backups/postgres/` | | 平台 JSON 配置(`apps/backend/data`,排除日志/缓存) | `tar -czf` | `backups/configs/` | - 每个备份对象附带 `.manifest.json` 清单:大小、sha256、创建时间、数据库版本、已应用迁移列表。没有有效清单的备份默认不会被 `latest` 选择,恢复也会拒绝;只有明确使用 `--allow-unverified` 才兼容历史备份。 - 不备份的内容:图片(已在 OSS)、Caddy 证书(自动重新签发)、日志(`apps/backend/data/logs`)。 - PostgreSQL dump 是数据库内部一致快照,但数据库与 OSS 不共享事务;备份时刻附近的新图片可能只存在一侧。恢复后应按 `file_assets.object_key` 抽查 OSS 对象,并为图片桶开启版本控制/保留策略。 - 服务器本地在 `backups/` 目录保留最近 `DB_BACKUP_KEEP` 份(默认 7),OSS 端过期由桶生命周期规则管理。 - 涉及脚本:`deploy/backup-db.sh`(备份)、`deploy/restore-db.sh`(恢复/演练)、后端镜像内 `dist/db-backup.js`(OSS 传输与校验)。 ## 首次开通 以下步骤在阿里云控制台与服务器上各执行一次。 ### 1. RAM 授权(建议独立,可直接复用现有凭证) 现有 `STORAGE_OSS_*` 的 RAM 用户拥有整个桶的权限,也能直接跑备份。但备份包含全部订单与用户数据,是最敏感的资产,建议单独创建一个 RAM 用户 `order-site-backup`,只授予 `backups/` 前缀,自定义权限策略: ```json { "Version": "1", "Statement": [ { "Effect": "Allow", "Action": ["oss:PutObject", "oss:GetObject"], "Resource": ["acs:oss:*:*:你的桶名/backups/*"] }, { "Effect": "Allow", "Action": ["oss:HeadBucket"], "Resource": ["acs:oss:*:*:你的桶名"] }, { "Effect": "Allow", "Action": ["oss:ListObjects"], "Resource": ["acs:oss:*:*:你的桶名"], "Condition": { "StringLike": { "oss:Prefix": ["backups/", "backups/*"] } } } ] } ``` 为该用户创建 AccessKey,填入服务器 `.env` 的 `DB_BACKUP_OSS_ACCESS_KEY_ID` / `DB_BACKUP_OSS_SECRET_ACCESS_KEY`(不填则回退 `STORAGE_OSS_*`)。若用独立备份桶,同时覆盖 `DB_BACKUP_OSS_ENDPOINT` / `DB_BACKUP_OSS_BUCKET` / `DB_BACKUP_OSS_REGION`。 ### 2. 桶生命周期规则(控制存储成本) 在 OSS 控制台为目标桶添加生命周期规则: - 前缀 `backups/`:最后一次访问 90 天后删除(按需调整,数据库每日一份,90 份足够覆盖一个季度)。 - 建议同时开启版本控制:误删备份时可找回旧版本;给非当前版本配置 7 天过期,避免版本堆积。 ### 3. 手动执行一次,验证链路 ```bash bash deploy/backup-db.sh bash deploy/restore-db.sh --list bash deploy/restore-db.sh --verify ``` `--verify` 会把最新带有效清单的备份恢复到一个隔离的临时 PostgreSQL 容器(不接入业务网络,不影响线上库),并打印迁移列表与各表行数。建议之后每月执行一次演练——没验证过的备份等于没有备份。 ### 4. 配置每日定时备份 ```bash crontab -e ``` ```cron # 每天 03:00 备份数据库与配置到 OSS(目录需先存在,首次开通时脚本已创建) 0 3 * * * cd /srv/order_site && bash deploy/backup-db.sh >> backups/backup.log 2>&1 ``` 把 `/srv/order_site` 换成实际项目目录。日志追加在 `backups/backup.log`,可用 logrotate 管理;备份失败时 cron 不通知,请定期查看该文件,或后续接入告警 webhook。 ## 日常运维 - **部署自动备份**:`bash deploy/ubuntu-deploy.sh` 在每次执行数据库迁移前会自动备份一次;`--reset-db` 会在删除数据卷前抓最后一份旧库备份,并跳过重置后空库备份,避免空库被误选为 latest。备份失败会中止部署,确认为可接受后可用 `--skip-backup` 继续。 - **手动备份**:`bash deploy/backup-db.sh`(可加 `--keep N` 覆盖本地保留份数)。 - **查看备份列表**:`bash deploy/restore-db.sh --list`。 - **恢复演练**:`bash deploy/restore-db.sh --verify`(可加 `--key` 指定备份)。 - **兼容历史备份**:仅在确认对象来源可信时加 `--allow-unverified`,不要作为生产默认参数。 ## 恢复 ### 场景 A:整库恢复到最新备份(最常用) ```bash bash deploy/restore-db.sh ``` 流程:下载最新备份并强制校验 sha256 → 停 backend/web 并确认已停止 → `pg_restore --clean --if-exists` → 执行迁移补差 → 重启并等待健康检查。任一步失败会尝试恢复原先运行的服务,并明确报告数据库可能处于部分恢复状态。执行前需交互输入 `RESTORE` 确认(非交互终端用 `RESTORE_CONFIRM=1` 或 `--yes`)。 如需同时恢复 `apps/backend/data` 配置: ```bash bash deploy/restore-db.sh --restore-configs ``` 也可以用 `--config-key` 指定配置归档。配置归档会先校验路径,再解包到 data 目录。 ### 场景 B:恢复到指定时间点 ```bash bash deploy/restore-db.sh --list bash deploy/restore-db.sh --key backups/postgres/order_site-20260823-030000.dump ``` ### 场景 C:服务器全损(数据卷丢失/换机) 在新服务器完成部署(`docs/部署启动与数据库重建.md`)后,把旧服务器 `.env`(含 OSS 凭证)恢复到新服务器,再执行场景 A。图片不受影响(在 OSS),平台 JSON 配置可从 `backups/configs/order_site-<时间戳>.tar.gz` 解回 `apps/backend/data/`。 ### 场景 D:外部数据库(DATABASE_URL 非 postgres 服务) `backup-db.sh` 与 `restore-db.sh` 会自动识别 `.env` 的 `DATABASE_URL`:指向外部库时,从 postgres 容器经网络直连执行 pg_dump/pg_restore,因此服务器仍需有可用的 postgres 镜像/容器作为客户端。恢复外部库前会额外告警确认目标。 ### 恢复后检查 - 打开后台登录、近期订单列表,抽查数据到备份时间点为止。 - `bash deploy/restore-db.sh --list` 确认下一次定时备份已恢复产生新对象。 ## 故障排查 | 现象 | 处理 | | --- | --- | | `[db-backup] HeadBucket 403` | RAM 策略缺 `oss:HeadBucket`,按上文策略补齐 | | `ListObjects 403` | 策略缺 `oss:ListObjects` 或 `oss:Prefix` 条件与实际前缀不匹配 | | `PutObject 403` | `Resource` 里的桶名/前缀与 `.env` 的 `DB_BACKUP_OSS_BUCKET` / `DB_BACKUP_OSS_PREFIX` 不一致 | | `缺少 OSS 配置` | `.env` 里 `DB_BACKUP_OSS_*` 与 `STORAGE_OSS_*` 都为空;改完后备份脚本用的是镜像内程序,若同时改了代码需重新构建 | | 下载校验 `sha256 校验失败` | 备份对象被篡改或截断,换更早的备份重试 | | `pg_restore` 报版本错误 | 不会发生在本编排内(pg_dump/pg_restore 与库同镜像);仅外部库场景需保证客户端 ≥ 服务端大版本 | ## 安全注意 - 备份 RAM 凭证只进服务器 `.env`(已 gitignore),不要提交到仓库。 - 备份桶/前缀保持私有,不要开公共读写;`backups/` 下的对象不要通过前端暴露。 - 若备份桶与图片桶相同,确认图片访问逻辑只读图片前缀,不会枚举 `backups/`。