数据同步和备份是我们日常工作中绕不开的话题。无论是服务器之间的文件同步、定期备份,还是代码部署,都离不开一个神器级的工具——rsync。
说起rsync,可能很多新手朋友会觉得它很复杂,参数一大堆,看着就头疼。但实际上,掌握了rsync的核心原理和常用操作后,你会发现它简直是运维工作的得力助手。
Rsync(Remote Sync)是一个开源的快速备份工具,可以在不同主机之间镜像整个目录树。它的最大特点就是使用"rsync算法"进行增量传输,只传输文件的差异部分,大大提高了传输效率。
简单来说,rsync就像是一个聪明的搬运工,它不会傻乎乎地把所有文件都重新搬一遍,而是会比较源文件和目标文件的差异,只搬运有变化的部分。这样既节省了时间,又减少了网络带宽的消耗。
Rsync有三种主要的工作模式,理解这些模式对于正确使用rsync至关重要。
这种模式就是在同一台机器上进行文件同步,类似于cp命令,但功能更强大。
# 基本语法
rsync [选项] 源路径 目标路径
# 示例:同步本地目录
rsync -av /home/user/documents/ /backup/documents/
通过SSH等远程shell协议进行同步,这是最常用的模式。
# 推送到远程主机
rsync -av /local/path/ user@remote-host:/remote/path/
# 从远程主机拉取
rsync -av user@remote-host:/remote/path/ /local/path/
rsync以守护进程方式运行,提供rsync服务。
# 连接rsync守护进程
rsync -av /local/path/ rsync://user@remote-host/module-name/
rsync的参数很多,但常用的就那么几个,掌握这些就够日常使用了。
理论说得再多,不如实际操作来得直接。我们通过几个真实的运维场景来看下rsync的具体应用。
我们需要将本地开发的网站文件同步到生产服务器。(这招也可以用在云计算磁盘需要缩容的过程中,毕竟云硬盘,多开1G都是要花钱的,利用率最大化才是省钱王道)
# 基本同步命令
rsync -avz --delete /var/www/html/ root@192.168.1.100:/var/www/html/
# 排除不需要同步的文件
rsync -avz --delete \
--exclude='*.log' \
--exclude='cache/' \
--exclude='.git/' \
/var/www/html/ root@192.168.1.100:/var/www/html/
这个命令会:
对于数据库备份文件的异地同步,我们通常需要更谨慎的处理。
#!/bin/bash
# 数据库备份同步脚本
BACKUP_DIR="/backup/mysql"
REMOTE_HOST="backup-server.company.com"
REMOTE_DIR="/data/mysql-backup"
LOG_FILE="/var/log/backup-sync.log"
# 执行同步
rsync -avz --progress \
--log-file=$LOG_FILE \
--timeout=300 \
--bwlimit=10000 \
$BACKUP_DIR/ root@$REMOTE_HOST:$REMOTE_DIR/
# 检查同步结果
if [ $? -eq 0 ]; then
echo "$(date): 备份同步成功" >> $LOG_FILE
else
echo "$(date): 备份同步失败" >> $LOG_FILE
# 发送告警邮件
echo "备份同步失败,请检查" | mail -s "备份告警" admin@company.com
fi
在集群环境中,经常需要将配置文件分发到多台服务器。
#!/bin/bash
# 配置文件分发脚本
CONFIG_DIR="/etc/app-config"
SERVERS=("web01" "web02" "web03" "web04")
for server in "${SERVERS[@]}"; do
echo "正在同步配置到 $server..."
rsync -avz --dry-run \
--exclude='*.bak' \
$CONFIG_DIR/ root@$server:$CONFIG_DIR/
# 确认无误后执行实际同步
read -p "确认同步到 $server 吗?(y/n): " confirm
if [ "$confirm" = "y" ]; then
rsync -avz \
--exclude='*.bak' \
$CONFIG_DIR/ root@$server:$CONFIG_DIR/
echo "$server 同步完成"
fi
done
当排除规则很复杂时,可以使用过滤规则文件。
# 创建过滤规则文件 exclude-list.txt
cat > exclude-list.txt << EOF
*.log
*.tmp
cache/
.git/
node_modules/
*.pyc
__pycache__/
EOF
# 使用过滤规则文件
rsync -avz --exclude-from=exclude-list.txt /source/ /destination/
在生产环境中,为了不影响正常业务,通常需要限制rsync的传输速度。
# 限制传输速度为1MB/s
rsync -avz --bwlimit=1000 /source/ user@remote:/destination/
# 在脚本中动态调整限速
if [ $(date +%H) -ge 9 ] && [ $(date +%H) -le 18 ]; then
# 工作时间限速更严格
BWLIMIT="500"
else
# 非工作时间可以跑满带宽
BWLIMIT="0"
fi
rsync -avz --bwlimit=$BWLIMIT /source/ user@remote:/destination/
为了安全和自动化,建议使用SSH密钥认证而不是密码认证。
# 生成SSH密钥对
ssh-keygen -t rsa -b 2048 -f ~/.ssh/rsync_key
# 将公钥复制到目标服务器
ssh-copy-id -i ~/.ssh/rsync_key.pub user@remote-host
# 在rsync中指定密钥文件
rsync -avz -e "ssh -i ~/.ssh/rsync_key" /source/ user@remote:/destination/
编写监控脚本来跟踪rsync的执行状态。
#!/bin/bash
# rsync监控脚本
SYNC_LOG="/var/log/rsync-monitor.log"
LOCK_FILE="/var/run/rsync-sync.lock"
# 检查是否已有rsync进程在运行
if [ -f "$LOCK_FILE" ]; then
echo "$(date): rsync进程已在运行" >> $SYNC_LOG
exit 1
fi
# 创建锁文件
echo $$ > $LOCK_FILE
# 执行同步并记录时间
START_TIME=$(date +%s)
echo "$(date): 开始同步" >> $SYNC_LOG
rsync -avz --stats /source/ user@remote:/destination/ 2>&1 | tee -a $SYNC_LOG
END_TIME=$(date +%s)
DURATION=$((END_TIME - START_TIME))
echo "$(date): 同步完成,耗时 ${DURATION} 秒" >> $SYNC_LOG
# 清理锁文件
rm -f $LOCK_FILE
说到实时同步,我想起之前在一个项目中遇到的需求,客户要求文件一有变化就立即同步到备份服务器,不能等定时任务。这时候单纯用rsync就不够了,需要配合inotify来监控文件变化。
inotify是Linux内核提供的文件系统事件监控机制,可以监控文件的创建、修改、删除等操作。配合rsync使用,就能实现准实时的文件同步。
最简单的方法是使用inotify-tools这个工具包,先安装一下:
# CentOS/RHEL
yum install inotify-tools
# Ubuntu/Debian
apt-get install inotify-tools
然后写个简单的监控脚本:
#!/bin/bash
SRC_DIR="/var/www/html"
DEST="user@remote_host:/var/www/html/"
inotifywait -mr --timefmt '%Y-%m-%d %H:%M:%S' --format '%T %w %f %e' \
-e modify,create,delete,move $SRC_DIR | while read TIME DIR FILE EVENT
do
echo "$TIME: $DIR$FILE was $EVENT"
rsync -av --delete $SRC_DIR/ $DEST
done
不过这个脚本有个问题,就是每次有文件变化都会触发一次完整的rsync,如果文件变化很频繁,会造成很大的系统负担。
我后来改进了一下,加了个延迟机制,在一定时间内的多次变化只触发一次同步:
#!/bin/bash
SRC_DIR="/var/www/html"
DEST="user@remote_host:/var/www/html/"
DELAY=5
SYNC_LOCK="/tmp/rsync.lock"
inotifywait -mr --format '%w %f %e' -e modify,create,delete,move $SRC_DIR | while read DIR FILE EVENT
do
echo "检测到文件变化: $DIR$FILE ($EVENT)"
# 如果已经有同步任务在等待,就跳过
if [ -f $SYNC_LOCK ]; then
continue
fi
# 创建锁文件
touch $SYNC_LOCK
# 延迟执行同步
(
sleep $DELAY
echo "开始同步..."
rsync -av --delete $SRC_DIR/ $DEST
rm -f $SYNC_LOCK
echo "同步完成"
) &
done
这样就避免了频繁同步的问题,实际使用效果还不错。不过要注意,inotify监控的文件数量是有限制的,可以通过调整内核参数来增加:
echo 8192 > /proc/sys/fs/inotify/max_user_watches
问题:同步后文件权限不正确或无法访问某些文件。
解决方案:
# 确保源文件有读权限
chmod -R +r /source/
# 使用sudo执行rsync
sudo rsync -avz /source/ user@remote:/destination/
# 或者在远程端使用sudo
rsync -avz --rsync-path="sudo rsync" /source/ user@remote:/destination/
问题:在传输大文件时网络不稳定导致中断。
解决方案:
# 使用-P参数支持断点续传和显示进度
rsync -avzP /source/ user@remote:/destination/
# 设置超时和重试
rsync -avz --timeout=60 --contimeout=60 /source/ user@remote:/destination/
# 编写重试脚本
#!/bin/bash
MAX_RETRIES=3
RETRY_COUNT=0
while [ $RETRY_COUNT -lt $MAX_RETRIES ]; do
rsync -avzP /source/ user@remote:/destination/
if [ $? -eq 0 ]; then
echo "同步成功"
break
else
RETRY_COUNT=$((RETRY_COUNT + 1))
echo "第 $RETRY_COUNT 次重试..."
sleep 30
fi
done
问题:目标磁盘空间不够导致同步失败。
解决方案:
# 先进行试运行检查空间需求
rsync -avzn /source/ /destination/ | grep "total size"
# 使用--max-size限制单个文件大小
rsync -avz --max-size=100M /source/ /destination/
# 分批同步,先同步小文件
rsync -avz --max-size=10M /source/ /destination/
rsync -avz --min-size=10M --max-size=100M /source/ /destination/
问题:软链接没有正确处理或指向错误位置。
解决方案:
# 保持软链接(默认行为)
rsync -avz /source/ /destination/
# 复制软链接指向的实际文件内容
rsync -avzL /source/ /destination/
# 转换绝对路径软链接为相对路径
rsync -avz --copy-unsafe-links /source/ /destination/
对于多个独立的同步任务,可以使用并发处理来提高效率。
#!/bin/bash
# 并发同步脚本
sync_task() {
local src=$1
local dst=$2
local name=$3
echo "开始同步 $name..."
rsync -avz "$src/" "$dst/" > "/tmp/sync_${name}.log" 2>&1
if [ $? -eq 0 ]; then
echo "$name 同步成功"
else
echo "$name 同步失败,查看日志: /tmp/sync_${name}.log"
fi
}
# 后台并发执行多个同步任务
sync_task "/data/web1" "user@server1:/backup/web1" "web1" &
sync_task "/data/web2" "user@server2:/backup/web2" "web2" &
sync_task "/data/db" "user@server3:/backup/db" "database" &
# 等待所有后台任务完成
wait
echo "所有同步任务完成"
通过SSH连接复用来减少连接建立的开销。
# 在 ~/.ssh/config 中配置连接复用
cat >> ~/.ssh/config << EOF
Host backup-server
HostName 192.168.1.100
User root
Port 22
ControlMaster auto
ControlPath ~/.ssh/master-%r@%h:%p
ControlPersist 10m
Compression yes
EOF
# 这样多次rsync操作会复用同一个SSH连接
rsync -avz /data1/ backup-server:/backup/data1/
rsync -avz /data2/ backup-server:/backup/data2/
根据网络环境调整rsync的传输参数。
# 高带宽低延迟网络
rsync -avz --compress-level=1 /source/ user@remote:/destination/
# 低带宽高延迟网络
rsync -avz --compress-level=9 --whole-file /source/ user@remote:/destination/
# 局域网内可以关闭压缩
rsync -av --no-compress /source/ user@remote:/destination/
#!/bin/bash
# 完整的定时备份脚本 /usr/local/bin/auto-backup.sh
# 配置参数
SOURCE_DIRS=("/var/www" "/etc" "/home")
BACKUP_SERVER="backup.company.com"
BACKUP_BASE="/backup/$(hostname)"
LOG_DIR="/var/log/backup"
RETENTION_DAYS=7
# 创建日志目录
mkdir -p $LOG_DIR
# 获取当前时间
DATE=$(date +%Y%m%d_%H%M%S)
LOG_FILE="$LOG_DIR/backup_$DATE.log"
# 记录开始时间
echo "=== 备份开始: $(date) ===" > $LOG_FILE
# 检查网络连通性
if ! ping -c 3 $BACKUP_SERVER > /dev/null 2>&1; then
echo "错误: 无法连接到备份服务器 $BACKUP_SERVER" >> $LOG_FILE
exit 1
fi
# 备份每个目录
for dir in "${SOURCE_DIRS[@]}"; do
if [ -d "$dir" ]; then
echo "正在备份 $dir..." >> $LOG_FILE
# 创建远程目录
ssh root@$BACKUP_SERVER "mkdir -p $BACKUP_BASE$(dirname $dir)"
# 执行同步
rsync -avz --delete \
--exclude='*.tmp' \
--exclude='*.log' \
--exclude='cache/' \
--timeout=3600 \
--log-file=$LOG_FILE \
$dir/ root@$BACKUP_SERVER:$BACKUP_BASE$dir/
if [ $? -eq 0 ]; then
echo "$dir 备份成功" >> $LOG_FILE
else
echo "$dir 备份失败" >> $LOG_FILE
# 发送告警
echo "目录 $dir 备份失败" | mail -s "备份失败告警" admin@company.com
fi
else
echo "警告: 目录 $dir 不存在" >> $LOG_FILE
fi
done
# 清理旧日志
find $LOG_DIR -name "backup_*.log" -mtime +$RETENTION_DAYS -delete
echo "=== 备份结束: $(date) ===" >> $LOG_FILE
# 设置定时任务
# crontab -e
# 0 2 * * * /usr/local/bin/auto-backup.sh
#!/bin/bash
# 代码部署脚本
PROJECT_NAME="myapp"
LOCAL_PATH="/var/jenkins/workspace/$PROJECT_NAME"
SERVERS=("web01.company.com" "web02.company.com")
REMOTE_PATH="/var/www/$PROJECT_NAME"
BACKUP_PATH="/var/www/backup/$PROJECT_NAME"
echo "开始部署 $PROJECT_NAME..."
# 检查本地代码是否存在
if [ ! -d "$LOCAL_PATH" ]; then
echo "错误: 本地代码路径不存在"
exit 1
fi
# 部署到每台服务器
for server in "${SERVERS[@]}"; do
echo "正在部署到 $server..."
# 创建备份
ssh root@$server "
mkdir -p $BACKUP_PATH
if [ -d $REMOTE_PATH ]; then
cp -r $REMOTE_PATH $BACKUP_PATH/$(date +%Y%m%d_%H%M%S)
fi
"
# 同步代码
rsync -avz --delete \
--exclude='.git/' \
--exclude='node_modules/' \
--exclude='*.log' \
$LOCAL_PATH/ root@$server:$REMOTE_PATH/
if [ $? -eq 0 ]; then
echo "$server 部署成功"
# 重启服务
ssh root@$server "systemctl restart $PROJECT_NAME"
# 健康检查
sleep 5
if curl -f http://$server/health > /dev/null 2>&1; then
echo "$server 服务启动正常"
else
echo "$server 服务启动异常,正在回滚..."
# 回滚操作
LATEST_BACKUP=$(ssh root@$server "ls -t $BACKUP_PATH | head -1")
ssh root@$server "
rm -rf $REMOTE_PATH
cp -r $BACKUP_PATH/$LATEST_BACKUP $REMOTE_PATH
systemctl restart $PROJECT_NAME
"
fi
else
echo "$server 部署失败"
fi
done
echo "部署完成"
#!/bin/bash
# rsync状态监控脚本
MONITOR_LOG="/var/log/rsync-monitor.log"
ALERT_EMAIL="admin@company.com"
check_rsync_status() {
local log_file=$1
local max_age_hours=2
if [ ! -f "$log_file" ]; then
echo "告警: 同步日志文件不存在"
return 1
fi
# 检查日志文件是否太旧
if [ $(find "$log_file" -mmin +$((max_age_hours * 60)) | wc -l) -gt 0 ]; then
echo "告警: 同步日志超过 $max_age_hours 小时未更新"
return 1
fi
# 检查是否有错误
if grep -q "rsync error\|failed\|No such file" "$log_file"; then
echo "告警: 同步过程中发现错误"
return 1
fi
return 0
}
# 检查各个同步任务
SYNC_LOGS=("/var/log/web-sync.log" "/var/log/db-sync.log" "/var/log/config-sync.log")
for log in "${SYNC_LOGS[@]}"; do
if ! check_rsync_status "$log"; then
echo "$(date): $log 检查失败" >> $MONITOR_LOG
# 发送告警邮件
tail -50 "$log" | mail -s "Rsync同步告警: $(basename $log)" $ALERT_EMAIL
fi
done
rsync作为运维工程师的必备工具,其强大的功能和灵活的配置能够满足各种数据同步需求。无论是日常的文件备份,还是复杂的多服务器部署,rsync都能提供可靠的解决方案。
在实际工作中,建议大家根据具体业务场景选择合适的同步策略,并结合其他工具(如cron、监控系统、配置管理工具等)构建完整的自动化运维体系。
记住,工具的价值在于解决实际问题。理解业务需求,选择合适的技术方案,才能真正发挥rsync的威力,提高运维工作的效率和可靠性。
希望这篇文章能够帮助大家更好地掌握rsync,在运维工作中游刃有余。如果在使用过程中遇到问题,建议查阅官方文档或寻求社区帮助,持续学习和实践是提高技能的最好方法。
如果这篇文章对你有帮助,欢迎点赞转发,让更多的朋友看到。
关注@运维躬行录,获取更多实用的运维技术干货,我们一起在运维的道路上躬行致远!