Inotify+rsync 系统实时热备【毫秒级】

93次阅读
没有评论

Inotify+rsync 系统实时热备【毫秒级】

一、部署流程

1. 环境准备(主 / 备两台 CentOS/RHEL 7 服务器)

节点 角色 核心配置
主节点 被监控端 部署 inotify+rsync 客户端
备节点 同步目标端 开启 SSH 22 端口
同步目录 主 / 备一致 /apps(可自定义)
依赖 主节点必装 rsync、inotify-tools、sshpass、curl、openssl、net-tools
核心网卡 主节点 ens36(内网网卡,rsync同步绑定此网卡)
网卡IP ens36 需配置固定IP(如10.0.0.191)

2. 主节点安装基础依赖(补充 net-tools)

# 安装epel源(inotify-tools依赖)
[ -f /etc/yum.repos.d/epel.repo ] || yum install epel-release -y
# 安装核心组件(补充net-tools用于网卡检测)
yum install rsync inotify-tools sshpass curl openssl net-tools -y

3. 主节点内核优化(保留原配置,增加文件描述符)

cat >/etc/sysctl.d/inotify.conf<<EOF
fs.inotify.max_queued_events=99999999  # 最大监听事件队列
fs.inotify.max_user_watches=99999999    # 最大监听文件数
fs.inotify.max_user_instances=65535     # 最大监听实例数
fs.file-max=6553500                     # 新增:提升文件描述符上限
EOF
# 生效配置
sysctl -p /etc/sysctl.d/inotify.conf

4. 主节点创建目录结构(新增进程 PID 目录)

# 业务目录(需同步的目录)
mkdir -p /apps
# 脚本/配置目录
mkdir -p /shell
# 日志目录(同步/推送日志)
mkdir -p /var/log/rsync_inotify
# PID目录(进程守护用)
mkdir -p /var/run/rsync_inotify

5. 主节点编写优化版配置文件

vim /shell/inotify_config.conf

# 实时热备配置文件
# 文件路径:/shell/inotify_config.conf
# 修改后需执行:systemctl restart inotify-rsync
​
# 远程备节点信息
# 说明:配置备份目标服务器(备节点)的SSH连接信息
REMOTE_USER="root"                # SSH登录用户名,建议使用专用账号而非root
REMOTE_HOST="172.16.1.192"        # 备节点IP地址(业务内网IP)
REMOTE_PATH="/apps"               # 备节点同步目录,建议与主节点DIRNAME保持一致
REMOTE_PASSWORD="oldboy123.com"   # SSH登录密码,请确保密码强度足够
​
# 注意事项:
# 1. 备节点必须开启SSH服务(默认端口22),确保主节点可以SSH免密或密码登录到备节点
# 2. REMOTE_PATH目录必须在备节点上已存在且有写入权限
# 3. 主备节点必须通过同一内网通信,建议使用业务内网IP(172.16.1.x)
​
# 本地主节点信息
# 说明:配置主节点的监控目录和网络接口
DIRNAME="/apps"                   # 需要实时监控的本地目录路径
MONITOR_NIC="ens36"               # 业务网卡名称,rsync同步将绑定此网卡
MONITOR_NIC_IP="172.16.1.191"     # ens36网卡的实际IP地址(业务内网IP)
​
# 重要提示:
# 1. 使用 ifconfig ens36 命令确认网卡名称和IP地址
# 2. MONITOR_NIC_IP 必须与实际配置完全一致,否则网络检测会失败
# 3. rsync同步会强制通过此网卡进行(使用BindAddress参数)
# 4. DIRNAME目录必须有读取权限,且存在文件变更事件
# 5. 主备节点ens36网卡必须配置在同一网段(172.16.1.0/24)
​
# 日志文件路径 
# 说明:配置各类日志文件的存储路径,便于问题排查
RSYNC_ERR_PATH="/var/log/rsync_inotify/rsync_err.log"    # rsync同步失败的错误日志
SCRIPT_RUN_LOG="/var/log/rsync_inotify/script_run.log"   # 脚本运行日志(只记录错误和异常)
PUSH_LOG_PATH="/var/log/rsync_inotify/push.log"          # 钉钉推送记录
PID_FILE="/var/run/rsync_inotify/inotify_rsync.pid"      # 进程PID文件,systemd用于管理进程
ALERT_COOLDOWN_FILE="/tmp/alert_cooldown"                # 告警冷却标记文件,防止重复告警
ALERT_COOLDOWN_TIME="300"                                # 告警冷却时间(秒),300秒=5分钟
​
# 日志使用说明:
# - 同步失败时查看 RSYNC_ERR_PATH
# - 脚本异常时查看 SCRIPT_RUN_LOG
# - 告警未推送时检查 ALERT_COOLDOWN_FILE
# - 推送记录查看 PUSH_LOG_PATH
​
# 钉钉机器人配置
# 说明:配置钉钉群机器人的Webhook和加签密钥,用于发送告警通知
# 注意:只推送异常告警,不推送成功消息
ROBOT="https://oapi.dingtalk.com/robot/send?access_token=457eba50934214c031a6fbc1a9f4561389ae70652fdd5d2f51c8c5605034dd2d"
SECRET="SEC6a40e01f9632fd2885dcc339063dd7041b21de521166379bf33742f2f81af0bd"
PUSH_RETRY_TIMES="2"              # 推送失败重试次数,网络不稳定时可增加到3-5次
PUSH_TIMEOUT="5"                  # 单次推送超时时间(秒),内网可设为3,外网建议5-10
​
# 钉钉配置获取方法:
# 1. 在钉钉群中添加「自定义机器人」
# 2. 安全设置选择「加签」方式
# 3. 复制Webhook地址到 ROBOT 配置项
# 4. 复制加签密钥到 SECRET 配置项
# 5. 测试推送:curl -X POST "${ROBOT}&timestamp=xxx&sign=xxx" -H 'Content-Type: application/json' -d '{"msgtype":"text","text":{"content":"test"}}'
​
# 告警类型说明:
# - nic_error:网卡未启用
# - nic_ip_error:网卡IP不匹配
# - network_error:无法连接备节点
# - sync_fail:文件同步失败
# - sync_success:文件同步成功(仅当OPEN_PUSH_INFO=on时推送)
# - script_exit:脚本异常退出

6. 主节点部署优化版同步脚本

vim /shell/inotify_rsync.sh

#!/bin/bash
# =====================================================
# 金融系统 Inotify+rsync 实时热备脚本
# 功能:监控目录变更,实时同步到备节点,异常告警
# =====================================================
​
set -uo pipefail
​
# ================= 第一步:加载配置 =================
CONFIG_FILE="/shell/inotify_config.conf"
​
if [ ! -f "${CONFIG_FILE}" ]; then
    echo "错误:配置文件不存在 ${CONFIG_FILE}"
    exit 1
fi
​
source "${CONFIG_FILE}" || {
    echo "错误:配置文件加载失败"
    exit 1
}
​
LOCAL_SERVER_IP=$(ifconfig ${MONITOR_NIC} | grep 'inet ' | awk '{print $2}' | sed 's/addr://')
REMOTE_PORT="22"
PUSH_LOG_PATH="/var/log/rsync_inotify/push.log"
​
# ================= 第二步:初始化 =================
mkdir -p "$(dirname ${PID_FILE})" \
         "$(dirname ${SCRIPT_RUN_LOG})" \
         "$(dirname ${RSYNC_ERR_PATH})" \
         "$(dirname ${PUSH_LOG_PATH})"
​
echo $$ > ${PID_FILE}
​
# ================= 第三步:定义函数 =================
​
log_info() {
    echo "$(date +%F_%T) [INFO] $1" >> ${SCRIPT_RUN_LOG}
}
​
log_error() {
    local msg="$(date +%F_%T) [ERROR] $1"
    echo "${msg}" >> ${SCRIPT_RUN_LOG}
    echo "${msg}" >> ${RSYNC_ERR_PATH}
}
​
ding_sign() {
    local timestamp=$(date +%s%3N)
    local sign=$(echo -en "${timestamp}\n${SECRET}" | \
                 openssl dgst -sha256 -hmac "${SECRET}" -binary | \
                 base64 | tr -d '\n')
    echo "timestamp=${timestamp}&sign=${sign}"
}
​
# 钉钉告警推送
ding_push() {
    local alert_title="$1"
    local alert_detail="$2"
    local alert_type="${3:-}"
    local push_success=0
​
    # 防重复告警(抢占式冷却)
    if [ -n "${alert_type}" ]; then
        local cooldown_file="${ALERT_COOLDOWN_FILE}.${alert_type}"
        local lock_file="${cooldown_file}.lock"
        
        # 检查文件锁(防止并发)
        if [ -f "${lock_file}" ]; then
            local lock_age=$(($(date +%s) - $(cat "${lock_file}" 2>/dev/null || echo 0)))
            if [ ${lock_age} -lt 5 ]; then  # 5秒内认为正在推送
                log_info "告警推送中,跳过: ${alert_type}"
                return 0
            else
                rm -f "${lock_file}"  # 锁超时,清除
            fi
        fi
        
        # 检查冷却时间
        if [ -f "${cooldown_file}" ]; then
            local last_time=$(cat "${cooldown_file}" 2>/dev/null || echo 0)
            local now=$(date +%s)
            local age=$((now - last_time))
            if [ ${age} -lt ${ALERT_COOLDOWN_TIME} ]; then
                log_info "告警被冷却拦截: ${alert_type} (剩余 $((ALERT_COOLDOWN_TIME - age)) 秒)"
                return 0
            fi
        fi
        
        # 抢占锁和冷却文件(在推送前就创建)
        date +%s > "${lock_file}"
        date +%s > "${cooldown_file}"
    fi
​
    # 设置默认告警详情
    local alert_detail="${2:-无详细信息}"
​
    # 构建告警消息
    local timestamp=$(date +%Y-%m-%d\ %H:%M:%S)
    local msg_content="🔔 *${alert_title}*
​
${alert_detail}
​
━━━━━━━━━━━━━━
• 服务器: ${LOCAL_SERVER_IP}
• 时间: ${timestamp}"
    local push_url="${ROBOT}&$(ding_sign "${SECRET}")"
​
    for ((i=1; i<=${PUSH_RETRY_TIMES}; i++)); do
        local result=$(curl -s --max-time ${PUSH_TIMEOUT} -X POST "${push_url}" \
            -H "Content-Type: application/json" -d "${msg_content}" 2>/dev/null)
        
        echo "$(date +%F_%T) [PUSH-${i}] ${result}" >> ${PUSH_LOG_PATH}
        
        if echo "${result}" | grep -q '"errcode":0'; then
            push_success=1
            break
        fi
        sleep 1
    done
​
    [ ${push_success} -eq 0 ] && log_error "钉钉推送失败"
}
​
# 网络检测
check_network() {
    if ! ifconfig ${MONITOR_NIC} >/dev/null 2>&1; then
        log_error "网卡异常:${MONITOR_NIC}"
        ding_push "网卡异常" "网卡 ${MONITOR_NIC} 未启用" "nic_error"
        return 1
    fi
​
    local nic_ip=$(ifconfig ${MONITOR_NIC} | grep 'inet ' | awk '{print $2}' | sed 's/addr://')
    if [ "${nic_ip}" != "${MONITOR_NIC_IP}" ]; then
        log_error "网卡IP异常:${MONITOR_NIC}=${nic_ip}"
        ding_push "网卡IP异常" "网卡: ${MONITOR_NIC}\n当前IP: ${nic_ip}\n期望IP: ${MONITOR_NIC_IP}" "nic_ip_error"
        return 1
    fi
​
    if ! nc -z -w 3 -s ${MONITOR_NIC_IP} ${REMOTE_HOST} ${REMOTE_PORT} >/dev/null 2>&1; then
        log_error "网络异常:无法连接 ${REMOTE_HOST}:${REMOTE_PORT}"
        ding_push "网络异常" "无法连接备节点 ${REMOTE_HOST}:${REMOTE_PORT}\n使用网卡: ${MONITOR_NIC}(${MONITOR_NIC_IP})" "network_error"
        return 1
    fi
​
    return 0
}
​
# ================= 第四步:启动监控 =================
ssh-keyscan -H ${REMOTE_HOST} >> ~/.ssh/known_hosts 2>/dev/null || true
​
inotifywait -mrq -e 'create,close_write' \
    --timefmt "%Y-%m-%d_%H:%M:%S" \
    --format "%T %w%f %e" \
    --exclude '.swp$|.swx$|4913|~$' \
    "${DIRNAME}" | while read -r line
do
    LOCAL_SOURCE=$(echo "${line}" | awk '{print $2}')
​
    if ! check_network; then
        continue
    fi
​
    sshpass -p "${REMOTE_PASSWORD}" rsync -avz \
        --timeout=10 \
        -e "ssh -o StrictHostKeyChecking=no \
              -o ConnectTimeout=5 \
              -o BindAddress=${MONITOR_NIC_IP}" \
        ${DIRNAME}/ ${REMOTE_USER}@${REMOTE_HOST}:${REMOTE_PATH}/ \
        2>>${RSYNC_ERR_PATH}
​
    if [ $? -ne 0 ]; then
        log_error "同步失败: ${LOCAL_SOURCE}"
        ding_push "文件同步失败" "同步文件: ${LOCAL_SOURCE}\n目标节点: ${REMOTE_HOST}:${REMOTE_PATH}" "sync_fail"
    fi
done
​
# ================= 第五步:异常退出 =================
log_error "脚本异常退出!"
ding_push "脚本异常退出" "监控脚本异常退出,请检查日志" "script_exit"
rm -f ${PID_FILE}
exit 1

7.主节点脚本授权与验证

# 赋予执行权限
chmod +x /shell/inotify_rsync.sh
​
# 创建日志目录
mkdir -p /var/log/rsync_inotify
mkdir -p /var/run/rsync_inotify

8. 创建 systemd 服务(推荐:系统级进程管理)

vim /etc/systemd/system/inotify-rsync.service

[Unit]
Description=Inotify Rsync Real-time Backup Service
After=network.target
Wants=network.target
​
[Service]
Type=simple
ExecStart=/shell/inotify_rsync.sh
Restart=always
RestartSec=10
StandardOutput=append:/var/log/rsync_inotify/script_run.log
StandardError=append:/var/log/rsync_inotify/script_run.log
​
# 资源限制(可选)
LimitNOFILE=65535
LimitNPROC=4096
​
# 进程管理
PIDFile=/var/run/rsync_inotify/inotify_rsync.pid
​
[Install]
WantedBy=multi-user.target

启用并启动服务:

# 重新加载 systemd 配置
systemctl daemon-reload
​
# 设置开机自启动并启动服务
systemctl enable --now inotify-rsync
​
# 查看服务状态
echo "=== 服务状态 ==="
systemctl status inotify-rsync --no-pager
​
echo "=== 进程状态 ==="
ps -ef | grep inotify_rsync | grep -v grep
​
echo "=== PID文件 ==="
cat /var/run/rsync_inotify/inotify_rsync.pid 2>/dev/null || echo "PID文件暂未生成"

输出示例(正常状态):

● inotify-rsync.service - Inotify Rsync Real-time Backup Service
Loaded: loaded (/etc/systemd/system/inotify-rsync.service; enabled)
Active: active (running) since Mon 2026-05-04 14:20:00 CST; 5s ago
Main PID: 15680 (inotify_rsync.sh)
 Tasks: 3
Memory: 8.5M
CGroup: /system.slice/inotify-rsync.service
        ├─15680 /bin/bash /shell/inotify_rsync.sh
        └─15681 inotifywait -mrq -e create,close_write /apps

9. 钉钉机器人测试

source /shell/inotify_config.conf
​
# 简单测试推送
ding_sign() {
    local timestamp=$(date +%s%3N)
    local sign=$(echo -en "${timestamp}\n${SECRET}" | openssl dgst -sha256 -hmac "${SECRET}" -binary | base64 | tr -d '\n')
    curl -s -X POST "${ROBOT}&timestamp=${timestamp}&sign=${sign}" \
    -H "Content-Type: application/json" \
    -d "{\"msgtype\":\"text\",\"text\":{\"content\":\"热备系统测试:服务器${LOCAL_SERVER_IP}\\n时间:$(date +%F_%T)\"}}"
}
​
ding_sign

成功返回:{"errcode":0,"errmsg":"ok"}

三、快速验证

1. 服务状态检查

# 查看 systemd 服务状态
systemctl status inotify-rsync --no-pager
​
# 查看进程是否运行
ps -ef | grep inotify_rsync | grep -v grep
​
# 查看 PID 文件
cat /var/run/rsync_inotify/inotify_rsync.pid

预期结果:

  • ✅ 服务状态为 active (running)
  • ✅ 进程正常运行
  • ✅ PID 文件存在且有效

2. 基础同步验证

# 主节点创建测试文件
echo "test $(date)" > /apps/test_sync.log
sleep 5
​
# 备节点验证
ssh root@172.16.1.192 "cat /apps/test_sync.log"
​
# 清理
rm -f /apps/test_sync.log
ssh root@172.16.1.192 "rm -f /apps/test_sync.log"

预期结果: 备节点文件内容与主节点一致


3. 告警功能验证

# 模拟网络故障(会触发告警)
ssh root@172.16.1.192 "systemctl stop sshd"
echo "test_alert" > /apps/test_alert.txt
sleep 5
​
# 查看日志
tail -5 /var/log/rsync_inotify/script_run.log
tail -5 /var/log/rsync_inotify/push.log
​
# 恢复
ssh root@172.16.1.192 "systemctl start sshd"
rm -f /apps/test_alert.txt

预期结果:

  • script_run.log 显示“网络异常”
  • push.log 显示钉钉推送成功
  • 收到钉钉告警消息

4. 日志查看

# 脚本运行日志(只记录错误)
tail -20 /var/log/rsync_inotify/script_run.log
​
# rsync错误日志
tail -20 /var/log/rsync_inotify/rsync_err.log
​
# 钉钉推送记录
tail -20 /var/log/rsync_inotify/push.log
​
# 实时监控日志
tail -f /var/log/rsync_inotify/script_run.log

四、日常维护

1. 服务管理

# 重启服务(修改配置后需要重启)
systemctl restart inotify-rsync
​
# 停止/启动服务
systemctl stop inotify-rsync
systemctl start inotify-rsync
​
# 查看状态
systemctl status inotify-rsync --no-pager
​
# 开机自启
systemctl enable inotify-rsync

2. 日志查看

# systemd日志(推荐)
journalctl -u inotify-rsync -f
​
# 脚本运行日志(只记录错误)
tail -f /var/log/rsync_inotify/script_run.log
​
# rsync错误日志
tail -f /var/log/rsync_inotify/rsync_err.log
​
# 钉钉推送记录
tail -f /var/log/rsync_inotify/push.log
​
# 查看最近100行
journalctl -u inotify-rsync -n 100 --no-pager

3. 配置管理

# 备份配置文件
cp /shell/inotify_config.conf /shell/inotify_config.conf.bak_$(date +%Y%m%d)
​
# 编辑配置
vim /shell/inotify_config.conf
​
# 重新加载(需重启服务)
systemctl restart inotify-rsync

4. 网络测试

# 测试SSH连接
ssh root@172.16.1.192 "echo OK"
​
# 测试端口连通性
nc -z -w 3 172.16.1.192 22 && echo "OK" || echo "FAIL"
​
# 查看网卡状态
ifconfig ens36

正文完
 0
评论(没有评论)