生产环境使用amazon linux2,对 GlusterFS的使用有些困难,决定使用ansible+sync+inotify-tools来完成宝塔管理的nginx负载均衡 通过ansible调用sync模块同步负载节点的配置,保证和主节点一样,用inotifywait写脚本持续监控目录,配置目录有变化则执行ansible-playbook,在宝塔的reload脚本内加入ansible剧本,实现同时触发reload操作 对新开启的服务器安装openresty服务,然后用ansible创建出必要的目录

---
- name: 同步www目录到所有服务器
  hosts: nginx_upstream
  become: true  # 提权
  tasks:
    - name: 创建 www 用户
      user:
        name: www
        comment: "Web server user"
        shell: /bin/false  # 禁止登录
        home: /www         # 指定主目录
        state: present     # 确保用户存在
    - name: 创建 /www 目录
      file:
        path: /www
        state: directory
        owner: root
        group: root
        mode: '0755'

    - name: 创建 /www/server/nginx 目录
      file:
        path: /www/server/nginx
        state: directory
        owner: root
        group: root
        mode: '0755'
    - name: 创建 /www/server/nginx/conf 目录
      file:
        path: /www/server/nginx/conf
        state: directory
        owner: root
        group: root
        mode: '0755'

    - name: 创建 /www/wwwroot 目录
      file:
        path: /www/wwwroot
        state: directory
        owner: root
        group: root
        mode: '0755'

    - name: 创建 /www/server/panel 目录
      file:
        path: /www/server/panel
        state: directory
        owner: root
        group: root
        mode: '0755'

    - name: 创建 /www/wwwlogs 目录
      file:
        path: /www/wwwlogs
        state: directory
        owner: www
        group: www
        mode: '0700'
    - name: 创建 /dev/shm/nginx-cache/wp/ 目录
      file:
        path: /dev/shm/nginx-cache/wp/
        state: directory
        owner: www
        group: www
        mode: '0755'
    - name: 创建 /www/server/nginx/logs 目录
      file:
        path: /www/server/nginx/logs
        state: directory
        owner: root
        group: root
        mode: '0755'

然后在写一个同步nginx所有配置目录和必要文件的剧本

---
- name: 同步www目录到所有服务器
  hosts: nginx_upstream
  become: true  # 提权
  serial: 2                         # 同时对 2 台服务器执行同步
  tasks:
    - name: 同步 www/server/panel/vhost/ 目录到目标服务器
      synchronize:
        src: /www/
        dest: /www/
        archive: true   # 保留权限、时间戳等
        delete: true    # 删除目标中不在源中的文件
        rsync_opts:
          - "--exclude=server/panel/data/"
          - "--exclude=wwwlogs/"
          - "--exclude=server/panel/config/"
          - "--exclude=server/panel/logs/"
          - "--exclude=server/nginx/nginx/logs/"
          - "--exclude=server/panel/cache/thumbnail/"
          - "--exclude=backup/"

--exclude是指定不同步的目录,写绝对路径会导致同步速度变慢,这里都是用相对路径 手动执行一次剧本,如果目录内文件多,第一次同步会比较慢 image.png 同步完成,然后需要解决在宝塔中保存证书或者更新配置后自动触发同步并且reload的问题 宝塔的nginx pid号默认是在nginx的配置目录中,之前我们同步过去会导致所有服务器都使用同一个pid号,所以在主节点换一个pid文件的目录,改成/run/nginx.pid 然后在同步一下,可以检查针对这次修改同步的速度和结果是否符合预期,修改了pid文件的路径,找到主节点宝塔的nginx脚本,同样要修改一下,一般是 /etc/init.d/nginx 这个路径image.png 修改后回到宝塔reload,看看服务的线程pid是否有变化,如果变了就正常,然后写一个reload的剧本,手动测试一下可以让负载服务器的nginx正常reload

---
- name: reload
  hosts: nginx_upstream
  become: true  # 提权
  tasks:
    - name: 重启 Nginx 服务(server1)
      shell: openresty -s reload -c /www/server/nginx/conf/nginx.conf

image.png 执行后登陆到其他节点看下线程pid是否有变化,没什么问题需要把这个剧本加入到宝塔nginx的脚本内image.png 我在这里添加了等待5秒后执行重启,为了给同步留一点时间,如果立马重启可能会导致数据还没同步完全,并且加了一个日志的记录 然后去宝塔可以做一个配置添加或者直接对站点修改,测试是否可以触发reload,并且观察日志 如果没有问题,之后下载使用inotify-tools inotify-tools 是一组基于 Linux 内核中的 inotify 子系统的命令行工具和库,用于监控文件系统的事件。它包含以下两个主要工具:

inotifywait:用于等待文件系统上的事件。 inotifywatch:用于收集文件系统事件的统计信息。 我们用inotifywait这个命令来监控nginx配置和资源目录,出现变动后调用ansible的剧本来执行同步,可以做到实时同步资源和配置到所有负载服务器 下载好命令后添加一个脚本

#!/bin/bash
WATCHED_DIRS=( "/www/" )
EXCLUDED_DIRS=( "/www/server/panel/data/" "/www/wwwlogs/" "/www/server/panel/config/" "/www/server/panel/logs/" "/www/server/nginx/nginx/logs/"  "/www/server/panel/cache/thumbnail/" "/www/backup/" )  # 定义排除的多个目录
PLAYBOOK="/root/upstream/sync.yaml"  # Ansible Playbook 的路径
INVENTORY="/etc/ansible/hosts"  # Ansible Inventory 文件路径
LOGFILE="/var/log/upstream_sync.log"  # 日志文件
BUFFER_FILE="/tmp/sync_inotify_buffer"  # 暂存文件变化记录的缓冲区文件
DELAY=3  # 延迟执行的秒数

# 确保日志文件和缓冲文件可写
touch $LOGFILE
chmod 644 $LOGFILE
touch $BUFFER_FILE
chmod 644 $BUFFER_FILE

echo "$(date) - Starting directory watch on ${WATCHED_DIRS[*]}" >> $LOGFILE

# 定义函数来触发 Ansible Playbook 执行并清空缓冲区
run_playbook() {
    echo "$(date) - Running Ansible Playbook due to changes in:" >> $LOGFILE
    cat $BUFFER_FILE >> $LOGFILE
    ansible-playbook -i $INVENTORY $PLAYBOOK >> $LOGFILE 2>&1
    # 清空缓冲区
    : > $BUFFER_FILE
}

# 将排除目录转为正则表达式,供 inotifywait 使用
EXCLUDE_REGEX=$(printf "%s|" "${EXCLUDED_DIRS[@]}" | sed 's/\/$/\//' | sed 's/|$//')

# 开始监控目录
inotifywait -m -r -e modify,create,delete --exclude "${EXCLUDE_REGEX}" "${WATCHED_DIRS[@]}" | while read path action file; do
    echo "$(date) - Checking path: $path$file against exclusions: ${EXCLUDE_REGEX}" >> $LOGFILE

    # 将检测到的文件变化记录到缓冲区
    echo "$path$file" >> $BUFFER_FILE

    # 如果没有其他进程在等待,设置一个延迟执行 Playbook 的任务
    if [[ ! -f /tmp/ansible_task_scheduled ]]; then
        echo "$(date) - Detected event for $path$file" >> $LOGFILE
        touch /tmp/ansible_task_scheduled

        # 设置延迟执行任务
        (
            sleep $DELAY
            run_playbook
            rm /tmp/ansible_task_scheduled
        ) &
    else
        echo "$(date) - Collecting event for $path$file" >> $LOGFILE
    fi
done

修改这个脚本满足自己的需要,添加一个system管理,可以做到后台持续运行

cat /etc/systemd/system/monitor.service
[Unit]
Description=目录监控并触发 Ansible 的脚本
After=network.target
Requires=network.target

[Service]
Type=simple
ExecStart=/root/upstream/monitor.sh #脚本路径
Restart=always
RestartSec=5
User=root
Group=root
StandardOutput=syslog
StandardError=syslog
SyslogIdentifier=dir_watch

[Install]
WantedBy=multi-user.target

启动脚本后尝试手动对监控目录做修改,然后检查是否有同步打到想要的结果