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站点→数据库映射
站点 数据库 gottenergy.getgrowth.app _067bd10f1b718d20demo2.getgrowth.app _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'
"库名可以自定义
不需要和原名一样。恢复后用
site_config.json里的db_name指向它即可。
第 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"sed 做了什么
sed -n '/USE _6ddc.../,/USE /p':从目标库的 USE 语句开始,到下一个库的 USE 语句为止,只提取这一段sed 's/_6ddc.../_recovered/g':把 SQL 里所有出现原库名的地方替换成新库名(包括 CREATE TABLE、INSERT、触发器等里面的引用)
第 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'
"为什么需要授权
MariaDB 的用户权限是绑定到具体数据库的。
_6ddc22772f5bd6f5用户原本只能访问同名的_6ddc22772f5bd6f5库。恢复到新库_recovered后,用户没有新库的权限,连接会被拒绝。
第 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
'这是在做什么
复制原站点的
site_config.json(含正确的 MariaDB 用户名密码),只改db_name指向恢复库。必需的三个子目录(logs/public/private)也要创建,否则 bench 启不来。
🔍 验证方法
# 源库 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 表。
🕳️ 踩过的坑
坑 1:
site_config.json改了db_name但 bench 连不上现象:
bench console报Access denied for user根因:MariaDB 用户权限绑定到原库名,新库名没授权 解决:GRANT ALL PRIVILEGES ON _recovered.* TO "原用户名"@"%"+FLUSH PRIVILEGES
坑 2:
FileNotFoundError: sites/recovery.local/logs/database.log现象:bench 连接恢复库时报找不到日志文件 根因:只复制了
site_config.json,没有创建logs/、public/files/、private/files/目录 解决:mkdir -p sites/recovery.local/{logs,public/files,private/files}
坑 3:全量备份恢复时覆盖了其他库
现象:不用
sed提取直接把全量备份 pipe 给 mariadb,会把所有库都恢复到 m1 上 根因:mysqldump --all-databases包含所有库的 CREATE/INSERT 解决:必须用sed -n '/^USE _目标库/,/^USE /p'只提取目标库的 SQL 段
⚠️ 已知限制 / 副作用
- 仅覆盖数据库: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 备份恢复验证通过 |