Inotify+Rsync实现linux文件实时同步

 

一、inotify简介

inotify是Linux内核2.6.13 (June 18, 2005)版本新增的一个子系统(API),它提供了一种监控文件系统(基于inode的)事件的机制,可以监控文件系统的变化如文件修改、新增、删除等,
  并可以将相应的事件通知给应用程序。该机制由著名的桌面搜索引擎项目beagle引入用于替代此前具有类似功能但存在诸多缺陷  的dnotify。
  inotify既可以监控文件,也可以监控目录。当监控目录时,它可以同时监控目录及目录中的各子目录及文件的。此外,inotify 使用文件描述符作为接口,
  因而可以使用通常的文件I/O操作select、poll和epoll来监视文件系统的变化。

  inotify 可以监视的文件系统常见事件包括:
  IN_ACCESS:文件被访问
  IN_MODIFY:文件被修改
  IN_ATTRIB,文件属性被修改
  IN_CLOSE_WRITE,以可写方式打开的文件被关闭
  IN_CLOSE_NOWRITE,以不可写方式打开的文件被关闭
  IN_OPEN,文件被打开
  IN_MOVED_FROM,文件被移出监控的目录
  IN_MOVED_TO,文件被移入监控着的目录
  IN_CREATE,在监控的目录中新建文件或子目录
  IN_DELETE,文件或目录被删除
  IN_DELETE_SELF,自删除,即一个可执行文件在执行时删除自己
  IN_MOVE_SELF,自移动,即一个可执行文件在执行时移动自己
通过/proc接口中的如下参数设定inotify能够使用的内存大小:
  1、/proc/sys/fs/inotify/max_queue_events
  应用程序调用inotify时需要初始化inotify实例,并时会为其设定一个事件队列,此文件中的值则是用于设定此队列长度的上限;超出此上限的事件将会被丢弃;
  2、/proc/sys/fs/inotify/max_user_instances
  此文件中的数值用于设定每个用户ID(以ID标识的用户)可以创建的inotify实例数目的上限;
  3、/proc/sys/fs/inotify/max_user_watches
  此文件中的数值用于设定每个用户ID可以监控的文件或目录数目上限;

二、inotify-tools

inotify是一个API,需要通过开发应用程序进行调用,对于大多数用户来讲这有着许多不便,inotify-tools的出现弥补了这一不足。inotify-tools是一套组件,它包括一个C库和几个命令行工具,这些命令行工具可用于通过命令行或脚本对某文件系统的事件进行监控。它由Rohan McGovern开发,其项目网址为http://inotify-tools.sourceforge.net。
inotify-tools提供的两个命令行工具:
  
  inotifywait:通过inotify API等待被监控文件上的相应事件并返回监控结果,默认情况下,正常的结果返回至标准输出,诊断类的信息则返回至标准错误输出。它可以在监控到对应监控对象上指定的事件后退出,也可以进行持续性的监控。
  inotifywatch:通过inotify API收集被监控文件或目录的相关事件并输出统计信息。
inotifywait命令使用简介:
  inotifywait尤其适用于在脚本中等待某事件的发生,并可基于特定的事件执行相应操作。如将其用于脚本中监控某指定目录中的文件上的修改、新建、删除、属性信息的改变,而后使用rsync命令将某事件对应的文件同步至其它主机上。其常用选项如下:
  -m, --monitor:inotifywait的默认动作是在监控至指定文件的特定事件发生一次后就退出了,而使用此选项则可实现持续性的监控;
  -r, --recursive:递归监控指定目录下的所有文件,包括新建的文件或子目录;如果要监控的目录中文件数量巨大,则通常需要修改/proc/sys/fs/inotify/max_users_watchs内核参数,因为其默认值为8192。
  -e <event>, --event <event>:指定要监控的特定事件,默认是监控所有的事件;此处<event>包括access, modify, attrib, close_write, close_nowirte, close, open,       moved_to, moved_from, move, create, delete, delete_selt等;
  --timefmt <fmt>:当在--format选项中使用%T时,--timefrt选项则可以用来指定自定义的符合strftime规范的时间格式,此时间格式可用的格式符可以通过strftime的手册页获取;-      -timefrt后常用的参数是'%d/%m/%y %H:%M';
  --format <fmt>:自定义inotifywait的输出格式,如--format '%T %w %f';常用的格式符如下:
     %w:显示被监控文件的文件名;
     %f:如果发生某事件的对象是目录,则显示被监控目录的名字;默认显示为空串;
     %T:使用--timefmt选项中自定义的时间格式;
  例如,要监控/tmp/test目录及其内部所有文件上发生的create,delete,modify,close_write事件,则使用如下命令:
  # inotify -r --timefmt '%d/%m/%y %H:%M' --format '%T %w %f' -e create,delete,modify,close_write /tmp/test
  此命令在监控到某文件上第一次事件后就会退出,如果想一直监控,则需要为命令添加-m选项。
  在很多场景中都会用到将某主机上的某目录下的所有文件改变实时同步至另一主机上的指定位置,这也可以通过在脚本中使用inotifywait结合rsync命令来实现,比如如下脚本:
#!/bin/bash
  DESTHOST=172.16.100.6
  DESTHOSTDIR=/www/htdocs/
  SRCDIR=/www/htdocs/
  inotifywait -mr --timefmt '%d/%m/%y %H:%M' --format '%T %w %f' -e create,delete,modify,attrib  $SRCDIR | while read DATE TIME DIR FILE
  do
     $FILECHANGE=${DIR}${FILE}
     rsync -avze 'ssh' $SRCDIR root@${DESTHOST}:${DESTHOSTDIR} &>/dev/null && 
     echo "At ${TIME} on ${DATE}, file $FILECHANGE was backed up via rsync" >> /var/log/filesync.log
  done
需要注意的是,此脚本中的rsync是通过ssh加密后进行文件传输的,因此需要事先配置好相应的ssh能够基于密钥对用户进行认证,以免每一次文件同步都需要用户手动输入密码。
  当然,如果数据传输不需要加密,此处也可以通过在目录主机的启动rsyncd守护进程来实现。

三、配置rsyncd+inotify实现文件实时同步:

本案例实现监控原主机上指定目录中的所有文件变化,并将变化实时同步至目标主机的指定目录中;所用主机及相关目录如下:
  源主机:RHEL5.4(x86),172.16.100.1, 文件所在的目录为/www/htdocs;
  目标主机:RHEL5.4(x86),172.16.100.6, 文件所在的目录为/www/htdocs;
1、设定目标主机(本例为172.16.100.6)
  本案例中采用基于rsync守护进程的方式进行数据同步,其数据传输过程是明文方式,因此只适用于在特定的场景中应用。
1)安装相关软件:
  目标主机是接收别的主机发送来的文件的服务器,因此,其rsync需要以守护进程的方式工作。rsync服务通常基于超级守护进程xinetd管理的方式来实现,因此需要事先安装rysnc和   xinetd:
  # yum -y install rsync xinetd
2)为rsync提供配置文件/etc/rsyncd.conf,内容类似如下内容:
  # Section 1: Global settings
  uid = nobody
  gid = nobody
  use chroot = no
  max connections = 3
  strict modes = yes
  pid file = /var/run/rsyncd.pid
  log file = /var/log/rsyncd.log
  # Section 2:Directory to be synced
  [htdocs]
  path = /www/htdocs
  ignore errors = yes
  read only = no
  write only = no
  hosts allow = 172.16.0.0/16
  hosts deny = *
  list = false
  uid = root
  gid = root
  auth users = wwwuser
  secrets file = /etc/rsync.passwd
  其中的相关指令及其说明可以通过rsyncd.conf的手册而获取。而其访问控制功能也可基于xinetd进行,具体方法请参照xinetd.conf的手册页。
3)提供secrets file所指定的口令文件/etc/rsync.passwd,其内容类似如下:
  htdocsuser:passwOrdForhtdOcs
  其中冒号前的是用户名,冒号后的是对应用户的密码。此文件不能为其他任意用户可访问,因此可用如下命令修改:
  # chmod 600  /etc/rsync.passwd
4)配置服务可以开机启动:
  # chkconfig rsync on
  # chkconfig xinetd on
  # service xinetd start
默认情况下,rsyncd监听的端口为873/TCP,这可以通过如下命令查看:
  # netstat -tnlp | grep ":873"
  tcp        0      0 0.0.0.0:873                 0.0.0.0:*                   LISTEN      3653/xinetd
2、设定源主机
  1)安装相关软件

源主机需要实时监控指定目录中的所有文件上与文件改变相关的事件,并在事件发生时将改变的数据同步至目录主机,因此,源主机上需要确保内核支持inotify,并安装inotify-tools和rsync。

rsync的安装参照目标主机的中的方式进行即可。
  inotify-tools的安装可以基于源码编译的方式进行,也可以通过安装其rpm进行。
  ×××地址:http://github.com/downloads/rvoicilas/inotify-tools/inotify-tools-3.14.tar.gz
  rpm包下载页面:http://rpm.pbone.net/index.php3/stat/4/idpl/15265939/dir/redhat_el_5/com/inotify-tools-3.14-1.el5.i386.rpm.html
这里以编译源代码的方式演示安装过程:
  # tar xf inotify-tools-3.14.tar.gz
  # cd inotify-tools-3.14
  # ./configure
  # make
  # make install
  # echo "/usr/local/lib" > /etc/ld.so.conf.d/usr_local_lib.conf
  # ldconfig
2)为源主机的rsync提供通过htdocsuser用户同步文件至目标主机的口令文件
  # echo 'passwOrdForhtdOcs' > /etc/rsync.passwd
  # chmod 600 /etc/rsync.passwd
  注意,对于RHEL5.4自带的2.6.8版本的rsync来说,其客户端(即此处的源主机上的rsync)的口令文件中只能保存用户的口令,而不能类似目标主机上可同时指定用户名。
3)建立脚本/root/bin/htdocsync.sh,通过inotifywait监控目标文件上的相应事件,并在事件触发时启动同步过程:
  
  #!/bin/bash
  DESTHOST=172.16.100.6
  DESTHOSTDIR=/www/htdocs/
  SRCDIR=/www/htdocs/
  inotifywait -mr --timefmt '%d/%m/%y %H:%M' --format '%T %w %f' -e close_write,modify,delete,create,attrib $SRCDIR |  while read DATE TIME DIR FILE
  do
     FILECHANGE=${DIR}${FILE}
     rsync -avz --password-file=/etc/rsync.passwd $SRCDIR htdocsuser@${DESTHOST}::htdocs &>/dev/null && \
     echo "At ${TIME} on ${DATE}, file $FILECHANGE was backed up via rsync" >> /var/log/websync.log
  done
而后给此脚本执行权限,并执行即可:
  # chmod u+x /root/bin/htdocsync.sh
  # /root/bin/htdocsync.sh &
如果想让此功能可以在开机时自动启动,则可以通过如下方式进行:
  # echo '/root/bin/htdocsync.sh &' >> /etc/rc.d/rc.local