7.2 KiB
数据库备份与恢复
生产数据库(PostgreSQL,Compose 容器)每日备份到阿里云 OSS,与图片存储共用 OSS 基础设施,但使用独立的 backups/ 前缀与(建议)独立的 RAM 凭证。
备份内容与存放
| 内容 | 方式 | OSS key 前缀 |
|---|---|---|
| PostgreSQL 全库 | pg_dump -Fc(custom 格式,自带压缩,MVCC 快照无需停机) |
backups/postgres/ |
平台 JSON 配置(apps/backend/data,排除日志/缓存) |
tar -czf |
backups/configs/ |
- 每个备份对象附带
<key>.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/ 前缀,自定义权限策略:
{
"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 deploy/backup-db.sh
bash deploy/restore-db.sh --list
bash deploy/restore-db.sh --verify
--verify 会把最新带有效清单的备份恢复到一个隔离的临时 PostgreSQL 容器(不接入业务网络,不影响线上库),并打印迁移列表与各表行数。建议之后每月执行一次演练——没验证过的备份等于没有备份。
4. 配置每日定时备份
crontab -e
# 每天 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 deploy/restore-db.sh
流程:下载最新备份并强制校验 sha256 → 停 backend/web 并确认已停止 → pg_restore --clean --if-exists → 执行迁移补差 → 重启并等待健康检查。任一步失败会尝试恢复原先运行的服务,并明确报告数据库可能处于部分恢复状态。执行前需交互输入 RESTORE 确认(非交互终端用 RESTORE_CONFIRM=1 或 --yes)。
如需同时恢复 apps/backend/data 配置:
bash deploy/restore-db.sh --restore-configs
也可以用 --config-key 指定配置归档。配置归档会先校验路径,再解包到 data 目录。
场景 B:恢复到指定时间点
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/。