写在前面的碎碎念

上次不小心把网站数据清空之前就想用宝塔的自动备份功能做网站备份,然后送到其他的网盘中持久保存,但是因为学业紧张(自己比较懒...)的原因,迟迟没有实施,出现了上次的事故之后我痛定思痛,决定做好补救措施,就有了现在这篇水文。主要思路是利用宝塔定期对网站和数据库进行备份,inotify监控宝塔备份的文件夹,rsync进行同步。

一、首先安装inotify

这里使用的是CentOS 7,其他系统请自行百度。
yum install -y inotify-tools 

关键参数

ll /proc/sys/fs/inotify
在proc/sys/fs/inotify目录下的三个文件,对inotify机制有一定的限制
max_user_watches:设置inotifywait或inotifywatch命令可以监视的文件数量(单进程)
max_user_instances:设置每个用户可以运行的inotifywait或inotifywatch命令的进程数。
max_queued_events:设置inotify实例事件(event)队列可容纳的事件数量。
对于网站的备份默认值就没问题的,不需要修改。

二、测试inotifywait

先看下inotifywait在哪个位置,我看了很多网上的教程说的位置和我这边不一样,所以还是先检测一下为好。
找到路径以后使用命令测试inotifywait是否能够正常工作
[root@abc ~]# which inotifywait
/usr/bin/inotifywait

[root@abc ~]# /usr/bin/inotifywait -mrq --format '%Xe %w%f' -e modify,create,delete,attrib /root/ &
执行上面命令,是让inotifywait监听/root/目录,当监听到有发生modify,create,delete,attrib等事件发生时,按%Xe %w%f的格式输出。
在/root/目录touch几个文件
[root@cloud ~]# /usr/bin/inotifywait -mrq --format '%Xe %w%f' -e modify,create,delete,attrib /root/ &
[1] 4057
[root@cloud ~]# touch /root/{1..5}.txt
ATTRIB /root/1.txt
CREATE /root/2.txt
ATTRIB /root/2.txt
CREATE /root/3.txt
ATTRIB /root/3.txt
CREATE /root/4.txt
ATTRIB /root/4.txt
CREATE /root/5.txt
ATTRIB /root/5.txt
看到以上输出就证明inotifywait工作正常了。

三、用Rsync进行同步文件

Rsync获取inotifywait监控到的文件列表来做指定的文件同步,而不是每次都由rsync做全目录扫描来判断文件是否存在差异。当然这样也会有遗漏的可能,后面会讲到解决方案。
新建一个脚本web_backup.sh,把下面的代码粘贴进去。
[root@abc ~]# touch web_backup.sh && chmod +x web_backup.sh && vi web_backup.sh
#!/bin/bash
src=/te/t1                #需要同步的源路径,末尾不带/表示的是整个目录包括目录本身。
des=/root/gdrive                   #谷歌云盘挂载的目录
cd ${src}                            
# 此方法中,由于rsync同步的特性,这里必须要先cd到源目录,inotify再监听 ./ 才能rsync同步后目录结构一致。
/usr/bin/inotifywait -mrq --format  '%Xe %w%f' -e modify,create,delete,attrib,close_write,move ./ | while read file
# 把监控到有发生更改的"文件路径列表"循环
do
        INO_EVENT=$(echo $file | awk '{print $1}')      # 把inotify输出切割 把事件类型部分赋值给INO_EVENT
        INO_FILE=$(echo $file | awk '{print $2}')       # 把inotify输出切割 把文件路径部分赋值给INO_FILE
        echo "-------------------------------$(date)------------------------------------"
        echo $file
        #增加、修改、写入完成、移动进事件
        #增、改放在同一个判断,因为他们都肯定是针对文件的操作,即使是新建目录,要同步的也只是一个空目录,不会影响速度。
        if [[ $INO_EVENT =~ 'CREATE' ]] || [[ $INO_EVENT =~ 'MODIFY' ]] || [[ $INO_EVENT =~ 'CLOSE_WRITE' ]] || [[ $INO_EVENT =~ 'MOVED_TO' ]]         # 判断事件类型
        then
                echo 'CREATE or MODIFY or CLOSE_WRITE or MOVED_TO'

# INO_FILE变量代表路径哦  -c校验文件内容
                                rsync -avzcR $(dirname ${INO_FILE}) ${des} && rsync -avzcR $(dirname ${INO_FILE}) ${des}
#仔细看上面的rsync同步命令 源是用了$(dirname ${INO_FILE})变量 即每次只针对性的同步发生改变的文件的目录(只同步目标文件的方法在生产环境的某些极端环境下会漏文件 现在可以在不漏文件下也有不错的速度 做到平衡)
#然后用-R参数把源的目录结构递归到目标后面 保证目录结构一致性
#利用宝塔面板的定时任务每天备份网站,本地磁盘空间有限设置只保留3分副本。
#但是挂载的谷歌团队盘无限空间,并且想要保存很多份副本,所以不需要删除功能,我经常折腾自己的网站,指不定什么时候就后悔了。有需要的可以把下面的注释取消。
#        fi
        #删除、移动出事件
#        if [[ $INO_EVENT =~ 'DELETE' ]] || [[ $INO_EVENT =~ 'MOVED_FROM' ]]
#        then
#                echo 'DELETE or MOVED_FROM'
#                rsync -avzR --delete $(dirname ${INO_FILE}) ${des} && rsync -avzR --delete $(dirname ${INO_FILE}) ${des}

#看rsync命令 如果直接同步已删除的路径${INO_FILE}会报no such or directory错误 所以这里同步的源是被删文件或目录的上一级路径
#并加上--delete来删除目标上有而源中没有的文件,这里不能做到指定文件删除,如果删除的路径越靠近根,则同步的目录越多,同步删除的操作就越花时间。

        fi
        #修改属性事件 指 touch chgrp chmod chown等操作
        if [[ $INO_EVENT =~ 'ATTRIB' ]]
        then
                echo 'ATTRIB'
                if [ ! -d "$INO_FILE" ]
# 如果修改属性的是目录 则不同步,因为同步目录会发生递归扫描,等此目录下的文件发生同步时,rsync会顺带更新此目录。
                then
                        rsync -avzcR  $(dirname ${INO_FILE}) ${des} && rsync -avzcR  $(dirname ${INO_FILE}) ${des}
                fi
        fi
done

四、后台运行脚本

[root@abc ~]# nohup /root/web_backup.sh >> /root/inotify_flume.log &
查看脚本是否后台运行
[root@abc ~]# jobs
[1]+  Running                 nohup /root/web_backup.sh >> /root/inotify_flume.log &
手动触发下同步,查看一下log文件,显示有文件在传输就证明配置正确了。
[root@abc ~]# touch /te/t1/1 && rm -fr /te/t1/1
[root@abc ~]# tail inotify_flume.log
./
database/

五、问题

如果在使用过程中出现

1、报错

/usr/local/bin/inotifywait: error while loading shared libraries: libinotifytools.so.0: cannot open shared object file: No such file or directory

解决方案:1

32位系统:

ln -s /usr/local/lib/libinotifytools.so.0 /usr/lib/libinotifytools.so.0

64位系统:

ln -s /usr/local/lib/libinotifytools.so.0 /usr/lib64/libinotifytools.so.0

解决方案:2

按照上面提到的方法检查inotifytools是否正确安装,安装位置是否和脚本里的一致。

2、inotify没有运行的时候文件发生改变,导致文件没有同步。

因为inotify只在启动时会监控目录,他没有启动期间的文件发生更改,他是不知道的,所以这里每2个小时做1次全量同步,防止各种意外遗漏。

解决方案:

添加计划任务,每两小时全量同步一下。
利用crontab -l 加 crontab file 两个命令实现自动添加
crontab -l > conf && echo "* */2 * * * rsync -avz /te/t1 /root/gdrive && rsync -avz /te/t1 /root/gdrive" >> conf && crontab conf && rm -f conf
由于crontab file会覆盖原有定时任务,所以使用 crontab -l 先导出原有任务到临时文件 “conf” 再追加新定时任务

优点:不限用户,任何有crontab权限的用户都能执行

缺点:稍微复杂,但是我都写好了,复制粘贴难度应该不大。

六、设置脚本开机自动启动

先将rc.local增加可执行权限,再修改/etc/rc.d/rc.local。
[root@abc rc.d]# chmod +x /etc/rc.d/rc.local && vi /etc/rc.d/rc.local
在最后一行加入nohup /root/web_backup.sh >> /root/inotify_flume.log &保存退出
#!/bin/bash
# THIS FILE IS ADDED FOR COMPATIBILITY PURPOSES
#
# It is highly advisable to create own systemd services or udev rules
# to run scripts during boot instead of using this file.
#
# In contrast to previous versions due to parallel execution during boot
# this script will NOT be run after all other services.
#
# Please note that you must run 'chmod +x /etc/rc.d/rc.local' to ensure
# that this script will be executed during boot.
systemctl start qemu-guest-agent
touch /var/lock/subsys/local
sh /root/change.sh && rm -rf /root/change.sh
sysctl -p
nohup /root/web_backup.sh >> /root/inotify_flume.log &

七、遗留问题

1、没有文件锁和进程锁,当同步大量文件时可能会出现问题。

2、在脚本中用echo 输出空格 困扰我很久,一直没有解决。

crontab -l > conf && echo "* */2 * * * rsync -avz $(dirname ${INO_FILE}) ${des} && rsync -avz $(dirname ${INO_FILE})  ${des}" >> conf && crontab conf && rm -f conf
这个是想在脚本里实现添加计划任务,总是得不到我想要的结果。技术有限,如有大佬还请指点。
预期是 这样的输出 rsync -avz /root/t2 /web_backup && rsync -avz /root/t2 /web_backup
结果是 这样的输出 rsync -avz /root/t2/web_backup && rsync -avz /root/t2/web_backup
变量之间的空格始终会被吃掉。