一、前言

无论使用什么操作系统下,都经常有同步文件的需求,不管发生在本地,还是发生在本地和远程主机之间。那么应该怎么做呢?

使用拷贝类的命令,本地使用cp命令,复制到远程主机使用scp这样的命令,保证复制到目标和源的一模一样。

但是,这种复制一般来说,只能判断文件在目标中是否存在,然后对存在的文件或目录可以选择覆盖或者不复制,覆盖能保证文件一致,不复制就不能保证一致,不存在的则创建这个文件或者目录。

每次同步如果都采用覆盖,即使只是少数文件发生部分变化,也是全部文件都覆盖,源和目标倒是保证一致了。但这种方式,会造成极大的网络带宽压力和源、目标磁盘IO的压力。

如果源中有1G数据怎么办,或者更多数据怎么办?

Linux下提供了一款工具rsync,它能够实现本地之间或者本地和远程之间的数据同步,它能通过分块滚动校验和算法和hash算法来计算文件的差异,源端负责计算出差异,将匹配块引用和差异序列发回目标端,由目标端重新组织生成新的文件。

它的特征是:

  • 分块提取特征码,比较差异

  • 对有差异的文件,进行增量复制

这样大大的节省了带宽和IO。

 

二、rsync的推拉模型

(一)服务模式

rsync的使用模型中,生成环境用的最多的还是跨主机间同步,也就是要使用服务模式。

rsync的服务模式

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

常用选项
-v, --verbose详细信息输出
-q, --quiet不显示非错误信息
-a, --archive归档模式
-r, --recursive递归目录
-z, --compress启用数据压缩
--delete删除目标目录中多余的文件
--progress显示进度
--password-file=FILE密码文件存放路径,注意这个文件的权限

 

(二)拉模型

拉模型指的是主动去服务器的指定端口获取数据

pullmode

通过rsync服务拉模型同步:

目标节点主动的连接到源节点后,双方比较文件差异,然后从源节点同步文件差异生成新文件以求保持一致。

目标节点需要定时去访问源节点,时间间隔不宜过于快,也不宜过慢。

而且比较差异,是遍历整个目录文件,是非常占资源的事情。

从以上分析可以看出,目标节点主动拉取数据是基于查询机制的,非常没有效率。

那么,解决问题的办法是使用一种机制,能够获取到源节点数据的变化之后,再启动同步,这将大大减少资源的占用。而Linux给我们提供了这种机制,就是inotify

inotify: Linux从2.6.13内核开始引入。通过inotify可以监控文件系统的添加、删除、修改、移动等各种细微事件,利用这个内核接口,第三方软件可以监控文件系统的各种变化,而inotify-tools就是其中一种工具。

 

(三)推模型

推模型指的是将数据主动推送至服务器指定端口

pushmode

推模型一样存在怎样推送、间隔多久推送的问题!

但是,推模型可以结合inotify来决定什么时候推送数据。

让源节点通过inotify探查到同步目录中文件发生了改变,然后源节点主动的将文件推至目标节点,这将大大提高效率。

这样,源节点使用inotify + rsync客户端,目标节点配置rsync的服务器。

下面就依照推模型的规划来完成本次实验。

 

三、inotify + rsync的推模型方案实验

本次实验使用的操作系统是CentOS 6.5。

# uname -r    
2.6.32-431.el6.x86_64

其确保内核的版本是2.6.13之上。

 

(一)目标节点配置

1、准备服务程序

rsync需要依赖超级守护进程xinetd,服务默认监听在TCP的873端口,客户端连接此端口,来推送数据或者拉取数据。

# yum -y install xinetd    
# yum -y install rsync     
[root@localhost ~]# sed -i 's/yes/no/' /etc/xinetd.d/rsync     
[root@localhost ~]# cat /etc/xinetd.d/rsync     
# default: off     
# description: The rsync server is a good addition to an ftp server, as it \     
#    allows crc checksumming etc.     
service rsync     
{     
    disable    = no     
    flags        = IPv6     
    socket_type     = stream     
    wait            = no     
    user            = root     
    server          = /usr/bin/rsync     
    server_args     = --daemon     
    log_on_failure  += USERID     
}
 
# mkdir /tmp/data/ –v

 

2、提供配置文件    

# vim /etc/rsyncd.conf
 
uid = nobody     
gid = nobody     
use chroot = no     
max connections = 10     
pid file = /var/run/rsyncd.pid     
log file = /var/log/rsyncd.log
 
[data]     
path = /tmp/data     
ignore errors = yes     
read only = true     
write only = no     
list = true
 
host allow = 192.168.60.0/24     
host deny = *
 
uid = root
 
gid = root
 
auth users = tiger, tom     
secrets file = /etc/rsyncd.secrets

 

3、准备用户认证文件


# vim /etc/rsyncd.secrets     
tiger:tigeradm     
tom:tomadm
 
# chmod 600 /etc/rsyncd.secrets

 

说明

write only如果是yes(true)就不许下载,也就是说,如果是no(false)就允许下载,但要检查是否有权限。

auth users 用户认证

secrets file 指明用户认证所使用的用户名和密码所存储的文件。注意这个文件的权限要400或者600

 

4、启动服务

启动超级守护进程,让它代为监听TCP 873端口。

# service xinetd start     
Starting xinetd:                        [  OK  ]     
# ss -tnlp | grep :873     
LISTEN     0      64           :::873         :::*      users:(("xinetd",1443,5))

 

 

(二)源节点配置

1、安装rsync

# yum -y install rsync     
# mkdir /data/     
# rsync -vv rsync://tom@192.168.60.144/data/* /data/     
opening tcp connection to 192.168.60.144 port 873     
sending daemon args: --server --sender -vve.Ls . "data/*"     
Password:     
delta-transmission enabled     
a.txt     
test.txt
 
sent 91 bytes  received 189 bytes  20.74 bytes/sec     
total size is 0  speedup is 0.00

 

2、安装inotify-tools

# tar xf inotify-tools-3.13.tar.gz     
# cd inotify-tools-3.13     
# ./configure     
# make     
# make install

 

3、inotifywait

inotify-tools提供了有用的工具inotifywait。

常用选项
-r监视目录及其子目录
-m始终处于事件监听状态不退出
-q --quiet静默,只打印时间
-e --event指定监听的事件,如果不指定,监听所有事件
--exclude使用扩展正则表达式描述的排除文件
--excludei使用扩展正则表达式描述的排除文件,忽略大小写
--format指定打印的格式,类似printf。          
%w 发生事件的目录名称          
%f 发生事件的文件名称          
%e 发生的事件          
%Xe 使用X分隔发生的事件          
%T --timefmt选项指定的时间格式
--timefmt使用和strftime兼容的格式的时间格式

 

事件
access文件或目录内容被读取
modify文件或目录内容被写入
attrib文件或目录属性改变,即元数据被修改
close_write在可写模式打开后关闭
close_nowrite在只读模式打开后关闭
close不管是否读写模式,只要是关闭
open打开
moved_to文件或目录移动至监控目录
moved_from文件或目录从监控目录移动走
move只要监控目录有移动事件
create在监控目录有文件或目录被创建
delete在监控目录有文件或目录被删除
delete_self监控的目录和文件本身被删除
unmount包含文件或目录的文件系统卸载

 

[ 事件分析 ]

使用下面的命令,来分析一下各种操作产生的事件是什么,从而分析出哪些事件是需要监控的。

# inotifywait -rm --format "%T %w %f %e" --timefmt "%Y-%m-%d %H:%M:%S" --excludei ".*\.(swp|swx|swpx)$" /data/

 

[root@localhost /]# cd /data/    
无事件

 

[root@localhost data]# ls    
2014-08-31 06:32:55 /data/  OPEN,ISDIR    
2014-08-31 06:32:55 /data/  CLOSE_NOWRITE,CLOSE,ISDIR

事件:open close_nowrite close

 

[root@localhost data]# touch test    
2014-08-31 06:35:54 /data/ test CREATE    
2014-08-31 06:35:54 /data/ test OPEN    
2014-08-31 06:35:54 /data/ test ATTRIB    
2014-08-31 06:35:54 /data/ test CLOSE_WRITE,CLOSE

事件:create open attirb close_write close

 

[root@localhost data]# mkdir content    
2014-08-31 06:36:58 /data/ content CREATE,ISDIR    
2014-08-31 06:36:58 /data/ content OPEN,ISDIR    
2014-08-31 06:36:58 /data/ content CLOSE_NOWRITE,CLOSE,ISDIR

事件:create open close_nowrite close

 

[root@localhost data]# ls content/    
2014-08-31 06:40:10 /data/ content OPEN,ISDIR    
2014-08-31 06:40:10 /data/content/  OPEN,ISDIR    
2014-08-31 06:40:10 /data/ content CLOSE_NOWRITE,CLOSE,ISDIR    
2014-08-31 06:40:10 /data/content/  CLOSE_NOWRITE,CLOSE,ISDIR

事件:open close_nowrite close

 

[root@localhost data]# cp /etc/issue content/    
2014-08-31 06:41:23 /data/  OPEN,ISDIR    
2014-08-31 06:41:23 /data/  CLOSE_NOWRITE,CLOSE,ISDIR    
2014-08-31 06:41:25 /data/content/ issue CREATE    
2014-08-31 06:41:25 /data/content/ issue OPEN    
2014-08-31 06:41:25 /data/content/ issue MODIFY    
2014-08-31 06:41:25 /data/content/ issue CLOSE_WRITE,CLOSE

事件:open close_nowrite close create open modify

 

[root@localhost data]# cat content/issue    
2014-08-31 06:44:09 /data/content/ issue OPEN    
2014-08-31 06:44:09 /data/content/ issue ACCESS    
2014-08-31 06:44:09 /data/content/ issue CLOSE_NOWRITE,CLOSE

事件:open access close_nowrite close

 

[root@localhost data]# nano testfile    
2014-08-31 07:50:35 /data/ testfile CREATE    
2014-08-31 07:50:35 /data/ testfile OPEN    
2014-08-31 07:50:35 /data/ testfile MODIFY    
2014-08-31 07:50:35 /data/ testfile CLOSE_WRITE,CLOSE

事件:create open modify close_write close

 

[root@localhost data]# vim content/issue    
2014-08-31 06:51:03 /data/  OPEN,ISDIR    
2014-08-31 06:51:03 /data/  CLOSE_NOWRITE,CLOSE,ISDIR    
2014-08-31 06:51:03 /data/  OPEN,ISDIR    
……    
2014-08-31 06:51:03 /data/content/ issue OPEN    
2014-08-31 06:51:03 /data/content/ .issue.swpx CREATE    
2014-08-31 06:51:03 /data/content/ .issue.swpx OPEN    
2014-08-31 06:51:03 /data/content/ .issue.swpx CLOSE_WRITE,CLOSE    
2014-08-31 06:51:03 /data/content/ .issue.swpx DELETE    
2014-08-31 06:51:03 /data/  OPEN,ISDIR    
2014-08-31 06:51:03 /data/  CLOSE_NOWRITE,CLOSE,ISDIR    
2014-08-31 06:51:03 /data/  OPEN,ISDIR    
2014-08-31 06:51:03 /data/  CLOSE_NOWRITE,CLOSE,ISDIR    
2014-08-31 06:51:03 /data/content/ issue CLOSE_NOWRITE,CLOSE    
2014-08-31 06:51:03 /data/content/ issue OPEN    
2014-08-31 06:51:03 /data/content/ issue ACCESS    
2014-08-31 06:51:03 /data/content/ issue CLOSE_NOWRITE,CLOSE    
2014-08-31 06:51:03 /data/  OPEN,ISDIR    
2014-08-31 06:51:03 /data/  CLOSE_NOWRITE,CLOSE,ISDIR    
2014-08-31 06:51:12 /data/  OPEN,ISDIR    
2014-08-31 06:51:12 /data/  CLOSE_NOWRITE,CLOSE,ISDIR    
2014-08-31 06:51:12 /data/content/ 4913 CREATE    
2014-08-31 06:51:12 /data/content/ 4913 OPEN    
2014-08-31 06:51:12 /data/content/ 4913 ATTRIB    
2014-08-31 06:51:12 /data/content/ 4913 CLOSE_WRITE,CLOSE    
2014-08-31 06:51:12 /data/content/ 4913 DELETE    
2014-08-31 06:51:12 /data/content/ issue MOVED_FROM    
2014-08-31 06:51:12 /data/content/ issue~ MOVED_TO    
2014-08-31 06:51:12 /data/content/ issue CREATE    
2014-08-31 06:51:12 /data/content/ issue OPEN    
2014-08-31 06:51:12 /data/content/ issue MODIFY    
2014-08-31 06:51:12 /data/content/ issue CLOSE_WRITE,CLOSE    
2014-08-31 06:51:12 /data/content/ issue ATTRIB    
2014-08-31 06:51:12 /data/content/ issue ATTRIB    
2014-08-31 06:51:12 /data/content/ issue~ DELETE    
2014-08-31 06:51:12 /data/  OPEN,ISDIR    
2014-08-31 06:51:12 /data/  CLOSE_NOWRITE,CLOSE,ISDIR

事件:create open attrib delete modify moved_from moved_to close_write close

下面是使用vim打开,无修改关闭的日志

2014-08-31 06:46:51 /data/content/ issue CLOSE_NOWRITE,CLOSE    
2014-08-31 06:46:51 /data/content/ issue OPEN    
2014-08-31 06:46:51 /data/content/ issue ACCESS    
2014-08-31 06:46:51 /data/content/ issue CLOSE_NOWRITE,CLOSE

可以看出关键的事件是close_nowrite

 

[root@localhost data]# mv content/issue /tmp/    
2014-08-31 06:52:48 /data/content/ issue MOVED_FROM    
[root@localhost data]# mv /tmp/issue /data/content/    
2014-08-31 06:54:55 /data/content/ issue MOVED_TO

事件:moved_from moved_to

 

[root@localhost data]# rm /data/content/issue    
2014-08-31 06:58:25 /data/content/ issue DELETE

[root@localhost data]# rm -rf /data/content    
2014-08-31 06:59:53 /data/ content OPEN,ISDIR    
2014-08-31 06:59:53 /data/content/  OPEN,ISDIR    
2014-08-31 06:59:53 /data/content/ t1.txt DELETE    
2014-08-31 06:59:53 /data/ content DELETE,ISDIR    
2014-08-31 06:59:53 /data/ content CLOSE_NOWRITE,CLOSE,ISDIR    
2014-08-31 06:59:53 /data/content/  CLOSE_NOWRITE,CLOSE,ISDIR    
2014-08-31 06:59:53 /data/content/  DELETE_SELF    
2014-08-31 06:59:53 /data/content/  IGNORED

事件:open delete delete_self close_nowrite close

 

[root@localhost data]# chmod 400 testfile

2014-08-31 08:22:53 /data/ testfile ATTRIB

事件:attrib

 

由此可见,一个监控中的文件夹内容改变的主要事件有:create delete attrib close_write。

选取create是因为,目录的创建,不会发生close_write事件。

文件的创建,create事件产生,保存退出,会产生close_write事件。文件的修改,modify事件产生,保存退出,会产生close_write事件。所以选取close_write。

修改一个文件属性,只会产生attrib事件,所以要选取它。

文件或目录的删除都会产生delete事件。

 

4、部署inotify监控

创建访问目标节点的访问密码文件

# echo "tomadm" > /etc/rsync.accesspasswd     
# cat /etc/rsync.accesspasswd     
tomadm
 
# chmod 400 /etc/rsync.accesspasswd

 

5、推送测试

# rsync -avzr --delete --progress --password-file=/etc/rsync.accesspasswd /data/* rsync://tom@192.168.60.144/data/     
sending incremental file list     
ERROR: module is read only     
rsync error: syntax or usage error (code 1) at main.c(866) [receiver=3.0.6]     
rsync: read error: Connection reset by peer (104)     
rsync error: error in rsync protocol data stream (code 12) at io.c(759) [sender=3.0.6]

修改目标节点的配置文件,修改为可写,但不可下载。然后再次测试

# sed -i -e 's/read only.*/read only = no/' -e 's/write only.*/write only = yes/' /etc/rsyncd.conf
# rsync -avzr --delete --progress --password-file=/etc/rsync.accesspasswd /data/* rsync://tom@192.168.60.144/data/     
sending incremental file list     
apx.txt     
           0 100%    0.00kB/s    0:00:00 (xfer#1, to-check=3/5)     
issue     
          68 100%    0.00kB/s    0:00:00 (xfer#2, to-check=2/5)     
test.txt     
           0 100%    0.00kB/s    0:00:00 (xfer#3, to-check=1/5)     
ttt/
 
sent 283 bytes  received 69 bytes  33.52 bytes/sec     
total size is 68  speedup is 0.19

 

6、提供后台运行脚本

提供脚本/usr/local/scripts/inotesync.sh,内容如下

#!/bin/bash     
prog=inotesync     
dir=/data/     
user=tom     
romote=192.168.60.144     
logfile=/var/log/${prog}.log
 
{     
        /usr/local/bin/inotifywait -rmq -e create,attrib,delete,close_write --format "%T %e %w%f" --timefmt "%Y-%m-%d %H:%M:%S" $dir | while read line     
        do     
                /usr/bin/rsync -avzr --delete --progress --password-file=/etc/rsync.accesspasswd $dir rsync://$user@$romote$dir     
        done     
} >> $logfile &
 
 
 
 
# chmod +x /usr/local/scripts/inotesync.sh
 
# echo "/usr/local/scripts/inotesync.sh" >> /etc/rc.local

重启测试成功。

 

脚本说明:

1、在rc.local尾部追加脚本路径,使其启动时运行

2、inotifywait、rsync使用绝对路径,否则脚本将不能正常运行,因为此时环境变量PATH还没有起作用

3、为了排错或者查看信息,使用一个花括号将语句块套起来,这样语句块内的所有的日志输出重定向到日志文件中

4、使用&符号,让命令后台执行

 

至此,使用inotify+rsync的推模型实验完成。

 

四、总结

inotify+rsync的推模型,很好的解决了拉模型中的几个问题,做到了有变化再做同步。但是使用inotify来监控文件夹,只是在监控的事件发生的时候,触发了一个事件,而每触发一个事件,rsync都要遍历比较所有文件和目录。当目录中文件非常多的时候效率也不高。

可以考虑从inotifywait的输出信息中,提取被创建、改变的文件,推送到目标节点。

 

参考资料

http://en.wikipedia.org/wiki/Rsync

http://zh.wikipedia.org/zh/Inotify

http://tutorials.jenkov.com/rsync/index.html

http://rsync.samba.org/tech_report/tech_report.html

http://en.wikipedia.org/wiki/Rolling_hash

http://blog.csdn.net/liuaigui/article/details/5793706

http://www.ibm.com/developerworks/cn/linux/l-inotify/

http://www.cnblogs.com/jingzhishen/p/3738637.html