Files
order_site/docs/数据库备份与恢复.md

146 lines
7.2 KiB
Markdown

# 数据库备份与恢复
生产数据库(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/` 前缀,自定义权限策略:
```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/`