Linux 服务器备份实战指南:从自动化脚本到异地容灾,IT大叔的生产环境数据保护方案
Linux 服务器备份实战指南:从自动化脚本…
Linux 服务器备份实战指南:从自动化脚本到异地容灾,IT大叔的生产环境数据保护方案
上个月一位朋友跑了三年的网站突然挂了,原因是数据库所在的 SSD 突然变只读。数据倒是找回来了,但恢复花了一整天——因为他从来没完整测试过恢复流程。备份不做恢复测试,等于没备份。
我自己管理的 VPS、NAS、Docker 容器加起来十几个节点,数据丢了任何一份都够心疼半天的。今天直接把我两年多验证过的备份体系拆开来讲,从单机脚本到异地容灾,按需取用。
一、最基础:单人单机的自动备份脚本
先别急着上复杂方案。大多数人需要的其实就是一个定时打包 + 清理旧文件的脚本。
以下是我所有 VPS 上都在跑的基础版本:
#!/bin/bash
# /usr/local/bin/backup-site.sh
BACKUP_DIR="/data/backups"
KEEP_DAYS=7
DATE=$(date +%Y%m%d_%H%M)
SITE_DIR="/var/www/html"
DB_NAME="wordpress"
DB_USER="root"
DB_PASS="yourpassword"
mkdir -p "$BACKUP_DIR/$DATE"
# 备份数据库
mysqldump -u"$DB_USER" -p"$DB_PASS" "$DB_NAME" | gzip > "$BACKUP_DIR/$DATE/db.sql.gz"
# 备份站点文件
tar czf "$BACKUP_DIR/$DATE/site.tar.gz" "$SITE_DIR"
# 清理超过7天的旧备份
find "$BACKUP_DIR" -maxdepth 1 -mtime +$KEEP_DAYS -exec rm -rf {} \;
配合 crontab 每天凌晨执行:
0 3 * * * /usr/local/bin/backup-site.sh
避坑要点: 数据库密码直接写在脚本里不安全。建议用 --defaults-extra-file 指定独立权限文件,权限设为 600。
二、Docker 环境的备份策略
如果你的服务全是 Docker 容器跑的(像我博客一样),就不能直接 mysqldump 宿主机了。正确的做法是从容器内导出:
# 备份 Docker 内的 MariaDB
docker exec vpnt_db mariadb-dump -uroot -p"$DB_PASS" wordpress | gzip > db.sql.gz
# 备份挂载卷(注意不是容器内路径,是宿主机路径)
tar czf volumes_backup.tar.gz /var/lib/docker/volumes/vpnt_wp_data/
关键建议: 用 docker-compose 的话,在 Compose 文件里给所有数据卷打上 标签,这样备份脚本可以自动发现哪些卷需要备份:
VOLUMES=$(docker volume ls --filter label=backup=true -q)
for v in $VOLUMES; do
docker run --rm -v $v:/data -v $BACKUP_DIR:/backup alpine tar czf /backup/${v}_$DATE.tar.gz -C /data .
done
三、异地容灾:不把鸡蛋放一个篮子里
本地备份做得再好,服务器被删了也没用。必须有一份数据在另一台机器上。
方案 A:rsync + 定时同步(低成本首选)
# 目标机(备份服务器)上拉取数据
rsync -avz --delete -e "ssh -p 22" user@source-server:/data/backups/ /data/remote-backups/
配合 crontab 每 6 小时同步一次,延迟最多 6 小时。够用,且免费。
方案 B:rclone + 对象存储(异地容灾最佳)
如果你有 S3 兼容的对象存储(Backblaze B2 或 阿里云OSS),rclone 是最优雅的方案:
rclone sync /data/backups remote:bucket-name/backups/ \
--progress --transfers 4 --checkers 8
将 rclone 命令写入 crontab,一天一次。Backblaze B2 的存储成本约为 $0.006/GB/月,100GB 一个月才六毛钱美金——比丢数据划算太多了。
四、恢复测试:备份的最后一道防线
这才是整个备份体系里最重要、也最容易被忽略的一步。 我每季度做一次完整的恢复演练,流程如下:
- 在一台临时机器(或 Docker 容器)上导入数据库:
gunzip < backup.sql.gz | mysql -u root -p wordpress_restore
- 解压站点文件:
tar xzf site.tar.gz -C /tmp/restore/
- 修改
/etc/hosts指向临时机器,访问验证所有页面正常 - 确认附件、图片、插件配置全部恢复
如果恢复步骤不能写成文档跑一遍,说明你还不够了解自己的备份。
五、我的生产环境备份拓扑(仅供参考)
┌──────────────┐ rsync every 6h ┌──────────────────┐
│ 主 VPS │ ──────────────────────→│ 同机房备机 (廉价VPS)│
│ Nginx + 数据 │ │ 保留最近7天版本 │
└──────────────┘ └──────────────────┘
│ │
│ rclone daily │ rclone daily
▼ ▼
┌──────────────────────┐ ┌──────────────────────┐
│ Backblaze B2 (异地) │ │ Backblaze B2 (异地) │
│ 保留30天,加密上传 │ │ 保留30天,加密上传 │
└──────────────────────┘ └──────────────────────┘
三个关键数字:
- 本地保留 7天(快速恢复)
- 同城备机保留 7天(机器挂了不慌)
- 异地对象存储保留 30天(最坏情况兜底)
写在最后
备份这件事,做起来花一两个小时,不做可能丢一年心血。 别等到数据丢了才想起来这篇教程。
从最简单的脚本开始,今天配好 crontab,明天加上 rclone,最迟下周搞定恢复测试。一步一步来,只要能保证"今天出问题,今天就能恢复"就算及格。
