服务器自动备份方案:用 rsync + cron 实现异地增量备份
服务器数据丢失是小概率但高代价的事故。硬盘故障、误操作 rm -rf、系统被黑客清空......这些情况听起来离你很远,但真发生了就是几个月甚至几年的工作付之一炬。备份不是"有了更好",是"没有迟早后悔"。
这篇文章介绍一套实用的自动备份方案:用 rsync 做增量同步,用 cron 定时触发,把数据异地备份到另一台服务器。整个方案不依赖第三方服务,成本低,可控性强。
备份策略设计
先想清楚要备份什么、备份多久、保留多少份:
| 备份类型 | 频率 | 保留时长 |
|---|---|---|
| 数据库 | 每天一次 | 保留 30 天 |
| 应用配置 | 每天一次 | 保留 30 天 |
| 用户上传文件 | 每天一次 | 保留 7 天 |
| 完整系统快照 | 每周一次 | 保留 4 周 |
最低成本的异地备份方案是再准备一台小服务器专门用来存备份。我在 雨云服务器 rainyun.com 开了一台 1 核 1G、50GB 硬盘的机器专门放备份数据,按月付,价格很低。雨云的性价比在这种场景下特别合适------备份机不需要高性能,只需要存储空间大、网络稳定。新用户注册填优惠码 2026off 可以领 5 折优惠券,一台备份机的月费低到可以忽略不计,但能给你的主服务器数据提供有效保障。
准备工作
主服务器(被备份的那台)
创建专用备份用户:
bash
adduser backup --disabled-password --gecos ""
备份服务器
同样创建对应用户:
bash
adduser backup --disabled-password --gecos ""
mkdir -p /backup/server-main
chown backup:backup /backup/server-main
配置 SSH 免密登录
在主服务器上,以 backup 用户身份生成 SSH 密钥:
bash
su - backup
ssh-keygen -t ed25519 -C "backup@main-server" -N "" -f ~/.ssh/id_backup
把公钥复制到备份服务器:
bash
ssh-copy-id -i ~/.ssh/id_backup.pub backup@备份服务器IP
测试连接:
bash
ssh -i ~/.ssh/id_backup backup@备份服务器IP echo "连接成功"
编写备份脚本
创建数据库备份脚本:
bash
sudo mkdir -p /opt/backup/scripts
sudo tee /opt/backup/scripts/backup-mysql.sh << 'EOF'
#!/bin/bash
DATE=$(date +%Y%m%d_%H%M%S)
BACKUP_DIR="/tmp/db_backup"
REMOTE_HOST="备份服务器IP"
REMOTE_DIR="/backup/server-main/databases"
KEEP_DAYS=30
mkdir -p $BACKUP_DIR
# 备份所有数据库(排除系统库)
DATABASES=$(mysql -u root -p"$MYSQL_PASS" -e "SHOW DATABASES;" 2>/dev/null | grep -Ev "(Database|information_schema|performance_schema|sys|mysql)")
for DB in $DATABASES; do
echo "备份数据库: $DB"
mysqldump -u root -p"$MYSQL_PASS" \
--single-transaction \
--routines \
--triggers \
"$DB" 2>/dev/null | gzip > "$BACKUP_DIR/${DB}_${DATE}.sql.gz"
done
# rsync 到备份服务器
rsync -avz \
-e "ssh -i /home/backup/.ssh/id_backup -o StrictHostKeyChecking=no" \
"$BACKUP_DIR/" \
"backup@${REMOTE_HOST}:${REMOTE_DIR}/"
# 清理本地临时文件
rm -rf $BACKUP_DIR
# 删除备份服务器上 30 天前的文件
ssh -i /home/backup/.ssh/id_backup backup@${REMOTE_HOST} \
"find ${REMOTE_DIR}/ -name '*.sql.gz' -mtime +${KEEP_DAYS} -delete"
echo "数据库备份完成: $DATE"
EOF
sudo chmod +x /opt/backup/scripts/backup-mysql.sh
创建文件备份脚本:
bash
sudo tee /opt/backup/scripts/backup-files.sh << 'EOF'
#!/bin/bash
REMOTE_HOST="备份服务器IP"
REMOTE_DIR="/backup/server-main/files"
LOG_FILE="/var/log/backup-files.log"
echo "=== 备份开始: $(date) ===" >> $LOG_FILE
# 需要备份的目录列表
BACKUP_DIRS=(
"/etc"
"/opt/myapp"
"/var/www"
"/home"
)
for DIR in "${BACKUP_DIRS[@]}"; do
if [ -d "$DIR" ]; then
echo "同步目录: $DIR" >> $LOG_FILE
rsync -avz \
--delete \
--exclude="*.log" \
--exclude="*.tmp" \
--exclude="node_modules" \
--exclude=".git" \
-e "ssh -i /home/backup/.ssh/id_backup -o StrictHostKeyChecking=no" \
"$DIR" \
"backup@${REMOTE_HOST}:${REMOTE_DIR}/" \
>> $LOG_FILE 2>&1
fi
done
echo "=== 备份结束: $(date) ===" >> $LOG_FILE
EOF
sudo chmod +x /opt/backup/scripts/backup-files.sh
配置 cron 定时任务
以 root 身份编辑 crontab:
bash
sudo crontab -e
添加以下任务:
bash
# 每天凌晨 2 点备份数据库
0 2 * * * MYSQL_PASS="你的MySQL密码" /opt/backup/scripts/backup-mysql.sh >> /var/log/backup-mysql.log 2>&1
# 每天凌晨 3 点备份文件
0 3 * * * /opt/backup/scripts/backup-files.sh
# 每周日凌晨 4 点做一次完整打包(可选)
0 4 * * 0 /opt/backup/scripts/backup-weekly.sh
验证备份效果
手动运行一次脚本测试:
bash
sudo MYSQL_PASS="你的密码" /opt/backup/scripts/backup-mysql.sh
sudo /opt/backup/scripts/backup-files.sh
登录备份服务器确认文件是否到位:
bash
ssh backup@备份服务器IP ls -lh /backup/server-main/databases/
检查日志:
bash
tail -50 /var/log/backup-mysql.log
tail -50 /var/log/backup-files.log
进阶:带保留策略的增量备份
rsync 加上 --link-dest 参数可以实现"快照式"备份------每次都是完整备份的样子,但实际上用硬链接节省空间,没变化的文件不占额外存储。
bash
#!/bin/bash
DATE=$(date +%Y%m%d)
REMOTE_HOST="备份服务器IP"
SNAPSHOT_DIR="/backup/server-main/snapshots"
LATEST_LINK="${SNAPSHOT_DIR}/latest"
ssh -i /home/backup/.ssh/id_backup backup@${REMOTE_HOST} \
"mkdir -p ${SNAPSHOT_DIR}/${DATE}"
rsync -avz \
--delete \
--link-dest="${LATEST_LINK}" \
-e "ssh -i /home/backup/.ssh/id_backup" \
/opt/myapp/ \
"backup@${REMOTE_HOST}:${SNAPSHOT_DIR}/${DATE}/"
# 更新 latest 软链接
ssh -i /home/backup/.ssh/id_backup backup@${REMOTE_HOST} \
"ln -sfn ${SNAPSHOT_DIR}/${DATE} ${LATEST_LINK}"
# 删除 7 天前的快照
ssh -i /home/backup/.ssh/id_backup backup@${REMOTE_HOST} \
"find ${SNAPSHOT_DIR} -maxdepth 1 -name '20*' -mtime +7 -exec rm -rf {} \;"
这样每天的快照独立存在,可以回滚到任意一天的状态,实际占用空间只比单次备份多一点点。
备份监控(可选)
建议加一个简单的备份成功通知。最简单的方式是在脚本末尾发送邮件:
bash
# 安装发件工具
sudo apt install -y msmtp msmtp-mta
# 在脚本末尾加
echo "备份完成 $(date)" | mail -s "服务器备份报告" 你的邮箱@example.com
或者用 curl 发 webhook 到你的通知渠道(Bark、钉钉、企业微信等):
bash
curl -s "https://api.day.app/你的Key/备份成功/$(date +%Y%m%d)"
异地备份搭起来之后基本是"无感"运行的。定期检查一下日志和备份服务器的文件大小,确认备份在正常进行。真正到需要用的那天,有备份和没备份的差别就是能不能快速恢复。