📌 前言
服务器数据无价,无论是网站、数据库还是 Docker 容器数据,一旦丢失后果不堪设想。本文介绍最经典的 Linux 服务器间数据备份方案——rsync + cron,实现增量自动备份到远程服务器。
🔍 rsync 是什么?
rsync(remote synchronize)是 Linux 下最强大的数据同步工具,核心优势:
- 增量传输:只传输变化的部分,大幅节省带宽
- 支持压缩:传输过程压缩数据,速度更快
- 保持属性:保留文件权限、时间戳、软链接等
- 支持排除:可排除指定文件/目录
- SSH 加密:通过 SSH 传输,安全可靠
📐 环境准备
| 角色 | 说明 | 示例 |
|---|---|---|
| 源服务器 A | 需要备份数据的机器 | 192.168.1.100 |
| 目标服务器 B | 存放备份的机器 | 192.168.1.200 |
两台机器都需要安装 rsync,大多数 Linux 发行版已预装。
准备2台服务器
我们使用莱卡云VPS来演示
需要vps的可以看以下信息,
莱卡云官网
安装 rsync
# Debian/Ubuntu
apt update && apt install -y rsync
# CentOS/RHEL/AlmaLinux
yum install -y rsync
# Arch Linux
pacman -S rsync
验证安装
rsync --version
# rsync version 3.2.7 protocol version 31
🔑 配置 SSH 免密登录
备份是自动化任务,不能每次都手动输密码,所以先配好 SSH 密钥认证。
1. 在源服务器 A 上生成密钥
# 如果已有密钥可跳过
ssh-keygen -t ed25519 -N "" -f ~/.ssh/id_ed25519
2. 推送公钥到目标服务器 B
# 默认22端口
ssh-copy-id root@192.168.1.200
# 自定义端口(如7722)
ssh-copy-id -p 7722 root@192.168.1.200
3. 验证免密登录
# 应该直接登录成功,不需要输入密码
ssh root@192.168.1.200 "echo connected"
# 自定义端口
ssh -p 7722 root@192.168.1.200 "echo connected"
⚠️ 如果使用了自定义端口,后续所有 rsync 命令都需要加
-e "ssh -p 端口"参数。
📂 在目标服务器创建备份目录
# 在服务器 B 上执行
mkdir -p /backup/app
mkdir -p /backup/mysql
mkdir -p /backup/docker
🚀 rsync 基础用法
命令格式
rsync [选项] 源路径 目标路径
常用选项
| 选项 | 说明 |
|---|---|
-a |
归档模式(保留权限、时间戳、软链接等) |
-v |
显示详细输出 |
-z |
传输时压缩数据 |
-P |
显示进度+支持断点续传 |
--delete |
删除目标中源端已不存在的文件(完全镜像) |
--exclude |
排除指定文件/目录 |
-e |
指定远程shell(如自定义SSH端口) |
最常用的备份命令
# 把服务器A的 /data/app 目录同步到服务器B的 /backup/app
rsync -avz --delete /data/app/ root@192.168.1.200:/backup/app/
🔔 注意尾随斜杠:
/data/app/(有斜杠)= 同步 app 目录里的内容/data/app(无斜杠)= 同步 app 目录本身备份场景建议加尾随斜杠。
自定义 SSH 端口
rsync -avz --delete -e "ssh -p 7722" /data/app/ root@192.168.1.200:/backup/app/
排除特定文件
# 排除日志和临时文件
rsync -avz --delete \
--exclude='*.log' \
--exclude='tmp/' \
--exclude='.cache/' \
/data/app/ root@192.168.1.200:/backup/app/
排除文件列表(大量排除规则时)
# 创建排除规则文件
cat > /etc/rsync-exclude.txt << 'EOF'
*.log
*.tmp
tmp/
.cache/
node_modules/
*.pid
EOF
# 使用 --exclude-from 引用
rsync -avz --delete --exclude-from='/etc/rsync-exclude.txt' \
/data/app/ root@192.168.1.200:/backup/app/
🔄 实战场景
场景一:备份网站数据
rsync -avz --delete \
--exclude='*.log' \
--exclude='.git/' \
/www/wwwroot/mysite/ \
root@192.168.1.200:/backup/mysite/
场景二:备份 Docker 数据
rsync -avz --delete \
--exclude='*.log' \
/var/lib/docker/volumes/ \
root@192.168.1.200:/backup/docker-volumes/
场景三:备份前先导出数据库
# 先导出MySQL数据库
mysqldump -u root -p'你的密码' --all-databases > /tmp/all_db.sql
# 再同步到远程
rsync -avz /tmp/all_db.sql root@192.168.1.200:/backup/mysql/
场景四:SQLite 数据库备份
# 先用SQLite的.backup命令安全备份(比直接复制更安全)
sqlite3 /data/app/onenav.db3 ".backup '/tmp/onenav_backup.db3'"
# 同步到远程
rsync -avz /tmp/onenav_backup.db3 root@192.168.1.200:/backup/db/
⏰ 配置 cron 自动定时备份
编写备份脚本
cat > /root/backup.sh << 'BACKUP_EOF'
#!/bin/bash
# ===== 配置区域 =====
REMOTE_IP="192.168.1.200"
REMOTE_PORT="22"
REMOTE_USER="root"
BACKUP_DIR="/backup"
# 备份任务列表(源路径:目标子目录名)
TASKS=(
"/www/wwwroot/mysite/:mysite"
"/data/docker/:docker"
"/root/scripts/:scripts"
)
# 日志文件
LOG_FILE="/var/log/backup.log"
# ===== 执行区域 =====
echo "========================================" >> $LOG_FILE
echo "备份开始: $(date '+%Y-%m-%d %H:%M:%S')" >> $LOG_FILE
for TASK in "${TASKS[@]}"; do
SRC="${TASK%%:*}"
DST="${TASK##*:}"
echo ">>> 同步 $SRC -> $REMOTE_IP:$BACKUP_DIR/$DST" >> $LOG_FILE
rsync -avz --delete \
--exclude='*.log' \
--exclude='.git/' \
--exclude='*.tmp' \
--exclude='.cache/' \
-e "ssh -p $REMOTE_PORT" \
"$SRC" "$REMOTE_USER@$REMOTE_IP:$BACKUP_DIR/$DST/" >> $LOG_FILE 2>&1
if [ $? -eq 0 ]; then
echo "✅ $DST 备份成功" >> $LOG_FILE
else
echo "❌ $DST 备份失败" >> $LOG_FILE
fi
done
echo "备份结束: $(date '+%Y-%m-%d %H:%M:%S')" >> $LOG_FILE
echo "" >> $LOG_FILE
BACKUP_EOF
chmod +x /root/backup.sh
设置定时任务
crontab -e
添加以下内容:
# 每天凌晨3点执行备份
0 3 * * * /root/backup.sh
# 如果想每12小时备份一次
0 */12 * * * /root/backup.sh
# 如果想每小时备份一次(高频场景)
0 * * * * /root/backup.sh
查看定时任务
crontab -l
📋 cron 时间表达式速查
┌──────── 分钟 (0-59)
│ ┌────── 小时 (0-23)
│ │ ┌──── 日 (1-31)
│ │ │ ┌── 月 (1-12)
│ │ │ │ ┌ 星期 (0-7, 0和7都是周日)
│ │ │ │ │
* * * * * 命令
| 表达式 | 说明 |
|---|---|
0 3 * * * |
每天凌晨3点 |
0 */6 * * * |
每6小时 |
0 3 * * 1 |
每周一凌晨3点 |
0 0 1 * * |
每月1号零点 |
*/30 * * * * |
每30分钟 |
📊 日志与监控
查看备份日志
tail -50 /var/log/backup.log
日志自动轮转
cat > /etc/logrotate.d/backup << 'EOF'
/var/log/backup.log {
weekly
rotate 4
compress
missingok
notifempty
create
}
EOF
备份结果通知(可选)
在 backup.sh 末尾加上通知,通过 Telegram Bot 推送:
# 在 backup.sh 的末尾添加
SUCCESS_COUNT=$(grep "✅" $LOG_FILE | tail -${#TASKS[@]} | wc -l)
FAIL_COUNT=$(grep "❌" $LOG_FILE | tail -${#TASKS[@]} | wc -l)
MESSAGE="📋 备份报告
时间: $(date '+%Y-%m-%d %H:%M:%S')
成功: ${SUCCESS_COUNT} / ${#TASKS[@]}
失败: ${FAIL_COUNT} / ${#TASKS[@]}"
# Telegram推送(替换为你的Bot Token和Chat ID)
curl -s -X POST "https://api.telegram.org/bot你的TOKEN/sendMessage" \
-d "chat_id=你的CHAT_ID" \
-d "text=$MESSAGE"
🔐 服务器安全建议
限制 SSH 权限
如果只为 rsync 使用,可以在服务器 B 的 ~/.ssh/authorized_keys 中限制来源 IP 和命令:
# 编辑服务器B的 authorized_keys
from="192.168.1.100",command="rsync --server" ssh-ed25519 AAAA...公钥内容...
这样即使密钥泄露,也只能从指定 IP 执行 rsync 命令。
防火墙限制
# 服务器B只允许服务器A的SSH连接
iptables -A INPUT -p tcp --dport 22 -s 192.168.1.100 -j ACCEPT
iptables -A INPUT -p tcp --dport 22 -j DROP
🔁 恢复数据
备份的意义在于能恢复。从目标服务器 B 恢复数据到源服务器 A:
# 反向同步(从B拉取到A)
rsync -avz root@192.168.1.200:/backup/app/ /data/app/
# 或者在服务器B上推送(推送到A)
rsync -avz /backup/app/ root@192.168.1.100:/data/app/
⚠️ 恢复时不要加
--delete,除非你确定要完全覆盖。
✅ 验证备份有效性
定期验证备份是否能用:
# 在服务器B上检查备份文件
ls -la /backup/app/
du -sh /backup/app/
find /backup/app/ -type f | wc -l
# 对比文件数量
find /data/app/ -type f | wc -l
🎯 总结
源服务器A ──── rsync over SSH ────→ 目标服务器B
/data/app/ /backup/app/
│ │
└──── cron 定时触发 ────────────────┘
| 步骤 | 操作 |
|---|---|
| 1 | 安装 rsync |
| 2 | 配置 SSH 免密登录 |
| 3 | 编写备份脚本 |
| 4 | 设置 cron 定时任务 |
| 5 | 定期检查日志和验证备份 |
💡 核心优势:rsync 增量传输只同步变化部分,对带宽和网络压力极小,适合长期稳定运行。

评论区