1. 背景
服务无法做成高可用,只能采用2个节点一主、一备(冷备)模式。为了保证主节点文件实时同步到备份节点,使用inotifywait监控主节点目录的事件,并触发rsync同步变更到备份节点。
2. 环境
-
服务器 VM 8C/32G/200G 2台
- 192.168.16.41 主节点
- 192.168.16.51 备份节点
-
操作系统 Ubuntu24.04
3. 安装依赖
bash
# CentOS
#yum install inotify-tools rsync cron
# Ubuntu
apt install inotify-tools rsync cron
4. 创建目录
bash
mkdir -p /public/script/logs
5. 修改系统参数
5.1 系统参数
bash
vim /etc/sysctl.conf
bash
# 增加 inotify 的相关配置,解决tail: inotify 资源耗尽的错误
# 默认128
fs.inotify.max_user_instances = 12800
# 默认8192
fs.inotify.max_user_watches = 819200
5.2 加载生效
bash
sysctl -p
6. 备份节点
6.1 配置rsync
- 配置rsync
bash
vim /etc/rsyncd.conf
bash
# /etc/rsyncd: configuration file for rsync daemon mode
# See rsyncd.conf man page for more options.
# configuration example:
uid = 0
gid = 0
use chroot = no
max connections = 100
pid file = /var/run/rsyncd.pid
lock file = /var/run/rsync.lock
log file = /var/log/rsyncd/rsyncd.log
# exclude = lost+found/
transfer logging = yes
# timeout = 900
ignore nonreadable = yes
dont compress = *.gz *.tgz *.zip *.z *.Z *.rpm *.deb *.bz2
#include = *.gz *.tgz *.zip *.z *.Z *.bz2
[public]
path = /public/
# 允许写入
read only = false
list = yes
#exclude = logs/ log/ *.hprof *.log log*
auth users = rsyncuser
secrets file = /etc/rsync.password
# 只允许192.168.16.41连接
hosts allow = 192.168.16.41
- 配置密码
bash
vim /etc/rsync.password
- 账号和密码为示例,请自定义并与主节点上的密码保持一致
bash
rsyncuser:kxx0000
- 文件赋权
bash
ll /etc/rsync*
bash
# 注意密码文件必须是600
-rw------- 1 root root 18 Aug 23 2024 rsync.password
-rw-r--r-- 1 root root 1204 Apr 9 15:03 rsyncd.conf
- 重启rsync
bash
systemctl start rsync
systemctl enable rsync
7. 主节点
7.1 编写脚本
-
脚本说明
- 脚本目录:/public/script
- 脚本rsync密码文件(权限600):/public/script/rsync_conf/rsync.password
- rsync密码需要与备份节点设置相同:注意只需要密码(不需要账号),例如:
kxx0000 - 监控目录:/public/application
- 远程目录:application (远程rsync服务的根目录是/public,所以合并/public/application,保持与监控目录一致)
- 监控事件忽略某些文件和目录(inotifywait语法,正则匹配):EXCLUDE_PATTERN
- rsync同步忽略某些文件和目录(rsync语法):RSYNC_EXCLUDES
- rsync使用排它锁,保证同一时间只有一个rsync进程执行:/usr/bin/flock
- inotifywait监控事件包括: create, modify, delete, move, attrib
-
脚本内容(详见注释)
bash
#!/bin/bash
#set -x
echo """
#########################################################################################################################
Real-time sync /public/application to remote rsync server
1. Monitor /public/application recursively using inotifywait
2. Trigger rsync when any file/directory changes
3. Preserve original filenames (ignore some files)
4. Log events and delete old logs
#########################################################################################################################
"""
# Define vars
SCRIPT_HOME=/public/script
PWD_FILE=${SCRIPT_HOME}/rsync_conf/rsync.password
LOG_FILE_NAME=`basename "$0" .sh`
CURRENT_DATE=$(date +"%Y-%m-%d")
LOG_FILE=$SCRIPT_HOME/logs/${LOG_FILE_NAME}_${CURRENT_DATE}.log
LOG_RETAIN_DAY=14
# The directory to monitor
WATCH_DIR=/public/application
# Remote rsync settings
LOCAL_HOST_IP="192.168.16.41" # 本机IP,用于远程路径组织
REMOTE_HOST_IP="192.168.16.51"
REMOTE_MODULE="public" # rsync 模块名
REMOTE_BASE_PATH="application"
# Sync lock file to prevent concurrent rsync runs
LOCK_FILE="/tmp/rsync_public_application.lock"
# 定义inotifywait 排除的正则表达式
# ^onlyoffice/rabbitmq/ 匹配路径中包含该目录片段(注意前导 ^ 表示从监控根开始匹配)
# .*\.(swp|tmp|log|out) 匹配任意文件名,只要它以 .swp、.tmp、.log 或 .out 结尾的文件
# .*~ 匹配以 ~ 结尾的文件
EXCLUDE_PATTERN='(^onlyoffice/rabbitmq/|.*[.](swp|tmp|log|out)$|.*~$)'
# 定义rsync 排除项(相对于源目录 /public/application/)
#RSYNC_EXCLUDES="--exclude=onlyoffice/rabbitmq"
# 如果有多个排除项,可以继续添加,例如:
RSYNC_EXCLUDES="--exclude=onlyoffice/rabbitmq --exclude=*.swp --exclude=*.tmp --exclude=*.log --exclude=*.out"
############################### start #########################################
DATE_TIME=$(date +"%Y-%m-%d %H:%M:%S")
echo -e "### ${DATE_TIME}: $(dirname $0)/$(basename $0) starts ###" >> $LOG_FILE
# Create logs directory if not exists
if [ ! -d "$SCRIPT_HOME/logs" ]; then
mkdir -p "$SCRIPT_HOME/logs"
DATE_TIME=$(date +"%Y-%m-%d %H:%M:%S")
echo -e "### ${DATE_TIME}: Directory $SCRIPT_HOME/logs has been created ###" >> $LOG_FILE
fi
# Check if watch directory exists
if [ ! -d "$WATCH_DIR" ]; then
DATE_TIME=$(date +"%Y-%m-%d %H:%M:%S")
echo -e "### ${DATE_TIME}: $WATCH_DIR does not exist, exit ###" >> $LOG_FILE
exit 1
fi
# Function to perform rsync sync
do_rsync() {
# Use flock to avoid multiple rsync processes
(
/usr/bin/flock -x -w 60 200 || exit 1
# -x:请求排他锁(exclusive lock)
# -w 60:超时等待 60 秒,退出子shell,返回1(非0)
# 200:自定义文件描述符的编号,与200>"$LOCK_FILE" 中值保持一致,表示打开同一个文件描述符。
DATE_TIME=$(date +"%Y-%m-%d %H:%M:%S")
echo -e "### ${DATE_TIME}: Syncing $WATCH_DIR to remote ..." >> $LOG_FILE
/usr/bin/rsync -avz --delete --password-file=${PWD_FILE} \
$RSYNC_EXCLUDES \
"$WATCH_DIR/" \
"rsync://rsyncuser@${REMOTE_HOST_IP}:873/${REMOTE_MODULE}/${REMOTE_BASE_PATH}/"
if [ $? -eq 0 ]; then
DATE_TIME=$(date +"%Y-%m-%d %H:%M:%S")
echo -e "### ${DATE_TIME}: Sync completed successfully ###" >> $LOG_FILE
else
DATE_TIME=$(date +"%Y-%m-%d %H:%M:%S")
echo -e "### ${DATE_TIME}: Sync failed with error code $? ###" >> $LOG_FILE
fi
) 200>"$LOCK_FILE"
}
# Delayed sync: collect events, wait for a quiet period, then sync
sync_triggered=0
delayed_sync() {
if [ $sync_triggered -eq 0 ]; then
sync_triggered=1
# Wait 5 seconds to see if more events come
sleep 5
do_rsync
sync_triggered=0
fi
}
# Monitor events recursively
DATE_TIME=$(date +"%Y-%m-%d %H:%M:%S")
echo -e "### ${DATE_TIME}: Start monitoring $WATCH_DIR for changes ###" >> $LOG_FILE
# Use inotifywait to monitor events: create, modify, delete, move, attrib
/usr/bin/inotifywait -m -r --exclude "$EXCLUDE_PATTERN" -e create -e modify -e delete -e move -e attrib "$WATCH_DIR" | while read path action file; do
# 忽略匹配模式的文件
#case "$file" in
# $IGNORE_PATTERNS) continue ;;
#esac
DATE_TIME=$(date +"%Y-%m-%d %H:%M:%S")
echo -e "### ${DATE_TIME}: Event: $action on $path$file ###" >> $LOG_FILE
# Trigger rsync after a short delay (to combine bursts of events)
delayed_sync &
# Clean old logs periodically (once per event, but it's cheap)
find "$SCRIPT_HOME/logs" -maxdepth 1 -mtime +$LOG_RETAIN_DAY -name "${LOG_FILE_NAME}*.log" -exec rm -rf {} \;
done
7.2 配置服务
- 编写服务脚本
bash
vim rsync_backup.service
bash
[Unit]
Description=rsync_backup.sh script
After=network.target
[Service]
Type=simple
PIDFile=/run/rsync_backup.pid
ExecStart=/public/script/rsync_backup.sh
ExecStop=/bin/kill -9 $MAINPID
#Restart=on-failure
Restart=always
PrivateTmp=true
#User=username
#Group=groupname
[Install]
WantedBy=multi-user.target
- 配置systemd
bash
ln -s /public/script/rsync_backup.service /etc/systemd/system/
bash
systemctl daemon-reload
systemctl enable rsync_backup.service
bash
systemctl start rsync_backup.service
systemctl status rsync_backup.service
bash
● rsync_backup.service - rsync_backup.sh script
Loaded: loaded (/etc/systemd/system/rsync_backup.service; enabled; preset: enabled)
Active: active (running) since Thu 2026-04-09 19:51:31 CST; 23min ago
Main PID: 2342963 (rsync_backup.sh)
Tasks: 3 (limit: 38332)
Memory: 1.1M (peak: 6.3M)
CPU: 5.238s
CGroup: /system.slice/rsync_backup.service
├─2342963 /bin/bash /public/script/rsync_backup.sh
├─2342971 /usr/bin/inotifywait -m -r --exclude "(^onlyoffice/rabbitmq/|.*[.](swp|tmp|log|out)\$|.*~\$)" -e create -e modify -e delete -e move -e attrib /public/application
└─2342972 /bin/bash /public/script/rsync_backup.sh
......
7.3 配置重启
bash
crontab -e
bash
@reboot /usr/sbin/ntpdate 192.168.5.254 > /dev/null 2>&1
0 */2 * * * /usr/sbin/ntpdate 192.168.5.254 > /dev/null 2>&1
30 */12 * * * /usr/sbin/ntpdate ntp1.aliyun.com > /dev/null 2>&1
# 每日凌晨0点,重启同步服务(rsync_backup)
0 0 * * * systemctl restart rsync_backup.service
8 验证测试
8.1 测试忽略文件
- 主节点创建test.log
bash
root@192-168-016-041:/public/application# echo "test" >> test.log
root@192-168-016-041:/public/application# ls
images nextcloud onlyoffice test.log
- 备份节点上未同步test.log
bash
root@192-168-016-051:/public/application# ls
images nextcloud onlyoffice
root@192-168-016-051:/public/application#
8.2 测试同步文件
- 主节点创建ok.txt
bash
root@192-168-016-041:/public/application# echo "ok" >> ok.txt
root@192-168-016-041:/public/application# ls
images nextcloud ok.txt onlyoffice test.log
- 备份节点上未同步ok.txt
bash
root@192-168-016-051:/public/application# ls
images nextcloud ok.txt onlyoffice