数据备份管理系统教程:MySQL/目录/InfluxDB/ETCD 备份到本地与 S3
数据库、服务器目录、InfluxDB、ETCD,各有一套备份办法:mysqldump、tar、influx backup、etcdctl snapshot,定时任务散落在 crontab 里,出问题了才发现备份早停了。system-backu(注意仓库名就是 system-backu,少了个 p,不是拼写错误)把这些收拢到一个带 Web 界面的备份系统里:数据源、存储目标、定时调度、保留策略、还原、运行历史,全部可视化操作,备份目标支持本地目录和 S3 兼容对象存储。这篇教程讲部署、配置和使用全流程。
一、项目简介
system-backu 是 git.cnbugs.com 上 AI-Agent 组织的一个开源项目(仓库地址 https://git.cnbugs.com/AI-Agent/system-backu),定位是带 Web 操作界面的数据备份管理系统。它支持把 MySQL 数据库、服务器目录、InfluxDB、ETCD 备份到本地目录或 S3 兼容对象存储(AWS S3 / MinIO / Ceph),支持手动触发、Cron 定时调度、备份保留策略,以及从备份还原。
后端是 Python 3.11 + FastAPI + SQLAlchemy 2 + APScheduler,前端是 Vite + React 18 + TypeScript + Ant Design 5,数据库用 SQLite(可平滑迁移 PostgreSQL)。部署上后端是 systemd 服务(8765 端口),前端是 Docker 容器里的 Nginx(5173 端口)。
二、功能特性
- 多数据源:MySQL 数据库(mysqldump)、服务器目录(tar.gz)、InfluxDB、ETCD。
- 多存储目标:本地目录、S3 兼容对象存储(AWS S3 / MinIO / Ceph)。
- Cron 定时调度:标准 5 段 cron 表达式。
- 备份还原:从备份恢复 MySQL 库 / 解压目录。
- 保留策略:保留最近 N 个 + 保留 N 天,双重控制。
- JWT 登录认证:RBAC 权限管理(admin/operator)。
- 运行历史与日志:每次备份的执行详情、SHA256 校验、大小、耗时。
- 敏感字段加密:密码、Token 在数据库中 Fernet 加密存储,前端永远看不到明文。
- 文件浏览:存储目标下的备份文件列表查看。
三、技术架构
| 层 | 技术选型 | 说明 |
|---|---|---|
| 后端 | Python 3.11、FastAPI、SQLAlchemy 2、APScheduler | systemd 服务,8765 端口,入口 backend/app/main.py |
| 前端 | Vite + React 18 + TypeScript + Ant Design 5 | Docker 容器(Nginx 反代),5173 端口 |
| 数据库 | SQLite(可平滑迁移 PostgreSQL) | 文件 /root/system-backu/data/backend/backup.db |
| 存储 | 本地目录 / S3 兼容对象存储 | aiofiles / aioboto3 异步读写 |
| 备份工具 | mysqldump、tar、influx CLI、etcdctl | 调用宿主机上的现成工具 |
| 敏感字段加密 | Fernet(cryptography 库) | 数据源密码、Token 加密后存库 |
| 数据库迁移 | Alembic | backend/alembic/,版本 0001/0002 |
架构上浏览器先到前端容器(Nginx 反代),再到后端服务,后端连 SQLite 和备份文件目录。所有数据都在宿主机上,容器重启不会丢失,备份文件通过文件系统直接访问和下载。备份文件的组织方式是按“任务名 / 日期 /”分目录存放。
四、适用场景
- 业务数据库日常备份:MySQL 每天定时 mysqldump,保留最近 30 天,支持一键还原。
- 配置文件与数据目录备份:/etc、应用数据目录定时打 tar.gz,推到 MinIO 或 S3。
- ETCD 与 InfluxDB 备份:K8s 集群的 ETCD 快照、时序库的定期备份,统一在一个界面里管。
- 小团队备份中枢:没有专职备份系统,几个服务、几个库,用一个 Web 界面管定时任务和保留策略。
五、环境准备
依赖工具要先装好,备份执行时后端会直接调用它们:
| 依赖 | 用途 | 安装方法 |
|---|---|---|
| Python 3.11+ | 后端运行 | 系统自带或自行安装 |
| Docker | 前端容器 | 仅前端容器需要 |
| mysqldump | 备份 MySQL | mysql-client 包 |
| etcdctl | 备份 ETCD | 随 ETCD 分发 |
| influx CLI | 备份 InfluxDB | 随 InfluxDB 分发 |
# Debian/Ubuntu 装 mysql-client(提供 mysqldump)
apt install -y mysql-client
# RHEL/CentOS 装 mysql 客户端
dnf install -y mysql
# 确认工具都在 PATH 里
which mysqldump etcdctl influx
mysqldump --version
etcdctl version
六、部署:后端 systemd + 前端 Docker
第一步准备环境变量。项目根目录和 backend 目录下都有 .env.example 模板,复制成 .env 再编辑,至少设置三项:
cd /root/system-backu
cp .env.example .env
# 生成 SECRET_KEY(JWT 签名)
openssl rand -hex 32
# 生成 FERNET_KEY(敏感字段加密)
python3 -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())"
# 编辑 .env,至少设置这三项:
# SECRET_KEY — 上面生成的 64 位十六进制串
# FERNET_KEY — 上面生成的 Fernet key
# INITIAL_ADMIN_PASSWORD — 管理员初始密码(登录后可在页面修改)
第二步起后端(systemd 服务,开机自启):
cd /root/system-backu/backend
python3 -m venv venv
source venv/bin/activate
pip install -r requirements.txt
# 部署 systemd 服务
cp deploy/backup-backend.service /etc/systemd/system/
systemctl daemon-reload
systemctl enable backup-backend
systemctl start backup-backend
# 查看状态和日志
systemctl status backup-backend --no-pager
journalctl -u backup-backend -f
第三步起前端(Docker 容器):
cd /root/system-backu
docker compose up -d frontend
# 确认容器在跑
docker ps --filter name=frontend
docker logs -f system-backu-frontend-1
访问地址:前端管理界面 http://<服务器IP>:5173,后端 API http://<服务器IP>:8765,API 文档 http://<服务器IP>:8765/docs。首次登录用 admin / INITIAL_ADMIN_PASSWORD,登录后第一件事就是改密码。
七、使用流程:存储目标 → 任务 → 调度
第一次使用按这个顺序走,缺一步后面都建不了任务:
- 第一步:建存储目标(Storages → 新建)。本地存储填宿主机上实际存在的目录,且后端进程要有读写权限;S3 存储填 Endpoint、Bucket、AccessKey、SecretKey。建完点一下测试连通性,通了再往下走。
- 第二步:建备份任务(Jobs → 新建)。选数据源类型(MySQL / 目录 / InfluxDB / ETCD),填源信息(MySQL 填主机、端口、库名、账号密码;目录填要打包的路径),选存储目标,保存。
- 第三步:手动触发或定时调度。Job 列表点 Run Now 立即跑一次,验证备份能成功;确认没问题后填
cron_expression(标准 5 段 cron 表达式,比如每天凌晨 2 点就是0 2 * * *)启用定时调度。 - 第四步:看运行历史。每次备份的执行详情、SHA256 校验值、文件大小、耗时都在 Runs 里,失败了点进去看日志。
本地存储的 path 必须是宿主机上实际存在的目录。README 里给的例子是把宿主机目录映射进部署路径(比如 /root/system-backu/data/backend/backups/),如果你把备份目录放在别处,要确认后端进程对它有读写权限,否则任务会失败。
八、备份还原
备份的价值在还原那一刻才体现出来。还原入口在前端 Restore 页面,也可以调 POST /api/restore。支持两种还原:MySQL 库还原(用备份的 mysqldump 文件恢复到目标库)、目录还原(把 tar.gz 解压到指定位置)。还原前注意两点:一是确认目标库/目标目录,MySQL 还原会覆盖目标库的数据;二是还原操作本身也会记一条运行记录,方便事后追溯。
建议每季度做一次还原演练:拿一个测试库,从备份里恢复,验证数据完整、应用能连上。只备不练的备份,等于没备。
九、保留策略
保留策略是“保留最近 N 个 + 保留 N 天”双重控制。两个条件同时起作用:比如保留最近 30 个、保留 30 天,超过 30 天的删掉,超过 30 个的也删掉(只留最新的 30 个)。策略在任务里配置,由后端的保留清理逻辑执行。
配保留策略要算一笔账:每天备份一次、每次 2GB、保留 30 天,本地磁盘至少要留 60GB 以上的余量;推 S3 的话按存储量和请求数计费,保留期越长费用越高。关键库的保留期可以放长,普通目录备份保留 7 天就够了。
十、API 端点与调用示例
| 端点 | 说明 |
|---|---|
POST /api/auth/login | 登录获取 JWT |
GET /api/storages | 存储目标列表 |
POST /api/storages | 创建存储目标 |
GET /api/storages/{id}/files | 列出存储目标下的备份文件 |
POST /api/storages/{id}/test | 测试存储目标连通性 |
GET /api/jobs | 备份任务列表 |
POST /api/jobs | 创建备份任务 |
POST /api/jobs/{id}/run | 手动触发备份 |
GET /api/runs | 运行历史 |
POST /api/restore | 从备份恢复 |
# 登录拿 Token
curl -s -X POST http://10.168.1.209:8765/api/auth/login \
-H "Content-Type: application/json" \
-d '{"username": "admin", "password": "你的管理员密码"}'
# 返回里有 access_token,存下来
export TOKEN="eyJhbGciOi..."
# 列出备份任务
curl -s http://10.168.1.209:8765/api/jobs \
-H "Authorization: Bearer $TOKEN" | python3 -m json.tool
# 手动触发一次备份(任务 id 为 1)
curl -s -X POST http://10.168.1.209:8765/api/jobs/1/run \
-H "Authorization: Bearer $TOKEN"
# 查运行历史
curl -s "http://10.168.1.209:8765/api/runs" \
-H "Authorization: Bearer $TOKEN" | python3 -m json.tool
# 列出某个存储目标下的备份文件
curl -s http://10.168.1.209:8765/api/storages/1/files \
-H "Authorization: Bearer $TOKEN" | python3 -m json.tool
完整 API 文档见 http://<服务器>:8765/docs(Swagger,可在线测试)。注意示例里的 10.168.1.209 是 README 里的示例地址,换成你自己的服务器 IP。
十一、安全注意事项
- FERNET_KEY 丢了就全完:数据源密码和 Token 是用 Fernet 加密后存数据库的,密钥丢了就解不出来,所有数据源配置都要重填。把 FERNET_KEY 和 SECRET_KEY 一起存到安全的地方(密码管理器或 secret manager),别只放在 .env 里。
- 前端永远看不到明文:敏感字段编辑时回显
***,这是设计如此,不是 bug。改密码要重新输入完整的新密码。 - 单实例部署:内置 APScheduler,不要启动多个 backend 进程,否则出现重复调度(同一个任务被跑两遍)。gunicorn 多 worker、systemd 重复启动都要避免。
- RBAC 只有两级:admin 和 operator,operator 能操作备份任务但不能改系统配置。给值班同事开 operator 就够了,admin 只给负责人。
- 备份目录权限:本地存储的目录要限制访问权限,备份文件里有数据库明文 dump,权限放开等于把数据库送人。
十二、常见问题
| 现象 | 原因与处理 |
|---|---|
| MySQL 备份失败,报找不到 mysqldump | 宿主机没装 mysql-client;装上并确认 which mysqldump 能找到 |
| 任务显示成功但存储目标里没文件 | 本地存储 path 不存在或后端没写权限;检查目录和权限,或用 POST /api/storages/{id}/test 测连通性 |
| 定时任务不执行 | 后端是不是起了多个进程(重复调度会互相干扰);确认只有一个 backup-backend 在跑,cron 表达式是标准 5 段 |
| 前端打不开,后端正常 | 前端容器没起来或端口不对;docker ps 查容器,docker logs 看 Nginx 报错 |
| 登录后提示密码错误 | INITIAL_ADMIN_PASSWORD 只在首次初始化时生效,改过密码后以新密码为准 |
| 磁盘满了 | 保留策略没配或配得太松;收紧保留个数和天数,手动清理过期备份 |
十三、运维建议
- 备份的备份:SQLite 数据库文件(
/root/system-backu/data/backend/backup.db)和备份文件目录本身也要备份——备份系统自己的数据丢了,任务配置和运行历史全没了。 - 盯着运行历史:每天看一眼 Runs,有失败的立即处理;失败的任务不会自动补跑(除非下次定时触发)。
- 定期做还原演练:每季度至少一次,验证备份文件真的能恢复。
- 监控磁盘:备份目录单独分区或单独磁盘,设磁盘使用率告警,避免备份把根分区写满。
- 升级流程:先停 backend 服务,
git pull拉新代码,跑 Alembic 迁移(alembic upgrade head),重装依赖,前端docker compose up -d --build frontend,再启动验证。
总结一下:system-backu 把 MySQL、目录、InfluxDB、ETCD 四种备份收进一个 Web 界面,本地目录和 S3 双存储目标、Cron 定时调度、保留策略、还原和运行历史一应俱全。部署记住:后端 systemd(8765)、前端 Docker(5173),SECRET_KEY、FERNET_KEY、INITIAL_ADMIN_PASSWORD 三个环境变量先配好;用起来记住:先建存储目标、再建任务、Run Now 验证、最后上定时调度;运维记住:只起一个 backend 进程、定期做还原演练。







