跳到主要内容

数据库备份与恢复

本文档描述 Open ACE 的数据库备份与恢复策略,覆盖 Kubernetes 部署(k8s/extras/backup/)与 Docker Compose 部署(docker-compose.yml)。

Issue #1853:Kubernetes 数据库备份参考实现

概览​

Open ACE 提供可选的数据库备份设施:

  • 通过 CronJob 定时备份 PostgreSQL(Kubernetes)
  • 手动 pg_dump/pg_restore 流程(Docker Compose)
  • 使用 pg_restore --list 校验完整性
  • 支持多种对象存储后端(AWS S3、MinIO 等)
  • 用于灾难恢复的恢复 Job(Kubernetes)

部署选项​

选项环境备份责任方典型 RPO
托管 PostgreSQL(RDS/Aurora、Cloud SQL、Azure DB、阿里云 RDS)有 SLA 的生产环境云厂商(自动备份、PITR)< 5 分钟
自管 CronJob(k8s/extras/backup/)Kubernetes、成本敏感本仓库清单6–24 小时(取决于 CronJob 频率)
在 postgres-data 卷上手动 pg_dumpDocker Compose 单机你自己,按本指南操作取决于你的排程

Kubernetes 备份(自管)​

必读:对象存储 Secret 不在 kustomization 内​

k8s/extras/backup/kustomization.yaml 的 resources 只包含:

  • serviceaccount.yaml
  • rbac.yaml
  • backup-script-configmap.yaml
  • cronjob.yaml
  • restore-job.yaml

凭据文件(从 secret-s3.yaml.example 复制的 secret-s3.yaml,或从 secret-minio.yaml.example 复制的 secret-minio.yaml)不在其中。如果照搬"复制示例后 kubectl apply -k ."的老步骤,apply 会成功、CronJob 会创建,但之后每个备份任务都会在上传一步失败——因为 CronJob 引用的 backup-storage-credentials Secret 根本不存在;除非查看任务日志,否则该失败是静默的。

先显式 apply Secret,再 apply kustomization:

cd k8s/extras/backup/

# 1. 创建并编辑凭据
cp secret-s3.yaml.example secret-s3.yaml
# 编辑 secret-s3.yaml,填入你的 S3 凭据

# 2. 显式 apply Secret(kustomization.yaml 不包含它)
kubectl apply -f secret-s3.yaml

# 3. apply 其余备份清单
kubectl apply -k .

# 4. 同时核对 CronJob 与 Secret
kubectl get cronjob -n open-ace
kubectl get secret backup-storage-credentials -n open-ace

手动触发备份(测试)​

# 从 CronJob 创建一次性 Job
kubectl create job --from=cronjob/postgres-backup manual-backup-$(date +%Y%m%d) -n open-ace

# 查看日志
kubectl logs -f job/manual-backup-$(date +%Y%m%d) -n open-ace

CronJob 参数​

以下数值是 k8s/extras/backup/cronjob.yaml 实际发布的内容:

参数值说明
Schedule0 2 * * *每天 02:00 UTC(cronjob.yaml:18)
activeDeadlineSeconds36001 小时超时;大库可提高到 7200(cronjob.yaml:33)
CPU 请求/上限200m / 500m(cronjob.yaml:132-137)
内存 请求/上限256Mi / 512Mi超过 1GB 的库建议提到 1Gi(cronjob.yaml:132-137)
concurrencyPolicyForbid禁止重叠运行
backoffLimit2失败最多重试 2 次
successfulJobsHistoryLimit / failedJobsHistoryLimit3 / 3保留以便排查
镜像alpine:3.19(init 容器:把 AWS CLI 装进 emptyDir)+ postgres:15-alpine(主容器)postgres:15-alpine 不含 AWS CLI

备份内容​

组件方法频率保留
PostgreSQL 数据库pg_dump -Fc(custom 格式)每天30 天
PostgreSQL 全局对象pg_dumpall --globals-only每天30 天
Redis(可选,BACKUP_REDIS)RDB 快照每天7 天

备份完整性校验​

每次备份都自动做完整性校验:

  1. 备份创建后: pg_restore --list db.dump 验证文件可解析
  2. 校验失败: Job 以退出码 1 结束,触发 CronJob 重试
  3. 成功标志: Job 以退出码 0 结束,备份上传到对象存储
# 手动校验
pg_restore --list backup.dump > /dev/null && echo "Valid" || echo "Corrupted"

对象存储凭据​

AWS S3(secret-s3.yaml):

# secret-s3.yaml
apiVersion: v1
kind: Secret
metadata:
name: backup-storage-credentials
namespace: open-ace
type: Opaque
stringData:
AWS_ACCESS_KEY_ID: "AKIAIOSFODNN7EXAMPLE"
AWS_SECRET_ACCESS_KEY: "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"
AWS_REGION: "us-east-1"
S3_BUCKET: "my-open-ace-backups"
S3_ENDPOINT: "" # Leave empty for AWS S3

MinIO(secret-minio.yaml,来自 secret-minio.yaml.example):

# secret-minio.yaml
apiVersion: v1
kind: Secret
metadata:
name: backup-storage-credentials
namespace: open-ace
type: Opaque
stringData:
AWS_ACCESS_KEY_ID: "minioadmin"
AWS_SECRET_ACCESS_KEY: "minioadmin"
AWS_REGION: "us-east-1"
S3_BUCKET: "open-ace-backups"
S3_ENDPOINT: "http://minio.example.com:9000"

恢复流程(Kubernetes)​

# 第 1 步:在对象存储中定位备份
aws s3 ls s3://your-bucket/open-ace/

# 第 2 步:下载
aws s3 cp s3://your-bucket/open-ace/20240115/backup.tar.gz .
tar -xzf backup.tar.gz

# 第 3 步:校验完整性
pg_restore --list db.dump

# 第 4 步:停掉应用写入,然后 apply 恢复 Job
kubectl scale deployment open-ace --replicas=0 -n open-ace
export RESTORE_TIMESTAMP=20240115
kubectl apply -f restore-job.yaml
kubectl logs -f job/postgres-restore -n open-ace

# 第 5 步:重启并验证应用
kubectl scale deployment open-ace --replicas=3 -n open-ace
kubectl exec -it deployment/open-ace -n open-ace -- \
curl -s http://localhost:19888/health

备份 Job 与现有 NetworkPolicy 兼容(PostgreSQL 5432、Redis 6379、外部 HTTPS 443 出站均已放行);无需修改 NetworkPolicy。

监控与排障​

# 列出最近的备份 Job
kubectl get jobs -n open-ace -l app.kubernetes.io/component=backup

# 查看最近 Job 状态
kubectl describe job -n open-ace -l app.kubernetes.io/component=backup

# 查看 Job 日志
kubectl logs job/postgres-backup-$(date +%Y%m%d) -n open-ace

常见故障:

症状原因修复
Job 运行、dump 成功、上传报凭据缺失secret-s3.yaml 从未 apply(它不在 kustomization 内)kubectl apply -f secret-s3.yaml
大库上 Job 被 OOM 杀死512Mi 内存上限过低把 CronJob 内存上限补丁到 1Gi
CronJob 从不启动被挂起,或上一个 Job 仍在 1 小时死线内检查 suspend 标志与 activeDeadlineSeconds

Docker Compose 备份与恢复​

Compose 栈运行 PostgreSQL 15(postgres:15-alpine),全部数据落在名为 postgres-data 的卷上,挂载于 /var/lib/postgresql/data。默认值来自环境变量:用户 ${DB_USER:-ace}、数据库 ${DB_NAME:-ace}。用 pg_dump 备份;PostgreSQL 运行期间切勿直接拷贝卷目录。

备份​

# custom 格式 dump,从容器流式导出(-T 关闭 TTY 以便重定向)
mkdir -p ./backups
docker compose exec -T postgres pg_dump -U ${DB_USER:-ace} -d ${DB_NAME:-ace} -Fc \
> ./backups/ace-$(date +%Y%m%d-%H%M).dump

# 验证可解析
pg_restore --list ./backups/ace-$(date +%Y%m%d-%H%M).dump > /dev/null && echo "Valid"

纯 SQL dump(可 grep、体积更大):

docker compose exec -T postgres pg_dump -U ${DB_USER:-ace} -d ${DB_NAME:-ace} \
> ./backups/ace-$(date +%Y%m%d-%H%M).sql

恢复​

# 1. 停掉写入方(app 与 scheduler,若启用)
docker compose stop app scheduler

# 2. 覆盖恢复到现有数据库(仅 custom 格式)
cat ./backups/ace-20260929-0200.dump \
| docker compose exec -T postgres pg_restore -U ${DB_USER:-ace} -d ${DB_NAME:-ace} --clean --if-exists

# 3. 重启并验证
docker compose start app scheduler
docker compose exec -T postgres psql -U ${DB_USER:-ace} -d ${DB_NAME:-ace} -c "SELECT version();"
curl http://localhost:19888/health

恢复到全新卷​

如果 postgres-data 卷整体丢失:

docker compose down
docker volume rm open-ace_postgres-data # 如 compose 项目前缀不同请相应调整
docker compose up -d postgres # 按环境变量重建空数据库
cat ./backups/ace-20260929-0200.dump \
| docker compose exec -T postgres pg_restore -U ${DB_USER:-ace} -d ${DB_NAME:-ace}
docker compose up -d

任何恢复之后,若备份早于当前模式,请运行 alembic upgrade head(见 ../dev/SCHEMA_MIGRATION_GUIDE.md)。

定时备份(可选)​

在 Docker 宿主机上配置 cron:

0 2 * * * cd /path/to/open-ace && docker compose exec -T postgres pg_dump -U ace -d ace -Fc > backups/ace-$(date +\%Y\%m\%d).dump

多租户说明​

pg_dump 会自动捕获所有租户——无需特殊备份配置。恢复之后,验证所有租户存在,并运行应用层权限校验。

恢复演练清单​

  1. 从对象存储(或 ./backups/)下载最新备份
  2. 校验完整性(pg_restore --list)
  3. 恢复到测试数据库
  4. 验证行数与模式校验
  5. 用恢复出的数据库启动应用并跑冒烟测试
  6. 记录实际达成的 RTO

相关文档​