工作原理:

  • 利用监控服务(inotify),监控同步数据服务器目录中信息的变化。
  • 发现目录中数据产生变化,就利用 rsync 服务推送到备份服务器上。

    1. inotify

    inotify 是一个异步的文件系统事件监控机制,利用事件驱动机制,而无须通过诸如 cron 等的轮询机制来获取事件,linux 内核从 2.6.13 起支持 inotify,通过 inotify 可以监控文件系统中添加、删除,修改、移动等各种事件。

    1.1 inotify 内核参数说明:

  • max_queued_events:inotify 事件队列最大长度,如值太小会出现 Event Queue Overflow 错误,默认值:16384,生产环境建议调大

  • max_user_instances:每个用户创建 inotify 实例最大值,默认值:128
  • max_user_watches:可以监视的文件的总数量(inotifywait 单进程),默认值:8192,生产环境建议调大 ```bash [root@centos7 ~]# cat /proc/sys/fs/inotify/max_queued_events 16384 [root@centos7 ~]# cat /proc/sys/fs/inotify/max_user_instances 128 [root@centos7 ~]# cat /proc/sys/fs/inotify/max_user_watches 8192

[root@centos7 ~]# sysctl -a | grep inotify sysctl: reading key “net.ipv6.conf.all.stable_secret” sysctl: reading key “net.ipv6.conf.default.stable_secret” sysctl: reading key “net.ipv6.conf.eth0.stable_secret” sysctl: reading key “net.ipv6.conf.lo.stable_secret” fs.inotify.max_queued_events = 16384 fs.inotify.max_user_instances = 128 fs.inotify.max_user_watches = 8192

  1. 修改内核参数:
  2. ```bash
  3. [root@centos7 ~]# vim /etc/sysctl.conf
  4. ......
  5. fs.inotify.max_queued_events=65536
  6. fs.inotify.max_user_watches=131072
  7. [root@centos7 ~]# sysctl -p
  8. fs.inotify.max_queued_events = 65536
  9. fs.inotify.max_user_watches = 131072

1.2 inotify-tools 工具

GitHub项目地址

1.2.1 安装

centos 发行版可以从 EPEL 仓库中直接安装。

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

1.2.2 使用

inotifywait:在被监控的文件或目录上等待特定文件系统事件(open ,close,delete等)发
生,常用于实时同步的目录监控。
格式:inotifywait [ options ] file1 [ file2 ] [ file3 ] [ ... ]
常用选项:

-m, --monitor                    # 始终保持事件监听
-d, --daemon                    # 以守护进程方式执行,和-m相似,配合-o使用
-r, --recursive                #    递归监控目录数据信息变化
-q, --quiet                        # 静默输出
--exclude <pattern>        # 指定排除文件或目录,支持扩展的正则表达式
--excludei <pattern>    # 和exclude相似,不区分大小写
-o, --outfile <file>    # 打印事件输出到指定文件中,相当于标准输出,注意:使用绝对路径
-s, --syslogOutput         # 发送错误到 syslog 相当于标准错误输出
--timefmt <fmt>                #    指定时间输出格式
--format <fmt>                #    指定的输出格式;即实际监控输出内容
-e                                        # 只监听指定的事件,省略则表示所有事件都进行监听

**--format**参数格式定义:

%T        # 输出时间格式中定义的时间格式信息,通过--timefmt option语法格式指定时间信息
%w        # 事件出现时,监控目录的名称信息
%f        # 事件出现时,监控目录下触发事件的文件名称信息
%e        # 显示发生的事件信息,不同的事件默认用逗号分隔
%Xe        # 显示发生的事件信息,不同的事件指定用X进行分隔

**-e**参数可指定的EVENT事件:

create                # 文件或目录创建
delete                # 文件或目录被删除
modify                # 文件或目录内容被写入
attrib                # 文件或目录属性改变
close_write        # 文件或目录关闭,在写入模式打开之后关闭的
close_nowrite # 文件或目录关闭,在只读模式打开之后关闭的
close                    # 文件或目录关闭,不管读或是写模式
open                    # 文件或目录被打开
lsdir                    # 浏览目录内容
moved_to            # 文件或目录被移动到监控的目录中
moved_from        # 文件或目录从监控的目录中被移动
move                    # 文件或目录不管移动到或是移出监控目录都触发事件
access                # 文件或目录内容被读取
delete_self        # 文件或目录被删除,目录本身被删除
unmount                # 取消挂载

使用范例:

#监控一次性事件
inotifywait /data/www

#持续前台监控,并排除指定文件
inotifywait -mrq /data/www --exclude=".*\.swx|\.swp"

#持续后台监控,并记录日志
inotifywait -o /root/inotify.log -drq /data/www --timefmt "%Y-%m-%d %H:%M:%S" --format "%T %w%f event: %e"

#持续前台监控特定事件
inotifywait -mrq /data/www --timefmt "%F %H:%M:%S" --format "%T %w%f event: %;e" -e create,delete,moved_to,close_write,attrib

2. rsync

rsync 官方网站
rsync 常用于做为 linux 系统下的数据传输和备份工具,实现远程同步,支持本地复制,或者与其他 SSH、 rsync 主机同步数据,支持增量备份,配合任务计划,rsync 能实现定时或间隔同步,配合 inotify 或 sersync,可以实现触发式的实时数据同步。

  • 服务文件:/usr/lib/systemd/system/rsyncd.service
  • 配置文件:/etc/rsyncd.conf
  • 端口:873/tcp

rsync有三种工作方式:

  1. 本地文件系统上实现同步,类似于cp命令。
  2. 本地主机使用远程shell和远程主机通信。类似于scp命令
  3. 本地主机通过网络套接字连接远程主机上的rsync daemon。

前两者的本质是通过本地或远程 shell,而第 3 种方式则是让远程主机上运行 rsyncd deamon 服务,使其监听在一个端口上,等待客户端的连接。

2.1 安装

# Debian
$ sudo apt-get install rsync

# Red Hat
$ sudo yum install rsync

# Arch Linux
$ sudo pacman -S rsync

注意:传输的双方都必须安装 rsync。

2.2 常用参数

-v                                        # 显示rsync过程中详细信息,可以使用多个v查看更详细的信息。
-P                                        # 显示文件传输的进度信息。(实际上"-P"="--partial --progress",其中的"--progress"才是显示进度信息的)。
-n --dry-run                    # 仅测试传输,而不实际传输。常和"-vvvv"配合使用来查看rsync是如何工作的。
-a --archive                    # 归档模式,递归传输并保持文件属性。等同于"-rtopgDl"。
-r --recursive                # 递归传输。
-t --times                        # 保持mtime属性。强烈建议任何时候都加上,否则目标文件mtime会设置为系统时间,导致下次更新检查出mtime不同从而导致增量传输无效。
-o --owner                        # 保持owner属性。
-g --group                        # 保持group属性。
-p --perms                        # 保持perms属性(权限,但不包括特殊权限)。
-D                                         # 是"--device --specials"选项的组合,即也拷贝设备文件和特殊文件。
-l --links                        # 如果文件是软链接文件,则拷贝软链接本身而非软链接所指向的对象
-z                                        # 传输时进行压缩提高效率
-R --relative                    # 使用相对路径。意味着将命令行中指定的全路径而非路径最尾部的文件名发送给服务端,包括它们的属性。
--size-only                        # 默认算法是检查文件大小和mtime不同的文件,使用此选项将只检查文件大小。
-u --update                        # 仅在源mtime比目标已存在文件的mtime新时才拷贝。注意,该选项是接收端判断的,不会影响删除行为。
-d --dirs                            # 以不递归的方式拷贝目录本身。默认递归时,如果源为"dir1/file1",则不会拷贝dir1目录,使用该选项将拷贝dir1但不拷贝file1。
--max-size                        # 限制rsync传输的最大文件大小,可以使用单位后缀,还可以是一个小数值(例如:"--max-size=1.5m")
--min-size                        # 限制rsync传输的最小文件大小。这可以用于禁止传输小文件或那些垃圾文件。
--exclude                            # 指定排除规则来排除不需要传输的文件。
--delete                            # 以SRC为主,对DEST进行同步。多则删之,少则补之。注意"--delete"是在接收端执行的,所以它是在exclude/include规则生效之后才执行的。
-b --backup                        # 对目标上已存在的文件做一个备份,备份的文件名后默认使用"~"做后缀。
--backup-dir                    # 指定备份文件的保存路径。不指定时默认和待备份文件保存在同一目录下。
-e                                        # 指定所要使用的远程shell程序,默认为ssh。
--port                                # 连接daemon时使用的端口号,默认为873端口。
--password-file                # daemon模式时的密码文件,可以从中读取密码实现非交互式。注意,这不是远程shell认证的密码,而是rsync模块认证的密码。
-W --whole-file                # rsync将不再使用增量传输,而是全量传输。在网络带宽高于磁盘带宽时,该选项比增量传输更高效。
--existing                        # 要求只更新目标端已存在的文件,目标端还不存在的文件不传输。注意,使用相对路径时如果上层目录不存在也不会传输。
--ignore-existing            # 要求只更新目标端不存在的文件。和"--existing"结合使用有特殊功能。
--remove-source-files    # 要求删除源端已经成功传输的文件

3. rsync + inotify- tools 实现文件同步

3.1 角色

  • 服务器 A:10.0.0.71(数据服务器)
  • 服务器 B:10.0.0.72(备份服务器)

    3.2 备份服务器开启 rsyncd 服务

    ```bash

    安装 rsync

    rpm -q rsync &> /dev/null || yum -y install rsync

设置 rsync 的配置文件

vi /etc/rsyncd.conf ……

全局配置

uid=root gid=root pid file = /var/run/rsyncd.pid port = 873 max connections=10

默认为 true,修改为 no,增加对目录文件软连接的备份

use chroot=no

忽略无关错误

ignore errors = yes

日志格式设置

log format = %t %a %m %f %b log file=/var/log/rsyncd.log

排除文件或目录

exclude = lost+found/

关闭反向解析

reverse lookup = no

访问控制

hosts allow = 10.0.0.0/24

模块配置

[www]

自定义注释

comment = Nginx页面备份

同步文件存放路径

path = /data/www/

设置rsync服务端为读写权限,默认为只读

read only = no

认证用户设置,与系统用户无关,没有设置则表明是匿名用户

auth users = rsync

密码认证文件,格式(虚拟用户名: 密码)

secrets file = /etc/rsyncd.pass ……

创建 rsync 认证文件,可以设置多个,格式为 用户名:密码

echo “rsync:rsync” > /etc/rsyncd.pass

设置文件所有者读取、写入权限

chmod 600 /etc/rsyncd.conf chmod 600 /etc/rsyncd.pass

启动 rsync 服务

systemctl enable —now rsyncd.service

<a name="H2gXQ"></a>
## 3.3 数据服务器运行 inotify_rsync 脚本
```bash
# 安装inotify-tools
yum install -y inotify-tools

# 创建密码文件
echo "rsync" > /etc/rsyncd.pass

# 修改权限
chmod 600 /etc/rsyncd.pass

# 编写脚本
vim inotify_rsync.sh
#!/bin/bash

SRC='/opt/nginx/'
DEST='rsync@10.0.0.72::www'

inotifywait -mrq --exclude=".*\.swp" --timefmt '%F %T' --format '%T %w %f' -e create,delete,moved_to,close_write,attrib ${SRC} | while read DATE TIME DIR FILE;do
    FILEPATH=${DIR}${FILE}
    rsync -az --delete --password-file=/etc/rsyncd.pass $SRC $DEST && echo "AT ${TIME} on ${DATE}, file ${FILEPATH} was backuped via rsync!" >> /var/log/file_change.log
done

# 查看日志
tail -f /var/log/changelist.log

4. sersync 实现文件同步

官方文档
项目地址
sersync类似于inotify,同样用于监控,但它克服了inotify 的缺点。inotify 最大的不足是会产生重复事件,或者同一个目录下多个文件的操作会产生多个事件,例如,当监控目录中有5个文件时,删除目录时会产生 6 个监控事件,从而导致重复调用 rsync 命令,另外像使用 vim 命令修改文件时,inotify 会监控到临时文件的事件,但这些事件相对于 rsync 来说是不应该被监控的。
sersync 的优点:

  1. sersync 是使用 c++ 编写,而且对 linux 系统文件系统产生的临时文件和重复的文件操作进行过滤,所以在结合 rsync 同步的时候,节省了运行时耗和网络资源,因此更快。
  2. sersync 配置起来很简单,bin 目录下已经有基本上静态编译的二进制文件,配合 bin 目录下的 xml 配置文件直接使用即可。
  3. sersync 使用多线程进行同步,尤其在同步较大文件时,能够保证多个服务器实时保持同步状态。
  4. sersync 有出错处理机制,通过失败队列对出错的文件重新同步,如果仍旧失败,则按设定时长对同步失败的文件重新同步。
  5. sersync 自带 crontab 功能,只需在 xml 配置文件中开启,即可按要求,隔一段时间整体同步一次。无需再额外配置 crontab 功能。
  6. 支持 socket 与 http 插件扩展,支持二次开发。

    4.1 基于 rsync daemon 实现 sersync

    4.1.1 角色

  • 服务器 A(数据服务器)
  • 服务器 B(备份服务器)
  • rsync 默认 TCP 端口为 873

    4.1.2 备份服务器 B 配置 rsyncd.service

    ```bash

    安装 rsync

    rpm -q rsync &> /dev/null || yum -y install rsync

设置 rsync 的配置文件

vi /etc/rsyncd.conf ……

全局配置

uid=root gid=root pid file = /var/run/rsyncd.pid port = 873

use chroot=no # 默认为 true,修改为 no,增加对目录文件软连接的备份 max connections=36000 # 最大连接数 ignore errors = yes # 忽略无关错误 log format = %t %a %m %f %b # 日志格式设置 log file=/var/log/rsyncd.log # 日志文件路径 exclude = lost+found/

hosts allow = 10.0.0.0/24 # IP访问权限设置 read only = no # 设置rsync服务端为读写权限,默认为只读

auth users = rsync # 认证用户设置,与系统用户无关,没有设置则表明是匿名用户 secrets file = /etc/rsync.pass # 密码认证文件,格式(虚拟用户名: 密码)

模块配置

[www] comment = www # 自定义注释 path=/data/nginx # 同步文件存放路径 ……

创建 rsync 认证文件,可以设置多个,格式为 用户名:密码

echo “rsync:rsync” > /etc/rsync.pass

设置文件所有者读取、写入权限

chmod 600 /etc/rsyncd.conf
chmod 600 /etc/rsync.pass

启动 rsync 服务

systemctl enable —now rsyncd.service

<a name="zHIKm"></a>
### 4.1.3 数据服务器 A 配置 sersync
```bash
# 下载
wget https://storage.googleapis.com/google-code-archive-downloads/v2/code.google.com/sersync/sersync2.5.4_64bit_binary_stable_final.tar.gz


# 解压
tar xvf sersync2.5.4_64bit_binary_stable_final.tar.gz -C /usr/local/


# 软连接
ln -s /usr/local/GNU-Linux-x86 /usr/local/sersync


# 环境变量设置
echo 'PATH=/usr/local/sersync:$PATH' > /etc/profile.d/sersync.sh


# 安装 rsync
rpm -q rsync &> /dev/null || yum -y install rsync


# 配置密码文件,密码要和服务器 B 中配置的密码一致
echo "123456" > /usr/local/sersync/www.pass


# 修改权限
chmod 600 /usr/local/sersync/www.pass


# 备份配置文件
cp /usr/local/sersync/confxml.xml{,.bak}


# 修改 confxml.conf
vim /usr/local/sersync/confxml.xml
<?xml version="1.0" encoding="ISO-8859-1"?>                                                    
<head version="2.5">
    <host hostip="localhost" port="8008"></host>
    <debug start="false"/>
    <fileSystem xfs="false"/>
    <filter start="true"> <!-- 是否开启文件过滤功能,当为true时,以下类型的文件将不同步 -->
                <exclude expression="(.*)\.svn"></exclude>
              <exclude expression="(.*)\.gz"></exclude>
              <exclude expression="logs/*"></exclude>
    </filter>

    <inotify> # 监控事件设置
              <delete start="true"/>
              <createFolder start="true"/>
              <createFile start="false"/>
              <closeWrite start="true"/>
              <moveFrom start="true"/>
              <moveTo start="true"/>
              <attrib start="false"/>
              <modify start="false"/>
    </inotify>

    <sersync>
              <localpath watch="/data/nginx"> <!-- 主服务器 A 要同步的文件夹路径 -->
                  <remote ip="10.0.0.73" name="www"/>  <!-- 备份服务器 B 地址和rsync daemon的模块名,如果开启ssh模式,此时的name为备份目标目录 -->
                  <!--<remote ip="192.168.8.39" name="tongbu"/>-->
                  <!--<remote ip="192.168.8.40" name="tongbu"/>-->
              </localpath>

              <rsync>
                  <commonParams params="-artuz"/> <!-- rsync 命令行参数设置 -->
                  <auth start="true" users="www" passwordfile="/usr/local/sersync/www.pass"/> <!-- 备份服务器 B 的rsync认证配置 -->
                  <userDefinedPort start="false" port="874"/><!-- port=874 -->
                  <timeout start="false" time="100"/><!-- timeout=100 -->
                  <ssh start="false"/> <!-- 默认使用rsync daemon运行rsync命令,true为使用ssh模式 -->
              </rsync>
              <failLog path="/tmp/rsync_fail_log.sh" timeToExecute="60"/><!--default every 60mins execute once--> <!-- 错误重传及日志文件路径 -->
              <crontab start="false" schedule="600"><!--600mins--> <!-- 定时任务功能设置 -->
                  <crontabfilter start="false">
                            <exclude expression="*.php"></exclude>
                            <exclude expression="info/*"></exclude>
                      </crontabfilter>
                  </crontab>
              <plugin start="false" name="command"/>
    </sersync>
         <!-- 下面为插件配置,一般不需要修改,可以忽略 -->
    <plugin name="command">
              <param prefix="/bin/sh" suffix="" ignoreError="true"/>  <!--prefix /opt/tongbu/mmm.sh suffix-->
              <filter start="false">
                  <include expression="(.*)\.php"/>
                  <include expression="(.*)\.sh"/>
              </filter>
    </plugin>

    <plugin name="socket">
              <localpath watch="/opt/tongbu">
          <deshost ip="192.168.138.20" port="8009"/>
              </localpath>
    </plugin>

    <plugin name="refreshCDN">
              <localpath watch="/data0/htdocs/cms.xoyo.com/site/">
                  <cdninfo domainname="ccms.chinacache.com" port="80" username="xxxx" passwd="xxxx"/>
                  <sendurl base="http://pic.xoyo.com/cms"/>
                  <regexurl regex="false" match="cms.xoyo.com/site([/a-zA-Z0-9]*).xoyo.com/images"/>
              </localpath>
    </plugin>
</head>


#查看命令帮助
sersync2 -h

# 启动服务
sersync2 -dro /usr/local/sersync/confxml.xml

4.2 基于 ssh 实现 sersync

基于 ssh 的 sersync 不需要配置 rsync daemon,只需要配置基于 key 验证的 ssh 免密登录即可。

# ssh key认证
ssh-keygen 
ssh-copy-id backup-server


# 修改 sersync 配置文件,启用 ssh 方式。
vim /usr/local/sersync/confxml.xml
<?xml version="1.0" encoding="ISO-8859-1"?>                                                    
<head version="2.5">
    <host hostip="localhost" port="8008"></host>
    <debug start="false"/>
    <fileSystem xfs="false"/>
    <filter start="true"> <!-- 是否开启文件过滤功能,当为true时,以下类型的文件将不同步 -->
                <exclude expression="(.*)\.svn"></exclude>
              <exclude expression="(.*)\.gz"></exclude>
              <exclude expression="logs/*"></exclude>
    </filter>

    <inotify> # 监控事件设置
              <delete start="true"/>
              <createFolder start="true"/>
              <createFile start="false"/>
              <closeWrite start="true"/>
              <moveFrom start="true"/>
              <moveTo start="true"/>
              <attrib start="false"/>
              <modify start="false"/>
    </inotify>

    <sersync>
              <localpath watch="/opt/nginx"> <!-- 主服务器 A 要同步的文件夹路径 -->
                  <remote ip="10.0.0.73" name="/data/www"/>  <!-- 备份服务器 B 地址和rsync daemon的模块名,如果开启ssh模式,此时的name为备份目标目录 -->
                  <!--<remote ip="192.168.8.39" name="tongbu"/>-->
                  <!--<remote ip="192.168.8.40" name="tongbu"/>-->
              </localpath>

              <rsync>
                  <commonParams params="-artuz"/> <!-- rsync 命令行参数设置 -->
                  <auth start="false" users="www" passwordfile="/usr/local/sersync/www.pass"/> <!-- 备份服务器 B 的rsync认证配置 -->
                  <userDefinedPort start="false" port="874"/><!-- port=874 -->
                  <timeout start="false" time="100"/><!-- timeout=100 -->
                  <ssh start="true"/> <!-- 默认使用rsync daemon运行rsync命令,true为使用ssh模式 -->
              </rsync>
              <failLog path="/tmp/rsync_fail_log.sh" timeToExecute="60"/><!--default every 60mins execute once--> <!-- 错误重传及日志文件路径 -->
              <crontab start="false" schedule="600"><!--600mins--> <!-- 定时任务功能设置 -->
                  <crontabfilter start="false">
                            <exclude expression="*.php"></exclude>
                            <exclude expression="info/*"></exclude>
                      </crontabfilter>
                  </crontab>
              <plugin start="false" name="command"/>
    </sersync>
         <!-- 下面为插件配置,一般不需要修改,可以忽略 -->
    <plugin name="command">
              <param prefix="/bin/sh" suffix="" ignoreError="true"/>  <!--prefix /opt/tongbu/mmm.sh suffix-->
              <filter start="false">
                  <include expression="(.*)\.php"/>
                  <include expression="(.*)\.sh"/>
              </filter>
    </plugin>

    <plugin name="socket">
              <localpath watch="/opt/tongbu">
          <deshost ip="192.168.138.20" port="8009"/>
              </localpath>
    </plugin>

    <plugin name="refreshCDN">
              <localpath watch="/data0/htdocs/cms.xoyo.com/site/">
                  <cdninfo domainname="ccms.chinacache.com" port="80" username="xxxx" passwd="xxxx"/>
                  <sendurl base="http://pic.xoyo.com/cms"/>
                  <regexurl regex="false" match="cms.xoyo.com/site([/a-zA-Z0-9]*).xoyo.com/images"/>
              </localpath>
    </plugin>
</head>


# 启动服务
sersync2 -dro /usr/local/sersync/confxml.xml