2019-06-24-打造文件实时同步架构之rsync篇

rsync是可以实现增量备份的工具。配合任务计划,rsync能实现定时或间隔同步,配合inotify或sersync,可以实现触发式的实时同步。事实上,rsync有一套自己的算法,其算法原理以及rsync对算法实现的机制可能比想象中要复杂一些。平时使用rsync实现简单的备份、同步等功能足以,没有多大必要去深究这些原理性的内容。如果你对这些原理感兴趣,可以通过rsync命令的man文档、并且借助”-vvvv”分析rsync执行过程,结合rsync的算法原理去深入理解。本篇文章由于篇幅有限,只是介绍rsync的使用方法和它常用的功能,以及rsync和sersync如何配合打造文件实时同步架构。

rsync文件同步机制简介

rsync是一款开源的、快速的、多功能的、可实现全量及增量的本地或远程数据镜像同步备份的优秀工具。rsync适用于Unix/Linux/Windows等多种操作系统平台。

rsync可以实现scp的远程拷贝(rsync不支持远程到远程的拷贝,但scp支持)、cp的本地拷贝、rm删除和”ls -l”显示文件列表等功能。但需要注意的是,rsync的最终目的或者说其原始目的是实现两端主机的文件同步,因此实现的scp/cp/rm等功能仅仅只是同步的辅助手段,且rsync实现这些功能的方式和这些命令是不一样的。

rsync的目的是实现本地主机和远程主机上的文件同步(包括本地推到远程,远程拉到本地两种同步方式),也可以实现本地不同路径下文件的同步,但不能实现远程路径1到远程路径2之间的同步,这个功能scp是可以实现的。

不考虑rsync的实现细节,就文件同步而言,涉及了源文件和目标文件的概念,还涉及了以哪边文件为同步基准。例如,想让目标主机上的文件和本地文件保持同步,则是以本地文件为同步基准,将本地文件作为源文件推送到目标主机上。反之,如果想让本地主机上的文件和目标主机上的文件保持同步,则目标主机上的文件为同步基准,实现方式是将目标主机上的文件作为源文件拉取到本地。当然,要保持本地的两个文件相互同步,rsync也一样能实现,这就像Linux中cp命令一样,以本地某文件作为源,另一文件作为目标文件,但是这两者实现的本质是完全不同的。

既然是文件同步,在同步过程中必然会涉及到源和目标两文件之间版本控制的问题,例如是否要删除源主机上没有但目标上多出来的文件,目标文件比源文件更新(newer than source)时是否仍要保持同步,遇到软链接时是拷贝软链接本身还是拷贝软链接所指向的文件,目标文件已存在时是否要先对其做个备份等等。

rsync同步过程中由两部分模式组成:决定哪些文件需要同步的检查模式以及文件同步时的同步模式。

1)检查模式是指按照指定规则来检查哪些文件需要被同步,例如哪些文件是明确被排除不传输的。默认情况下,rsync使用“quick check”算法快速检查源文件和目标文件的大小、mtime(修改时间)是否一致,如果不一致则需要传输。当然,也可以通过在rsync命令行中指定某些选项来改变quick check的检查模式,比如”–size-only”选项表示”quick check”将仅检查文件大小不同的文件作为待传输文件。rsync支持非常多的选项,其中检查模式的自定义性是非常有弹性的。

2)同步模式是指在文件确定要被同步后,在同步过程发生之前要做哪些额外工作。例如:上文所说的是否要先删除源主机上没有但目标主机上有的文件,是否要先备份已存在的目标文件,是否要追踪链接文件等额外操作。rsync也提供非常多的选项使得同步模式变得更具弹性。

相对来说,为rsync手动指定同步模式的选项更常见一些,只有在有特殊需求时才指定检查模式,因为大多数检查模式选项都可能会影响rsync的性能。

rsync的三种工作模式

rsync命令有三种常见模式,具体如下:

1)本地模式:本地文件系统上实现同步,命令行语法格式如下:

    • rsync [option] [SRC] [DEST]
    • rsync [选项] [源文件] [目标文件]

2)通过远程Shell访问模式:本地主机使用远程shell和远程主机通信,命令行语法格式如下:

  • 拉取(Pull):

      • rsync [option] [USER@]HOST:SRC [DEST]
      • rsync [选项] 用户@主机:源文件 [目标文件]
  • 推送(Push):

      • rsync [option] [SRC] [USER@]HOST:DEST
      • rsync [选项] [源文件] 用户@主机:目标文件

3)rsync守护进程模式:本地主机通过网络套接字连接远程主机上的rsync daemon,命令行语法格式如下:

  • 拉取(Pull):

      • rsync [option] [USER@]HOST::SRC [DEST]
      • rsync [选项] 用户@主机::源文件 [目标文件]
      • rsync [option] rsync://[USER@]HOST[:PORT]/SRC [DEST]
      • rsync [选项] rsync://用户@主机:端口/源文件 [目标文件]
  • 推送(Push):

      • rsync [option] SRC [USER@]HOST::DEST
      • rsync [选项] [源文件] 用户@主机::目标文件
      • rsync [option] SRC rsync://[USER@]HOST[:PORT]/DEST
      • rsync [选项] [源文件] rsync://用户@主机:端口/目标文件

前两者的本质是通过管道通信,而第三种则是让远程主机上运行rsync服务,使其监听在一个端口上,等待客户端的连接。

其实,还有第四种工作方式:通过远程shell也能临时启动一个rsync daemon,它不同于方式3,它不要求远程主机上事先启动rsync服务,而是临时派生出rsync daemon,属于单用途的一次性daemon,仅用于临时读取daemon的配置文件,当此次rsync同步完成,远程shell启动的rsync daemon进程也会自动消逝。此通信方式的命令行语法格式同方式3,但要求options部分必须明确指定”–rsh”选项或其短选项”-e”。

上述语法格式中,如果仅有一个SRC或DEST参数,则将以类似于”ls -l”的方式列出源文件列表(只有一个路径参数,总会认为是源文件),而不是复制文件。

另外,使用rsync一定要注意的一点是,源路径如果是一个目录的话,带上尾随斜线和不带尾随斜线是不一样的,不带尾随斜线表示的是整个目录包括目录本身,带上尾随斜线表示的是目录中的文件,不包括目录本身。

由于rsync支持一百多个选项,所以此处只介绍几个常用选项。完整的选项说明以及rsync的使用方法参见系统的man rsync帮助文档。其中,重要参数选项如下:

  • -v:显示rsync过程中详细信息。可以使用”-vvvv”获取更详细信息。
  • -P:显示文件传输的进度信息。(实际上”-P”=”–partial –progress”,其中的”–progress”才是显示进度信息的)。
  • -n –dry-run:仅测试传输,而不实际传输。常和”-vvvv”配合使用来查看rsync是如何工作的。
  • -a –archive:归档模式,表示递归传输并保持文件属性。等同于”-rtopgDl”。
  • -r –recursive:递归到目录中去。
  • -t –times:保持mtime属性。强烈建议任何时候都加上”-t”,否则目标文件mtime会设置为系统时间,导致下次更新检查出mtime不同从而导致增量传输无效。
  • -o –owner:保持owner属性(属主)。
  • -g –group:保持group属性(属组)。
  • -p –perms:保持perms属性(权限,不包括特殊权限)。
  • -D:是”–device –specials”选项的组合,即也拷贝设备文件和特殊文件。
  • -l –links:如果文件是软链接文件,则拷贝软链接本身而非软链接所指向的对象。
  • -z:传输时进行压缩提高效率。
  • -R –relative:使用相对路径。意味着将命令行中指定的全路径而非路径最尾部的文件名发送给服务端,包括它们的属性。
  • –size-only:默认算法是检查文件大小和mtime不同的文件,使用此选项将只检查文件大小。
  • -u –update:仅在源mtime比目标已存在文件的mtime新时才拷贝。注意,该选项是接收端判断的,不会影响删除行为。
  • -d –dirs:以不递归的方式拷贝目录本身。默认递归时,如果源为”dir1/file1”,则不会拷贝dir1目录,使用该选项将拷贝dir1但不拷贝file1。
  • –max-size:限制rsync传输的最大文件大小。可以使用单位后缀,还可以是一个小数值(例如:”–max-size=1.5m”)
  • –min-size:限制rsync传输的最小文件大小。这可以用于禁止传输小文件或那些垃圾文件。
  • –exclude:指定排除规则来排除不需要传输的文件。
  • –delete:以SRC为主,对DEST进行同步。多则删之,少则补之。注意”–delete”是在接收端执行的,所以它是在exclude/include规则生效之后才执行的。
  • -b –backup :对目标上已存在的文件做一个备份,备份的文件名后默认使用”~”做后缀。
  • –backup-dir:指定备份文件的保存路径。不指定时默认和待备份文件保存在同一目录下。
  • -e :指定所要使用的远程shell程序,默认为ssh。
  • –port:连接daemon时使用的端口号,默认为873端口。
  • –password-file:daemon模式时的密码文件,可以从中读取密码实现非交互式。注意,这不是远程shell认证的密码,而是rsync模块认证的密码。
  • -W –whole-file:rsync将不再使用增量传输,而是全量传输。在网络带宽高于磁盘带宽时,该选项比增量传输更高效。
  • –existing:要求只更新目标端已存在的文件,目标端还不存在的文件不传输。注意,使用相对路径时如果上层目录不存在也不会传输。
  • –ignore-existing:要求只更新目标端不存在的文件。和”–existing”结合使用有特殊功能,见下文示例。
  • –remove-source-files:要求删除源端已经成功传输的文件。

虽然选项非常多,但最常用的选项组合是”avz”,即压缩和显示部分信息,并以归档模式传输。

rsync本地和远程shell使用示例

以下示例既可以通过shell远程链接的工作模式实现,也可以在本地模式实现,具体的实现方式不同,应用的实际场景就不同,这里只是作为示例讲解,在实际使用时要灵活变通。

1)将/etc/hosts文件拷贝到本地/tmp目录下

1
2
3
[root@c7-test01 ~]# rsync /etc/hosts /tmp
[root@c7-test01 ~]# ls -l /tmp/hosts
-rw-r--r-- 1 root root 158 Jun 3 14:13 /tmp/hosts

2)将/etc/cron.d目录拷贝到/tmp目录下

1
2
3
[root@c7-test01 ~]# rsync -r /etc/cron.d /tmp
[root@c7-test01 ~]# ls -ld /tmp/cr*
drwxr-xr-x 2 root root 4096 Jun 3 14:14 /tmp/cron.d

3)将/etc/cron.d目录拷贝到/tmp下,但要求在/tmp下也生成etc子目录

1
2
3
4
5
6
7
8
9
10
# 使用-R选项,利用相对路径拷贝机制

[root@c7-test01 ~]# rsync -R -r /etc/cron.d /tmp
[root@c7-test01 ~]# tree -l /tmp/etc/
/tmp/etc/
└── cron.d
├── 0hourly
├── raid-check
└── sysstat
1 directory, 3 files

其中”-R”选项表示使用相对路径,此相对路径是以目标目录为根的。对于上面的示例,表示在目标上的/tmp下创建etc/cron.d目录,即/tmp/etc/cron.d,etc/cron.d的根”/“代表的就是目标/tmp。

4)如果要拷贝的源路径较长,但只想在目标主机上保留一部分目录结构,例如,要拷贝/var/log/anaconda/*到/tmp下,但只想在/tmp下保留从log开始的目录,这时可以使用一个点代表相对路径的起始位置即可,也就是将长目录进行划分。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
[root@c7-test01 ~]# rsync -R -r /var/./log/anaconda /tmp
[root@c7-test01 ~]# tree -l /tmp/log/
/tmp/log/
└── anaconda
├── anaconda.log
├── ifcfg.log
├── journal.log
├── ks-script-n7L3eP.log
├── ks-script-v6NMcO.log
├── packaging.log
├── program.log
├── storage.log
└── syslog
1 directory, 9 files

这种方式,从点开始的目录都是相对路径,其相对根目录为目标路径。所以对于上面的示例,将在目标上创建/tmp/log/anaconda/*。

5)对远程目录下已存在的文件做一个备份

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
[root@c7-test01 ~]# rsync -R -r --backup /var/./log/anaconda /tmp
[root@c7-test01 ~]# ls -l /tmp/log/anaconda/
total 4880
-rw------- 1 root root 10945 Jun 3 14:22 anaconda.log
-rw------- 1 root root 10945 Jun 3 14:18 anaconda.log~
-rw------- 1 root root 18216 Jun 3 14:22 ifcfg.log
-rw------- 1 root root 18216 Jun 3 14:18 ifcfg.log~
-rw------- 1 root root 1711915 Jun 3 14:22 journal.log
-rw------- 1 root root 1711915 Jun 3 14:18 journal.log~
-rw------- 1 root root 0 Jun 3 14:22 ks-script-n7L3eP.log
-rw------- 1 root root 0 Jun 3 14:18 ks-script-n7L3eP.log~
-rw------- 1 root root 0 Jun 3 14:22 ks-script-v6NMcO.log
-rw------- 1 root root 0 Jun 3 14:18 ks-script-v6NMcO.log~
-rw------- 1 root root 312693 Jun 3 14:22 packaging.log
-rw------- 1 root root 312693 Jun 3 14:18 packaging.log~
-rw------- 1 root root 29448 Jun 3 14:22 program.log
-rw------- 1 root root 29448 Jun 3 14:18 program.log~
-rw------- 1 root root 78942 Jun 3 14:22 storage.log
-rw------- 1 root root 78942 Jun 3 14:18 storage.log~
-rw------- 1 root root 320770 Jun 3 14:22 syslog
-rw------- 1 root root 320770 Jun 3 14:18 syslog~

通过–backup参数可以在目标目录下,对已存在的文件就被做一个备份,备份文件默认使用”~”做后缀,可以使用”–suffix”指定备份后缀。同时,可以使用“–backup-dir”指定备份文件保存路径,但要求保存路径必须存在。指定备份路径后,默认将不会加备份后缀,除非使用”–suffix”显式指定后缀,如”–suffix=~”。代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
[root@c7-test01 ~]# rsync -R -r --backup-dir=/tmp/backup --backup /var/./log/anaconda /tmp
[root@c7-test01 ~]# ls -l /tmp/backup/log/anaconda/
total 2440
-rw------- 1 root root 10945 Jun 3 14:22 anaconda.log
-rw------- 1 root root 18216 Jun 3 14:22 ifcfg.log
-rw------- 1 root root 1711915 Jun 3 14:22 journal.log
-rw------- 1 root root 0 Jun 3 14:22 ks-script-n7L3eP.log
-rw------- 1 root root 0 Jun 3 14:22 ks-script-v6NMcO.log
-rw------- 1 root root 312693 Jun 3 14:22 packaging.log
-rw------- 1 root root 29448 Jun 3 14:22 program.log
-rw------- 1 root root 78942 Jun 3 14:22 storage.log
-rw------- 1 root root 320770 Jun 3 14:22 syslog

6)源地址带与不带斜线(/)的区别的例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
# 创建实验环境

[root@C7-Server01 ~]# mkdir -p /data1/{test1,test2}/data2

# 如果源目录的末尾有斜线,就会复制目录内的内容,而不是复制目录本身

[root@C7-Server01 ~]# rsync -av /data1/ /data2/ # 本地同步
sending incremental file list
created directory /data2
./
test1/
test1/data2/
test2/
test2/data2/

sent 149 bytes received 64 bytes 426.00 bytes/sec
total size is 0 speedup is 0.00

# 如果源目录没有斜线,则会复制目录本身及目录下的内容

[root@C7-Server01 ~]# rsync -av /data1 /data2 # 本地同步
sending incremental file list
data1/
data1/test1/
data1/test1/data2/
data1/test2/
data1/test2/data2/

sent 155 bytes received 36 bytes 382.00 bytes/sec
total size is 0 speedup is 0.00

# 查看/data2目录下文件信息

[root@C7-Server01 ~]# ls -l /data2
total 0
drwxr-xr-x 4 root root 32 Apr 28 01:27 data1
drwxr-xr-x 3 root root 19 Apr 28 01:27 test1
drwxr-xr-x 3 root root 19 Apr 28 01:27 test2

源路径如果是一个目录的话,带上尾随斜线和不带尾随斜线是不一样的,不带尾随斜线表示的是整个目录包括目录本身,带上尾随斜线表示的是目录中的文件,不包括目录本身。

7)删除文件的特殊例子

当一个目录下有几十万或十几万个文件,使用rsync的–deleye选项可以很快进行删除。这里注意一点,是删除目录下的所有文件,而不是目录本身。

选项–delete使tmp目录内容和空目录保持一致,不同的文件及目录将会被删除,即null里有什么内容,tmp里就有什么内容,null里没有的,而tmp里有的就必须要删除,因为null目录为空,因此此命令会删除/tmp目录中的所有内容。使用”–delete”选项后,接收端的rsync会先删除目标目录下已经存在,但源端目录不存在的文件。也就是”多则删之,少则补之”。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
# 查看tmp目录下文件信息

[root@C7-Server01 ~]# ls -l /tmp
total 4
drwxr-xr-x 3 root root 23 Apr 28 00:22 home
-rw-r--r-- 1 root root 187 Apr 22 22:56 hosts
drwx------ 3 root root 17 Apr 27 21:43 systemd-private-5b1137097c084a899dfd1557504d079d-chronyd.service-6Ra806
。。。
drwx------ 2 root root 6 Apr 23 18:24 vmware-root_9660-3101179142

# 创建一个空目录null

[root@C7-Server01 ~]# mkdir /null

# 删除tmp目录下所有文件和子目录

[root@C7-Server01 ~]# rsync -av --delete /null/ /tmp/
sending incremental file list
deleting vmware-root_9660-3101179142/
deleting vmware-root_9609-4121731445/
deleting vmware-root_9604-3101310240/
deleting vmware-root_9573-4146439782/
deleting vmware-root_9458-2857896559/
deleting vmware-root_9456-2866351085/
。。。
deleting hosts
./

sent 47 bytes received 3,911 bytes 7,916.00 bytes/sec
total size is 0 speedup is 0.00

# 再次查看/tmp目录下文件和子目录信息

[root@C7-Server01 ~]# ls -l /tmp
total 0

8)”–existing”和”–ignore-existing”使用示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 创建测试环境,测试环境的结构如下:

[root@c7-test01 ~]# tree /tmp/{a,b}
/tmp/a
├── bashrc
├── c
│ └── find
├── fstab
├── profile
└── rc.local
/tmp/b
├── crontab
├── fstab
├── profile
└── rc.local
1 directory, 9 files

“–existing”是只更新目标端已存在的文件。目前/tmp/{a,b}目录中内容如上,bashrc在a目录中,crontab在b目录中,且a目录中多了一个c子目录。如下结果只有3个目标上已存在的文件被更新了,由于目标上没有c目录,所以c目录中的文件也没有进行传输。

1
2
3
4
5
6
7
[root@c7-test01 ~]# rsync -r -v --existing /tmp/a/ /tmp/b 
sending incremental file list
fstab
profile
rc.local
sent 2972 bytes received 70 bytes 6084.00 bytes/sec
total size is 204755 speedup is 67.31

“–ignore-existing”是更新目标端不存在的文件。如下:

1
2
3
4
5
6
7
[root@c7-test01 ~]# rsync -r -v --ignore-existing /tmp/a/ /tmp/b
sending incremental file list
bashrc
c/
c/find
sent 231 bytes received 62 bytes 586.00 bytes/sec
total size is 0 speedup is 0.00

“–existing”和”–ignore-existing”结合使用时,有个特殊功效,当它们结合”–delete”使用的时候,文件不会传输,但会删除receiver端额外多出的文件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# 创建实验环境,如下

[root@c7-test01 ~]# tree {a,b}
a
├── stu01
├── stu02
├── stu03
└── stu04
b
└── a.log
0 directories, 5 files
# 使用-n选项测试传输,并没有实际效果
[root@c7-test01 ~]# rsync -nrv --delete a/ b/
sending incremental file list
deleting a.log
stu01
stu02
stu03
stu04

sent 104 bytes received 33 bytes 274.00 bytes/sec
total size is 0 speedup is 0.00 (DRY RUN)

# --existing && --ignore-existing && --delete结合使用,是删除接收端(目的端)多余的文件,且不会传输文件
[root@c7-test01 ~]# rsync -nrv --existing --ignore-existing --delete a/ b/
sending incremental file list
deleting a.log

sent 92 bytes received 21 bytes 226.00 bytes/sec
total size is 0 speedup is 0.00 (DRY RUN)
[root@c7-test01 ~]# rsync -nrv --existing --ignore-existing --delete b/ a/
sending incremental file list
deleting stu04
deleting stu03
deleting stu02
deleting stu01

sent 58 bytes received 48 bytes 212.00 bytes/sec
total size is 0 speedup is 0.00 (DRY RUN)

实际上,“–existing”和”–ingore-existing”是传输规则,只会影响receiver要求让sender传输的文件列表,属于传输模式。而receiver决定哪些文件在传输之前如何处理属于同步模式,所以各种同步规则,比如:”–delete”等操作都不会被这两个选项影响。

9)”–remove-source-files”删除源端文件

使用该选项后,源端已经更新成功的文件都会被删除,源端所有未传输或未传输成功的文件都不会被移除。未传输成功的原因有多种,如exclude排除了,”quick check”未选则该文件,传输中断等等。总之,显示在”rsync -v”被传输列表中的文件都会被移除。如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
[root@c7-test01 ~]# rsync -r -v --remove-source-files a/ b/ .
sending incremental file list
a.log
stu01
stu02
stu03
stu04
sent 331 bytes received 151 bytes 964.00 bytes/sec
total size is 0 speedup is 0.00
[root@c7-test01 ~]# ls -l {a/,b/}
a/:
total 0
b/:
total 0

上述显示出来的文件在源端全部被删除。

10)”–exclude”排除规则

使用”–exclude”选项指定排除规则,排除那些不需要传输的文件。如下:

1
2
3
4
5
6
7
8
9
[root@c7-test01 ~]# rsync -r -v --exclude=/tmp/a/* /tmp/a/ /tmp/b/* a/
sending incremental file list
c/
sent 81 bytes received 16 bytes 194.00 bytes/sec
total size is 0 speedup is 0.00
[root@c7-test01 ~]# ls /tmp/b
c
[root@c7-test01 ~]# ls /tmp/a
c

上面的代码意思是不传送/tmp/a下的所有文件,但是传送/tmp/b下的所有文件,因此在传送列表中只有一个c/目录准备传送。

需要注意的是:一个”–exclude”只能指定一条规则,要指定多条排除规则,需要使用多个”–exclude”选项,或者将排除规则写入到文件中,然后使用”–exclude-from”选项读取该规则文件。

另外,除了”–exclude”排除规则,还有”–include”包含规则,顾名思义,它就是筛选出要进行传输的文件,所以include规则也称为传输规则。它的使用方法和”–exclude”一样。如果一个文件即能匹配排除规则,又能匹配包含规则,则先匹配到的立即生效,生效后就不再进行任何匹配。

关于规则,最重要的一点是它的作用时间。当发送端敲出rsync命令后,rsync将立即扫描命令行中给定的文件和目录(扫描过程中还会按照目录进行排序,将同一个目录的文件放在相邻的位置),这称为拷贝树(copy tree),扫描完成后将待传输的文件或目录记录到文件列表中,然后将文件列表传输给接收端。筛选规则的作用时刻是在扫描拷贝树时,会根据规则来匹配并决定文件是否记录到文件列表中(严格地说是所有文件都会记录到文件列表中的,只不过排除的文件会被标记为hide隐藏起来),只有记录到了文件列表中的文件或目录才是真正需要传输的内容。换句话说,筛选规则的生效时间在rsync整个同步过程中是非常靠前的,它会影响很多选项的操作对象,最典型的如”–delete”。

实际上,排除规则和包含规则都只是”–filter”筛选规则的两种特殊规则。“–filter”比较复杂,它有自己的规则语法和匹配模式,由于篇幅有限,以及考虑到本文实际应用定位,”–filter”规则不便在此多做解释,仅简单说明下规则类。

以下是rsync中的规则种类,不解之处请结合下文的”–delete”分析:

1)exclude规则:即排除规则,只作用于发送端,被排除的文件不会进入文件列表(实际上是加上隐藏规则进行隐藏)。

2)include规则:即包含规则,也称为传输规则,只作用于发送端,被包含的文件将明确记录到文件列表中。

3)hide规则:即隐藏规则,只作用于发送端,隐藏后的文件对于接收端来说是看不见的,也就是说接收端会认为它不存在于源端。

4)show规则:即显示规则,只作用于发送端,是隐藏规则的反向规则。

5)protect规则:即保护规则,该规则只作用于接收端,被保护的文件不会被删除掉。

6)risk规则:即取消保护规则。是protect的反向规则。

除此之外,还有一种规则是”clear规则”,作用是删除include/exclude规则列表。

rsync在发送端将文件列表发送给接收端后,接收端的generato进程会扫描每个文件列表中的信息,然后对列表中的每个信息条目都计算数据块校验码,最后将数据库校验码发给发送端,发送端通过校验码来匹配哪些数据块是需要传输的,这样就实现了增量传输的功能:只传输改变的部分,不会传输整个文件。而delete删除的时间点是generator进程处理每个文件列表时、生成校验码之前进行的,先将目标上存在但源上不存在的多余文件删除,这样就无需为多余的文件生成校验码。

但是,如果exclude和delete规则同时使用时,却不会删除被exclude排除的文件。这是因为,delete动作是比”–exclude”规则更晚执行的,被”–exclude”规则排除的文件不会进入文件列表中,在执行了delete时会认为该文件不存在于源端,从而会导致目标端将这些文件删除。但这是想当然的,尽管理论上确实是这样的,但是rsync为了防止众多误删除情况,提供了两种规则:保护规则(protect)和取消保护规则(risk)。默认情况下,“–delete”和”–exclude”一起使用时,虽然发送端的exclude规则将文件标记为隐藏,使得接收端认为这些被排除文件在源端不存在,但rsync会将这些隐藏文件标记为保护文件,使得它们不受delete行为的影响,这样delete就删除不了这些被排除的文件。如果还是想要强行删除被exclude排除的文件,可以使用”–delete-excluded”选项强制取消保护,这样即使被排除的文件也会被删除。

11)指定ssh连接参数,如端口、连接的用户、ssh选项等

1
2
3
4
5
[root@c7-test01 ~]# rsync -e "ssh -p 22 -o StrictHostKeyChecking=no" /etc/fstab 192.168.101.252:/tmp
Warning: Permanently added '192.168.101.252' (ECDSA) to the list of known hosts.
root@192.168.101.252's password:
Permission denied, please try again.
root@192.168.101.252's password:

在要求保障数据安全的场景下,可以使用-e选项借助SSH隧道进行加密传输数据,-p是SSH命令的选项,指定SSH传输的端口号为22,-o是SSH的认证方式,上述示例表示不通过秘钥认证,可见直接指定ssh参数是生效的。

12)拉取或推送文件及目录(类似scp命令)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
# 从Server02上拉取/home/目录到本地/tmp目录下

[root@C7-Server01 ~]# rsync -av 192.168.101.82:/home/ /tmp/
root@192.168.101.82's password:
receiving incremental file list
./
kkutysllb/
kkutysllb/.bash_history
kkutysllb/.bash_logout
kkutysllb/.bash_profile
kkutysllb/.bashrc
kkutysllb/202012312234.55
kkutysllb/data001
kkutysllb/data002
kkutysllb/data003
kkutysllb/data004
。。。
sent 767 bytes received 4,507 bytes 958.91 bytes/sec
total size is 1,440 speedup is 0.27

# 查看本地/tmp目录下内容

[root@C7-Server01 ~]# ls -l /tmp
total 4
drwx------ 7 root root 4096 Apr 28 00:00 kkutysllb

# 推送本地/etc/udev/目录下所有内容到Server02的/home/kkutysllb/目录下

[root@C7-Server01 ~]# rsync -av /etc/udev 192.168.101.82:/home/kkutysllb/
root@192.168.101.82's password:
sending incremental file list
udev/
udev/hwdb.bin
udev/udev.conf
udev/rules.d/

sent 7,944,769 bytes received 66 bytes 1,765,518.89 bytes/sec
total size is 7,942,619 speedup is 1.00

与scp命令复制的结果进行对比可以发现,使用rsync复制时,重复执行复制直至目录下文件相同就不再进行复制了。

rsync使用技巧

1)实际运维场景常用选项-avz,相当于-vzrtopg(这是网上文档常见的选项),但是此处建议大家使用-avz选项,更简单明了。如果在脚本中使用也可以省略-v选项。

2)关于z压缩选项的使用建议,如果为内网环境,且没有其他业务占用带宽,可以不使用z选项。不压缩传输,几乎可以满带宽传输(千M网络),压缩传输则网络发送速度就会骤降,压缩的速率赶不上传输的速度。

3)选项n是一个提高安全性的选项,它可以结合-v选项输出模拟的传输过程,如果没有错误,则可以去除n选项真正的传输文件。

rsync daemon模式

既然rsync通过远程shell就能实现两端主机上的文件同步,还要使用rsync的服务干啥?试想下,你有的机器上有一堆文件需要时不时地同步到众多机器上去,比如目录a、b、c是专门传输到web服务器上的,d/e、f、g/h是专门传输到ftp服务器上的,还要对这些目录中的某些文件进行排除,如果通过远程shell连接方式,无论是使用排除规则还是包含规则,甚至一条一条rsync命令地传输,这都没问题,但太过繁琐且每次都要输入同样的命令显得太死板。使用rsync daemon就可以解决这种死板问题。而且,rsync daemon是向外提供服务的,这样只要告诉了别人rsync的url路径,外人就能向ftp服务器一样获取文件列表并进行选择性地下载,所以,你所制定的列表,所有人都可以获取到并使用。

在Linux内核官网www.kernel.org上,就提供rsync的下载方式,官方给出的地址是rsync://rsync.kernel.org/pub,可以根据这个地址找出你想下载的内核版本。例如:要找出linux-3.0.15版本的内核相关文件,可以在客户端执行如下命令:

1
2
3
4
5
6
7
8
9
10
11
12
[root@c7-test01 ~]# rsync --no-motd -r -v -f "+ */" -f "+ linux-3.0.15*" -f "- *" -m rsync://rsync.kernel.org/pub/
receiving file list ... done
drwxr-xr-x 4,096 2019/05/04 03:15:23 .
drwxr-xr-x 4,096 2014/11/12 05:50:10 linux
drwxr-xr-x 4,096 2019/03/12 23:06:47 linux/kernel
drwxr-xr-x 258,048 2019/05/23 13:52:08 linux/kernel/v3.x
-rw-r--r-- 76,803,806 2012/01/04 03:00:31 linux/kernel/v3.x/linux-3.0.15.tar.bz2
-rw-r--r-- 96,726,195 2012/01/04 03:00:31 linux/kernel/v3.x/linux-3.0.15.tar.gz
-rw-r--r-- 836 2012/01/04 03:00:31 linux/kernel/v3.x/linux-3.0.15.tar.sign
-rw-r--r-- 63,812,604 2012/01/04 03:00:31 linux/kernel/v3.x/linux-3.0.15.tar.xz
sent 58 bytes received 82,915 bytes 2,720.43 bytes/sec
total size is 237,343,441 speedup is 2,860.49

我们无需关注上面的规则代表什么意思,需要关注的重点是通过rsync可以向外提供文件列表并提供相应的下载。同样,我们还可以根据路径,将rsync daemon上的文件拉取到本地实现下载的功能。

rsync daemon是”rsync –daemon”或再加上其他一些选项启动的,它会读取配置文件,默认是/etc/rsyncd.conf,并默认监听在873端口上,当外界有客户端对此端口发起连接请求,通过这个网络套接字就可以完成连接,以后与该客户端通信的所有数据都通过该网络套接字传输。

rsync daemon的通信方式和传输通道与远程shell不同。远程shell连接的两端是通过管道完成通信和数据传输的,当连接到目标端时,将在目标端上根据远程shell进程fork出rsync进程使其成为rsync server。而rsync daemon是事先在server端上运行好的rsync后台进程(根据启动选项,也可以设置为非后台进程),它监听套接字等待client端的连接,连接建立后所有通信方式都是通过套接字完成的。

rsync中的server的概念从来就不代表是rsync daemon,server在rsync中只是一种通用称呼,只要不是发起rsync请求的client端,就是server端,可以认为rsync daemon是一种特殊的server,其实daemon更准确的称呼应该是service。

以下是rsync client连接rsync daemon时的命令语法:

1
2
3
4
Pull: rsync [OPTION...] [USER@]HOST::SRC... [DEST]
rsync [OPTION...] rsync://[USER@]HOST[:PORT]/SRC... [DEST]
Push: rsync [OPTION...] SRC... [USER@]HOST::DEST
rsync [OPTION...] SRC... rsync://[USER@]HOST[:PORT]/DEST

连接命令有两种类型,一种是rsync风格使用双冒号的”rsync user@host::src dest”,一种是url风格的”rsync://user@host:port/src dest”。对于rsync风格的连接命令,如果想要指定端口号,则需要使用选项”–port”。

上述语法中,其中daemon端的路径,如user@host::src,它的src代表的是模块名,而不是真的文件系统中的路径。关于rsync中的模块就是其配置文件中定义的各个功能。

daemon配置文件rsyncd.conf

默认”rsync –daemon”读取的配置文件为/etc/rsyncd.conf,有些版本的系统上可能该文件默认不存在,需要手动创建。rsyncd.conf默认配置内容如下:

在上述示例配置文件中,先定义了一些全局选项,然后定义了[ftp1],这个用中括号包围的”[ftp1]”就是rsync中所谓的模块,ftp1为模块ID,必须保证唯一,每个模块中必须定义一项”path”,path定义的是该模块代表的路径,此示例文件中,如果想请求ftp1模块,则在客户端使用”rsync user@host::ftp1”,这表示访问user@host上的/home/ftp目录,如果要访问/home/ftp目录下的子目录www,则”rsync user@host::ftp1/www”。

以下是我自用的配置项,也算是一个配置示例:

[

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
root@c7-server01 ~]# cat /etc/rsyncd.conf 

# 全局配置参数

port=888 # 指定rsync端口。默认873
uid = 0 # rsync服务的运行用户,默认是nobody,文件传输成功后属主将是这个uid
gid = 0 # rsync服务的运行组,默认是nobody,文件传输成功后属组将是这个gid
use chroot = no # rsync daemon在传输前是否切换到指定的path目录下,并将其监禁在内
max connections = 200 # 指定最大连接数量,0表示没有限制
timeout = 300 # 确保rsync服务器不会永远等待一个崩溃的客户端,0表示永远等待
motd file = /var/rsyncd/rsync.motd # 客户端连接过来显示的消息
pid file = /var/run/rsyncd.pid # 指定rsync daemon的pid文件
lock file = /var/run/rsync.lock # 指定锁文件
log file = /var/log/rsyncd.log # 指定rsync的日志文件,而不把日志发送给syslog
dont compress = *.gz *.tgz *.zip *.z *.Z *.rpm *.deb *.bz2 # 指定哪些文件不用进行压缩传输

# 指定模块,并设定模块配置参数,可以创建多个模块

[kksql] # 模块ID
path = /kksql/ # 指定该模块的路径,该参数必须指定。启动rsync服务前该目录必须存在。rsync请求访问模块本质就是访问该路径。
ignore errors # 忽略某些IO错误信息
read only = true # 指定该模块是否可读写,即能否上传文件,false表示可读写,true表示可读不可写。所有模块默认不可上传
write only = false # 指定该模式是否支持下载,设置为true表示客户端不能下载。所有模块默认可下载
list = false # 客户端请求显示模块列表时,该模块是否显示出来,设置为false则该模块为隐藏模块。默认true
hosts allow = 10.0.5.0/24 # 指定允许连接到该模块的机器,多个ip用空格隔开或者设置区间
hosts deny = 0.0.0.0/32 # 指定不允许连接到该模块的机器
auth users = rsync_backup # 指定连接到该模块的用户列表,只有列表里的用户才能连接到模块,用户名和对应密码保存在secrets file中,这里使用的不是系统用户,而是虚拟用户。不设置时,默认所有用户都能连接,但使用的是匿名连接
secrets file = /etc/rsyncd.passwd # 保存auth users用户列表的用户名和密码,每行包含一个username:passwd。由于"strict modes"默认为true,所以此文件要求非rsync daemon用户不可读写。只有启用了auth users该选项才有效。
#
[kkweb] # 以下定义的是第二个模块
path=/kkweb/
read only = false
ignore errors
comment = anyone can access

1)从客户端推到服务端时,文件的属主和属组是配置文件中指定的uid和gid。但是客户端从服务端拉的时候,文件的属主和属组是客户端正在操作rsync的用户身份,因为执行rsync程序的用户为当前用户。

2)auth users和secrets file这两行不是一定需要的,省略它们时将默认使用匿名连接。但是如果使用了它们,则secrets file的权限必须是600。客户端的密码文件也必须是600。

3)关于secrets file的权限,实际上并非一定是600,只要满足除了运行rsync daemon的用户可读即可。是否检查权限的设定是通过选项strict mode设置的,如果设置为false,则无需关注文件的权限。但默认是yes,即需要设置权限。

配置完后,再就是提供模块相关目录、身份验证文件等。

1
2
3
[root@c7-server01 ~]# useradd -r -s /sbin/nologin rsync
[root@c7-server01 ~]# mkdir /{kksql,kkweb}
[root@c7-server01 ~]# chown -R rsync.rsync /{kksql,kkweb}

提供模块kksql的身份验证文件,由于rsync daemon是以root身份运行的,所以要求身份验证文件对非root用户不可读写,所以设置为600权限。

1
2
[root@c7-server01 ~]# echo "rsync_backup:Oms_2600" >> /etc/rsyncd.passwd
[root@c7-server01 ~]# chmod 600 /etc/rsyncd.passwd

然后启动rsync daemon,启动方式很简单。如果是CentOS 7,则自带启动脚本:systemctl stard rsyncd。

1
2
3
[root@c7-server01 ~]# rsync --daemon
# 或
[root@c7-server01 ~]# systemctl enable rsyncd && systemctl start rsyncd

启动好rysnc daemon后,它就监听在指定的端口上,等待客户端的连接。由于上述示例中的模块kksql配置了身份验证功能,所以客户端连接时会询问密码。如果不想手动输入密码,则可以使用”–password-file”选项提供密码文件,密码文件中只有第一行才是传递的密码,其余所有的行都会被自动忽略。

在客户端上访问daemon上各模块的文件示例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
# rsync模式
[root@c7-test01 ~]# rsync --list-only --port 888 root@192.168.101.11::kkweb/
drwxr-xr-x 4,096 2019/06/03 17:51:49 .
-rw-r--r-- 0 2019/06/03 17:51:49 stu01.img
-rw-r--r-- 0 2019/06/03 17:51:49 stu02.img
-rw-r--r-- 0 2019/06/03 17:51:49 stu03.img
-rw-r--r-- 0 2019/06/03 17:51:49 stu04.img
-rw-r--r-- 0 2019/06/03 17:51:49 stu05.img
-rw-r--r-- 0 2019/06/03 17:51:49 stu06.img
-rw-r--r-- 0 2019/06/03 17:51:49 stu07.img
-rw-r--r-- 0 2019/06/03 17:51:49 stu08.img
-rw-r--r-- 0 2019/06/03 17:51:49 stu09.img
-rw-r--r-- 0 2019/06/03 17:51:49 stu10.img
[root@c7-test01 ~]# rsync --list-only --port 888 rsync_bakup@192.168.101.11::kkweb/
drwxr-xr-x 4,096 2019/06/03 17:51:49 .
-rw-r--r-- 0 2019/06/03 17:51:49 stu01.img
-rw-r--r-- 0 2019/06/03 17:51:49 stu02.img
-rw-r--r-- 0 2019/06/03 17:51:49 stu03.img
-rw-r--r-- 0 2019/06/03 17:51:49 stu04.img
-rw-r--r-- 0 2019/06/03 17:51:49 stu05.img
-rw-r--r-- 0 2019/06/03 17:51:49 stu06.img
-rw-r--r-- 0 2019/06/03 17:51:49 stu07.img
-rw-r--r-- 0 2019/06/03 17:51:49 stu08.img
-rw-r--r-- 0 2019/06/03 17:51:49 stu09.img
-rw-r--r-- 0 2019/06/03 17:51:49 stu10.img

# url模式
[root@c7-test01 ~]# rsync --list-only rsync://root@192.168.101.11:888/kkweb/
drwxr-xr-x 4,096 2019/06/03 17:51:49 .
-rw-r--r-- 0 2019/06/03 17:51:49 stu01.img
-rw-r--r-- 0 2019/06/03 17:51:49 stu02.img
-rw-r--r-- 0 2019/06/03 17:51:49 stu03.img
-rw-r--r-- 0 2019/06/03 17:51:49 stu04.img
-rw-r--r-- 0 2019/06/03 17:51:49 stu05.img
-rw-r--r-- 0 2019/06/03 17:51:49 stu06.img
-rw-r--r-- 0 2019/06/03 17:51:49 stu07.img
-rw-r--r-- 0 2019/06/03 17:51:49 stu08.img
-rw-r--r-- 0 2019/06/03 17:51:49 stu09.img
-rw-r--r-- 0 2019/06/03 17:51:49 stu10.img
[root@c7-test01 ~]# rsync --list-only rsync://rsync@192.168.101.11:888/kkweb/
drwxr-xr-x 4,096 2019/06/03 17:51:49 .
-rw-r--r-- 0 2019/06/03 17:51:49 stu01.img
-rw-r--r-- 0 2019/06/03 17:51:49 stu02.img
-rw-r--r-- 0 2019/06/03 17:51:49 stu03.img
-rw-r--r-- 0 2019/06/03 17:51:49 stu04.img
-rw-r--r-- 0 2019/06/03 17:51:49 stu05.img
-rw-r--r-- 0 2019/06/03 17:51:49 stu06.img
-rw-r--r-- 0 2019/06/03 17:51:49 stu07.img
-rw-r--r-- 0 2019/06/03 17:51:49 stu08.img
-rw-r--r-- 0 2019/06/03 17:51:49 stu09.img
-rw-r--r-- 0 2019/06/03 17:51:49 stu10.img

晕吧?下一篇我们介绍sersync,继续晕。。。嘿嘿

-------------本文结束感谢您的阅读-------------
坚持原创技术分享,您的支持将鼓励我继续创作!