在日常运维和开发中,我们经常需要在服务器之间同步文件,比如备份网站、分发代码、迁移数据等。rsync
就是为此而生的"神器"。它快速、灵活,并且极其高效,因为它只传输文件的变化部分。
但 rsync
的强大也伴随着它丰富的选项和复杂的权限逻辑。很多初学者在使用时,常常会陷入各种"权限不足"的报错中。
本文将从 rsync
的基础用法讲起,然后通过一个真实网站同步的排错案例 ,一步步深入理解 rsync
的工作原理,彻底攻克那些令人头疼的权限问题。
rsync 快速入门
1. rsync 是什么?
简单来说,rsync
是一个文件同步工具。它可以在本地不同目录间、或在本地与远程服务器间同步文件。它最大的特点是"增量同步",第一次会同步所有内容,之后就只同步修改过的部分,非常节省时间和带宽。
2. 两种核心工作模式
rsync
有两种主要的远程同步模式:
- SSH 模式: 这是最常用、最简单也推荐的模式。它通过 SSH 协议连接远程服务器,安全性和认证都由 SSH 负责。
- Daemon 模式 (守护进程模式) : 远程服务器上需要运行一个
rsync
守护进程(默认监听873端口),客户端通过专门的rsync
协议连接。这种模式配置更复杂,但适合大规模、频繁的同步场景。
它们的语法有明显区别:
bash
# SSH 模式 (使用单冒号 :)
rsync [选项] [源] user@host:/path/to/dest
# Daemon 模式 (使用双冒号 ::)
rsync [选项] [源] user@host::module
本文的案例将聚焦于配置更复杂的 Daemon 模式,因为它的权限问题最具代表性。
3. 最重要的"陷阱":结尾的斜杠 /
在学习具体命令前,必须先掌握 rsync
最容易出错的一个点:源地址末尾的斜杠 /
。
- 没有斜杠
rsync ... /src ... /dest
: 同步目录本身。会把src
整个目录(包括src
这个名字)复制到dest
下,形成/dest/src
。 - 有斜杠
rsync ... /src/ ... /dest
: 同步目录下的内容。会把src
目录里的所有文件和子目录直接复制到dest
下,不会在dest
目录下创建src
这个子目录。
简单记:有斜杠,同步"内容";没斜杠,同步"目录本身"。
常用选项详解
rsync
的选项非常多,但我们日常使用中最核心的是 -avzP
这个组合。
-
-a
(archive): 归档模式,这是一个"全家桶"选项,相当于-rlptgoD
。-r
: 递归同步目录。-l
: 保持符号链接。-p
: 保持文件权限 (permissions)。-t
: 保持文件修改时间 (times)。-g
: 保持文件所属组 (group)。-o
: 保持文件所有者 (owner)。-D
: 保持设备文件等。 总之,-a
选项致力于让目标文件和源文件尽可能保持一模一样。
-
-v
(verbose): 详细模式。会显示同步过程,方便我们观察。 -
-z
(compress): 压缩模式。在传输时压缩数据,能节省带宽,但会增加 CPU 消耗。 -
-P
: 这是--partial
和--progress
的合体。--partial
: 保留因故中断传输的半成品文件,下次能断点续传。--progress
: 显示每个文件的传输进度条,对同步大文件非常友好。
还有几个在实战中非常有用的选项:
--delete
: 删除目标目录中,源目录已经不存在的文件,让两边真正"一模一样"。(危险操作,使用前务必确认!)--exclude=PATTERN
: 排除掉符合某种模式的文件,例如--exclude=*.log
。--exclude-from=FILE
: 从一个文件中读取排除规则列表。--bwlimit=KBPS
: 限制传输带宽,单位是 KB/s。避免rsync
占满带宽。
实战排错:同步网站遇到的连环"坑"
理论说完了,我们来看一个真实的案例。
目标 : 将服务器 A (10.0.0.1
) 的网站目录 /www/wwwroot/site.com/
,通过 rsync
守护进程(Daemon)模式,同步到服务器 B (10.0.0.2
)。
场景设定
-
服务器 B (目标端) : 已经配置好了
rsync
守护进程。其配置文件/etc/rsyncd.conf
内容如下,定义了一个名为website
的模块:ini# 全局配置 uid = root # 全局默认使用 root 用户写入 gid = root # 全局默认使用 root 组 use chroot = no max connections = 200 timeout = 600 log file = /var/log/rsyncd.log # "website" 模块配置 [website] path = /www/wwwroot/site.com/ comment = Website Backup read only = false ignore errors # 模块级配置会覆盖全局配置,这是关键! uid = www # 强制写入文件的所有者为 www gid = www # 强制写入文件的所属组为 www auth users = myuser secrets file = /etc/rsync.secrets
-
服务器 B (目标端) : 认证文件
/etc/rsync.secrets
内容为用户名:密码
格式:makefilemyuser:YourStrongPassword123
-
服务器 A (源端): 我们将把密码存储在一个文件中,以便命令可以自动读取。
关键的安全细节:密码文件
在客户端(服务器A),我们需要创建一个文件来存放密码,并通过 --password-file
参数指定它。
这个密码文件有严格的要求:
- 文件中只包含密码,不包含用户名和冒号。
- 为了安全,该文件的权限必须 设置为
600
(只有所有者可读写)。否则rsync
会拒绝使用它。
bash
# 在服务器 A 上操作
echo "YourStrongPassword123" > /etc/rsync.pass
chmod 600 /etc/rsync.pass
初始命令
一切就绪,我们在服务器 A 上写出了第一版命令:
bash
/usr/bin/rsync -avzP \
--port=873 \
--bwlimit=10240 \
--exclude-from=/etc/rsync_exclude.list \
--password-file=/etc/rsync.pass \
/www/wwwroot/site.com/ \
myuser@10.0.0.2::website
执行后,报错了。
坑 1: "文件或目录不存在"
text
rsync: failed to open exclude file /etc/rsync_exclude.list: No such file or directory (2)
rsync error: error in file IO (code 11)
- 问题分析 : 错误信息非常直白,
failed to open exclude file
,找不到排除规则文件。 - 原因 :
rsync
命令严格按照指令去找/etc/rsync_exclude.list
,但这个文件我们忘了创建。 - 解决方案 :
- 创建文件 : 在服务器 A 上
touch /etc/rsync_exclude.list
,然后把排除规则写进去。 - 规则解释 :
**/*.log
:**
代表任意深度的目录,所以这条规则会排除所有子目录下的.log
文件。upload/
: 排除顶层的upload
目录及其所有内容。
- 如果不需要排除任何文件,直接从命令中删除
--exclude-from
这部分即可。
- 创建文件 : 在服务器 A 上
我们选择创建文件并加入规则,问题解决,继续执行。又报错了!
坑 2: "chgrp: 操作不被允许"
text
rsync: chgrp "some/file.html" (in website) failed: Operation not permitted (1)
...
rsync error: some files/attrs were not transferred (code 23)
- 问题分析 :
chgrp failed
,rsync
在目标服务器上尝试更改文件所属组 失败。Operation not permitted
表明这是个权限问题。 - 原因 :
- 客户端要求 : 我们的命令里有
-a
,其中包含了-g
(保持所属组)。 - 源文件状态 : 服务器 A 上的网站文件,所属组是
root
。 - 服务器端限制 : 服务器 B 的
rsyncd.conf
已指定uid=www
和gid=www
,所以写入文件时,进程是以普通用户www
的身份运行的。 - 权限冲突 :
rsync
在服务器 B 上以www
用户的身份创建了文件,然后接到客户端的指令:"请把这个文件的所属组改成root
"。一个普通用户www
当然没有权限把文件交给root
组。于是,操作被系统拒绝。
- 客户端要求 : 我们的命令里有
- 解决方案 : 既然目标服务器已经强制指定了所有权,客户端就不应该再提要求。我们修改命令,将
-a
拆开,去掉-g
(所属组) 和-o
(所有者)。
修改后的命令选项 : -rltpDvzP
(从 -a
中去掉了 -g
和 -o
)
bash
/usr/bin/rsync -rltpDvzP ... [其他部分不变]
我们修正命令,再次尝试,结果......又报错了!
坑 3: "设置权限: 操作不被允许"
text
rsync: failed to set permissions on "some/file.html" (in website): Operation not permitted (1)
...
rsync error: some files/attrs were not transferred (code 23)
- 问题分析 :
failed to set permissions
,这次是设置文件权限位失败了。 - 原因 :
- 客户端要求 : 我们的新命令里还有
-p
(保持权限)。 - 源文件状态 : 服务器 A 上的某些文件或目录,可能带有一些只有
root
才能设置的特殊权限位,比如sticky bit
(粘滞位t
)。 - 服务器端限制 :
www
用户没有权限设置这些特殊的权限位。 - 权限冲突 :
rsync
在服务器 B 上以www
用户的身份,尝试复制一个只有root
才能设置的特殊权限,再次被系统无情拒绝。
- 客户端要求 : 我们的新命令里还有
- 解决方案: 我们需要再次"让步",不强求权限位完全一致,而是给所有同步过去的文件设置一个统一、安全、合理的权限。
最终的完美命令 : 去掉 -p
,改用 --chmod
。
bash
/usr/bin/rsync -rltDvzP \
--chmod=D755,F644 \
--port=873 \
--bwlimit=10240 \
--exclude-from=/etc/rsync_exclude.list \
--password-file=/etc/rsync.pass \
/www/wwwroot/site.com/ \
myuser@10.0.0.2::website

--chmod=D755,F644
: 这个选项是解决问题的关键。它告诉rsync
:- 所有目录 (D) 的权限都设置为
755
(rwxr-xr-x
)。 - 所有文件 (F) 的权限都设置为
644
(rw-r--r--
)。 这正好是 Web 服务器最标准、最安全的权限配置。
- 所有目录 (D) 的权限都设置为
再次执行这个最终版的命令,屏幕上终于滚动起正常的同步列表,所有报错都消失了。
rsync 权限问题的核心思想
通过这次排错之旅,我们可以总结出使用 rsync
(特别是守护进程模式)时关于权限的核心思想:
-
明确责任划分:
- 服务器端 (
rsyncd.conf
) :负责决定同步过来的文件**"属于谁"**。通过uid
和gid
参数来强制指定所有者和所属组。 - 客户端 (rsync 命令) :负责传输文件内容。应该放弃对所有权和特殊权限的控制,避免向服务器提出它无法完成的"权限要求"。
- 服务器端 (
-
告别对
-a
的盲目依赖 :-a
选项虽然方便,但在复杂的权限场景下(比如以非 root 用户同步),它就是权限问题的根源。要学会将它拆解为-rltpD
等,并根据实际情况去掉-g
,-o
,-p
。 -
主动设置目标权限 : 使用
--chmod
是一个非常好的实践。它能确保目标文件的权限统一、正确且安全,而不是依赖于源文件那可能混乱或不适用的权限设置。