来自: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/ |
以下是这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-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