Frappe 站点数据库备份恢复流程

TL;DR

站点数据库每日自动备份到 Press VPS (/backups/m1-mariadb/m1-all-YYYYMMDD.sql.gz),保留 7 天。恢复时从全量备份中提取目标站点的 SQL,恢复到 m1 上的新数据库,bench 容器挂载后站点即刻恢复。

现状:✅ 已通过端到端验证(demo2 站点备份 → 恢复库 → 数据逐项对比通过)


🎯 背景:为什么需要这个

  • 备份:m1 MariaDB 上跑着所有站点的数据库。如果 m1 磁盘故障或误删数据,需要有可恢复的备份
  • 当前覆盖:cron 每天 3:30 从 Press VPS 连 m1 做全量 mysqldump,保存到 /backups/m1-mariadb/,保留 7 天
  • 未覆盖:n1/f1 VPS (45.55.93.224) 上 bench 容器内的文件(app 代码、站点配置文件、上传文件)没有备份。此流程仅覆盖数据库层
  • 恢复验证:2026-07-06 用 demo2 站点完成了端到端恢复测试,确认备份可恢复、数据完整

✅ 前置条件

  • Press VPS (104.236.24.124) 的 SSH 访问权限
  • m1 VPS (159.65.38.50) 的 SSH 访问权限(通过 Press 容器跳板)
  • 备份文件存在于 /backups/m1-mariadb/
  • 知道目标站点的数据库名(查询方法见下方速查)

🏗️ 备份架构

m1 MariaDB (159.65.38.50)
  │  所有站点数据库: _067bd10f1b718d20 (gottenergy)
  │                  _6ddc22772f5bd6f5 (demo2)
  │                  _e56fcf0604db8cdd (...)
  │
  └── cron (Press VPS, 每天 3:30)
        │  mysqldump --all-databases
        │  压缩为 m1-all-YYYYMMDD.sql.gz
        ▼
      /backups/m1-mariadb/ (Press VPS, 保留 7 天)
  • 全量备份(所有库一个文件),12MB 压缩后
  • 恢复时用 sed 提取单个站点数据库的 SQL 段
  • Press 卷 (/backups/press-volumes/) 单独备份,每天 3:00

🛠️ 恢复步骤

第 1 步:找到目标站点的数据库名

# 在 bench 容器内查
docker exec bench-0003-000007-f1 bench --site demo2.getgrowth.app execute "frappe.conf.db_name"
# → _6ddc22772f5bd6f5

第 2 步:在 m1 上创建恢复目标库

docker exec -i $(docker ps -qf "name=press-backend") ssh root@159.65.38.50 "
  mariadb -e 'CREATE DATABASE _recovered CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci'
"

第 3 步:从全量备份提取目标站点并恢复

zcat /backups/m1-mariadb/m1-all-20260706.sql.gz \
  | sed -n '/^USE `_6ddc22772f5bd6f5`/,/^USE `/p' \
  | sed 's/`_6ddc22772f5bd6f5`/`_recovered`/g' \
  | docker exec -i $(docker ps -qf "name=press-backend") ssh root@159.65.38.50 "mariadb"

第 4 步:授权 MariaDB 用户访问新库

docker exec -i $(docker ps -qf "name=press-backend") ssh root@159.65.38.50 "
  mariadb -e 'GRANT ALL PRIVILEGES ON _recovered.* TO \"_6ddc22772f5bd6f5\"@\"%\"' &&
  mariadb -e 'FLUSH PRIVILEGES'
"

第 5 步:在 bench 容器创建恢复站点

docker exec -u root bench-0003-000007-f1 bash -c '
  mkdir -p /home/frappe/frappe-bench/sites/recovery.local/{logs,public/files,private/files} &&
  cp /home/frappe/frappe-bench/sites/demo2.getgrowth.app/site_config.json /home/frappe/frappe-bench/sites/recovery.local/ &&
  python3.14 -c "
import json
with open('/home/frappe/frappe-bench/sites/recovery.local/site_config.json') as f:
    c = json.load(f)
c['db_name'] = '_recovered'
with open('/home/frappe/frappe-bench/sites/recovery.local/site_config.json', 'w') as f:
    json.dump(c, f, indent=2)
  " &&
  chown -R frappe:frappe /home/frappe/frappe-bench/sites/recovery.local
'

🔍 验证方法

# 源库 vs 恢复库 逐项对比
docker exec -i $(docker ps -qf "name=press-backend") ssh root@159.65.38.50 "
  mariadb _6ddc22772f5bd6f5 -e 'SELECT COUNT(*) FROM tabSingles' &&
  mariadb _recovered -e 'SELECT COUNT(*) FROM tabSingles'
"

预期:两个数字完全相同。2026-07-06 验证结果:源库 1357 = 恢复库 1357,源库 1044 表 = 恢复库 1044 表。


🕳️ 踩过的坑


⚠️ 已知限制 / 副作用

  • 仅覆盖数据库:bench 容器内的 app 代码、站点配置文件、上传文件不在备份范围内。完整站点恢复需要 bench 容器内有对应 app 代码
  • 备份在 Press VPS 本地:如果 104.236.24.124 磁盘故障,备份和 Press 控制面一起丢失。应增加 R2 异地备份
  • 恢复后需手动更新 Press 的 Site 记录:如果恢复的库名变了,需要在 Press DB 里更新 database_name 字段,否则 Press UI 不认
  • 全量备份 12MB 但随站点增多会增长:目前 5 个站点 12MB,100 个站点时需评估备份窗口

📌 待办

  • 增加 R2 异地备份同步(当前仅 Press VPS 本地)
  • n1/f1 VPS 关键文件备份(bench 配置、桥接脚本、systemd 配置)
  • 完整站点恢复测试(数据库 + bench 文件 + nginx 路由 → 浏览器可访问)
  • 备份保留策略评估(当前 7 天,是否需要更长?)

📋 速查命令合集

# 查站点数据库名
docker exec bench-0003-000007-f1 bench --site demo2.getgrowth.app execute "frappe.conf.db_name"
 
# 查最新备份文件
ls -lh /backups/m1-mariadb/ | tail -3
 
# 查备份里有哪些数据库
zcat /backups/m1-mariadb/m1-all-20260706.sql.gz | grep '^CREATE DATABASE' | head -20
 
# 恢复并验证
zcat /backups/m1-mariadb/m1-all-YYYYMMDD.sql.gz \
  | sed -n '/^USE `_源库名`/,/^USE `/p' \
  | sed 's/`_源库名`/`_新库名`/g' \
  | docker exec -i $(docker ps -qf "name=press-backend") ssh root@159.65.38.50 "mariadb"
 
# 验证恢复完整性
docker exec -i $(docker ps -qf "name=press-backend") ssh root@159.65.38.50 "
  mariadb _源库名 -e 'SELECT COUNT(*) FROM tabSingles' &&
  mariadb _新库名 -e 'SELECT COUNT(*) FROM tabSingles'
"
 
# 清理恢复测试
docker exec -i $(docker ps -qf "name=press-backend") ssh root@159.65.38.50 \
  "mariadb -e 'DROP DATABASE IF EXISTS _新库名'"

📝 更新日志

日期改动原因
2026-07-06首次创建,完成端到端恢复验证P1-1 备份恢复验证通过