inotify Rsync基本应用说明

发布时间 2023-11-28 10:30:31作者: redrobot

来自:https://www.xjimmy.com/inotify_rsync.html

 

一、inotify基础知识  

inotify是啥东东?

inotify是Linux内核提供的一种对文件系统的监控机制,而机制是需要我们去实现了,目前实现的有两种方案:inotify本身和sersync。

这两种方案也可以说是两款工具,今天重点介绍inotify这款工具如果实现inotify的机制。

使用inotify的前提条件

请确保内核版本高于2.6.13,且在/proc/sys/fs/inotify目录下有以下三个文件,这表示系统支持inotify监控。

1
2
[root@jimmy ~]# uname -r
[root@jimmy ~]# ll /proc/sys/fs/inotify/

inotify确认结果

以下是这3个文件分别对应的内核参数的意义。

(1)max_queued_events:调用inotify_init时分配到inotify instance中可排队的event数的最大值,超出值时的事件被丢弃,但会触发队列溢出Q_OVERFLOW事件。

(2)max_user_instances:每一个real user可创建的inotify instances数量的上限。

(3)max_user_watches:每个inotify实例相关联的watches的上限,即每个inotify实例可监控的最大目录、文件数量。如果监控的文件数目巨大,需要根据情况适当增加此值,如

1
[root@jimmy ~]#echo 30000000 > /proc/sys/fs/inotify/max_user_watches

  二、Inotify工具安装  

inotify由inotify-tools包提供。epel源上提供了inotify-tools工具,可以直接在线安装。

1
[root@jimmy ~]# yum -y install inotify-tools

inotify安装

inotify-tools工具只提供了两个命令。

1
2
3
[root@jimmy ~]# rpm -ql inotify-tools |grep bin/
/usr/bin/inotifywait
/usr/bin/inotifywatch

inotifywait:实现监控(watch)文件的功能,该命令是inotify的核心命令,我们用inotify,其实就是用inotifywait这个命令。

inotifywatch:用于收集文件系统的统计数据,例如发生了多少次inotify事件,某文件被访问了多少次等等,一般用不上。

  三、inotifywait命令讲解  

inotifywait命令的常用选项说明:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
-m:表示始终监控,否则应该是监控到了一次就退出监控了
-r:递归监控,监控目录中的任何文件,包括子目录。递归监控可能会超出max_user_watches的值,需要适当调整该值
-e:指定监控的事件。一般监控的就delete、create、attrib、modify、close_write
 
@<file>:如果是对目录进行递归监控,则该选项用于排除递归目录中不被监控的文件。file是相对路径还是绝对路径由监控目录是相对还是绝对来决定
-q:--quiet的意思,静默监控,这样就不会输出一些无关的信息
 
--exclude <pattern> :通过模式匹配来指定不被监控的文件,区分大小写
--excludei <pattern>:通过模式匹配来指定不被监控的文件,不区分大小写
 
--timefmt:监控到事件触发后,输出的时间格式,可指定可不指定该选项,一般设置为[--timefmt '%Y/%m/%d %H:%M:%S']
--format:用户自定义的输出格式,如[--format '%w%f %e%T']
    %w:产生事件的监控路径,不一定就是发生事件的具体文件,例如递归监控一个目录,该目录下的某文件产生事件,将输出该目录而非其内具体的文件  
    %f:如果监控的是一个目录,则输出产生事件的具体文件名。其他所有情况都输出空字符串
    %e:产生的事件名称  
    %T:以"--timefmt"定义的时间格式输出当前时间,要求同时定义"--timefmt"

inotifywait 可监控的事件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
access:文件被访问
modify:文件被写入
attrib:元数据被修改。包括权限、时间戳、扩展属性等等
 
close_write:打开的文件被关闭,是为了写文件而打开文件,之后被关闭的事件
close_nowrite:read only模式下文件被关闭,即只能是为了读取而打开文件,读取结束后关闭文件的事件
close:是close_write和close_nowrite的结合,无论是何种方式打开文件,只要关闭都属于该事件
open:文件被打开
 
moved_to:向监控目录下移入了文件或目录,也可以是监控目录内部的移动
moved_from:将监控目录下文件或目录移动到其他地方,也可以是在监控目录内部的移动
move:是moved_to和moved_from的结合
moved_self:被监控的文件或目录发生了移动,移动结束后将不再监控此文件或目录
 
create:在被监控的目录中创建了文件或目录
delete:删除了被监控目录中的某文件或目录
delete_self:被监控的文件或目录被删除,删除之后不再监控此文件或目录umount:挂载在被监控目录上的文件系统被umount,umount后不再监控此目录
isdir :监控目录相关操作

  四、inotify监控事件说明  

前面提到了非常多的监控事件,但是实际应用中需要设置哪几个监控事件呢?

答案是:close_write、moved_to、moved_from、delete和isdir

其实,对文件的操作动作都会涉及close事件,且大多数情况都是伴随着close_write事件的。所以,大多数情况下在定义监控事件时,其实并不真的需要监控open、modify、close事件。特别是close,只需监控它的分支事件close_write和close_nowrite即可。由于一般情况下inotify都是为了监控文件的增删改,不会监控它的访问,所以一般只需监控close_write即可。

由于很多时候定义触发事件后的操作都是根据文件来判断的,例如a文件被监控到了变化(不管是什么变化),就立即执行操作A,又由于对文件的一个操作行为往往会触发多个事件,这样很可能会因为多个事件被触发而重复执行操作A。

综合以上考虑,建议对监控对象的close_write、moved_to、moved_from、delete和isdir(主要是create,isdir,但无法定义这两个事件的整体,所以仅监控isdir)事件定义对应的操作,因为它们互不重复。如有需要,可以将它们分开定义,再添加需要监控的其他事件。

inotifywait命令常见的使用方法

1
[root@jimmy ~]# inotifywait -mrq -e delete,close_write,moved_to,moved_from,isdir --format '%w%f:%e' /www

 

  五、inotify的缺陷及Bug  

inotify+rsync的缺陷:多次触发rsync,造成资源和网络带宽浪费

当我们给监控目录添加文件的时候,inotify会触发事件,就算经过上的简化,已经尽少量的触发事件了,但是如果往一个目录拷贝多个文件的时候,仍然是每个文件触发一个事件,而一个事件就是代表着一次rsync数据同步,如果多个文件,就会多次rsync,而我们如果同步的是目录的话,其实,一次rsync就足够了。

inotify的bug:经常会随机性地遗漏某些文件

当向监控目录下拷贝复杂层次目录(多层次目录中包含文件),或者向其中拷贝大量文件时,inotify经常会随机性地遗漏某些文件。遗漏某些文件的意思是,不会对某些文件触发相关事件,不会触发相关事件的后果就是不会调用rsync进行同步。

但是需要说明的是,只有拷贝多层次包括多文件的目录时才会出现此bug,拷贝单个文件或简单无子目录的时候不会出现此bug。对于inotify+rsync来说,由于触发事件后常使用rsync同步整个目录而非单个文件,所以这个bug对rsync来说并不算严重。

解决方案:设计inotify+rsync脚本时,有以下几个目标应该尽量纳入考虑或达到

(1)每个文件都尽量少地产生监控事件,但又不能遗漏事件。

(2)让rsync同步目录,而不是同步产生事件的单个文件。

(3)一次性操作同步目录下的多个文件会产生多个事件,导致多次触发rsync。如果能让这一批操作只触发一次rsync,则会大幅降低资源的消耗。

(4)rsync同步目录时,考虑好是否要排除某些文件,是否要加上"–delete"选项等。

(5)为了性能,可以考虑对子目录、对不同事件单独设计inotify+rsync脚本。

  六、inotify+rsync的最佳实现案例  

inotifywait一般不会单独使用,而是利用shell脚本,配合rsync进行数据的实时同步。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
[root@jimmy ~]# cat ~/inotify.sh
#!/bin/bash
  
watch_dir=/www
rsync_dir=/tmp
push_to=172.16.10.5
 
# First to do is initial sync
rsync -az --delete --exclude="*.swp" --exclude="*.swx" $watch_dir $push_to:${rsync_dir}
 
inotifywait -mrq -e delete,close_write,moved_to,moved_from,isdir --timefmt '%Y-%m-%d %H:%M:%S' --format '%w%f:%e:%T' --exclude=".*.swp" $watch_dir >>/tmp/inotifywait.log & 
while true;do
     if [ -s "/tmp/inotifywait.log" ];then
        grep -i -E "delete|moved_from" /tmp/inotifywait.log >> /tmp/inotify_away.log
        rsync -az --delete --exclude="*.swp" --exclude="*.swx" $watch_dir $push_to:${rsync_dir}
        if [ $? -ne 0 ];then
            echo "$watch_dir sync to $push_to failed at `date +"%F %T"`,please check it by manual" >> /tmp/inotifyerror.log
            cat /dev/null /tmp/inotifywait.log
            rsync -az --delete --exclude="*.swp" --exclude="*.swx" $watch_dir $push_to:${rsync_dir}    
        else
            sleep 1
    fi
done

脚本文件说明:

(1)脚本流程是人的实现方法是将inotifywait触发的事件记录到文件/etc/inotifywait.log中,然后在死循环中判断该文件,如果该文件不为空则调用一次rsync进行同步,同步完后立即清空inotifywait.log文件,防止重复调用rsync。

(2)但需要考虑一种情况,清空inotifywait.log文件的瞬间,又触发了一个事件,那么可能会导致这个事件被遗漏,所以在清空该文件后应该再调用一次rsync进行同步,这也变相地实现了失败重传的错误处理功能。

(3)如果没有监控到事件,inotifywait.log将是空文件,此时循环将睡眠1秒钟,所以该脚本并不是百分百的实时,但1秒钟的误差对于cpu消耗来说是很值得的。

(4)脚本中inotifywait命令中的后台符号"&"绝不能少,否则脚本将一直处于inotifywait命令阶段,不会进入到下一步的循环阶段。

(5)脚本示例中,过滤了两种临时文件(.swp和.swx),这是编辑文件时,产生的临时文件,而这种临时文件是不应该被监控,更不能被同步到远程目标主机的文件。

 

    本文是参考‘骏马金龙’大神的文章进行精简,加了一些个人在学习过程中的理解内容,然后整理成更适合自己的阅读方式,毕竟这位大神讲解的太基础和原理性了,而实际用到更多的应该就是本文总结的重点内容。
    下面贴出他这篇文章,一个更加强大和全面的inotify工具的用法,供各位学习:inotify+rsync详细说明和sersync