NFS双机热备高可用环境

一、 架构概述与设计目标

1.1 设计目标
  • **高可用 (HA)**:通过 Keepalived 实现 NFS 服务的 VIP (Virtual IP) 漂移,当主节点故障时,备用节点自动接管服务,客户端无感知。
  • 数据同步:通过 Rsync + Inotify 实现主备节点间共享数据的实时单向同步,确保备用节点上的数据与主节点一致。
  • 服务监控:Keepalived 监控 NFS 服务进程,异常时尝试重启,重启失败则主动放弃 VIP,触发故障转移。
1.2 核心组件与架构
  • NFS Server (主/备):提供实际的共享存储服务。
  • Keepalived:提供 VIP (172.16.60.244) 并监控 NFS 服务健康状态。
  • Rsync + Inotify:实现主备节点 /data/k8s_storage 目录的数据同步。
  • **客户端 (K8s Nodes)**:通过 VIP 挂载 NFS,不感知后端物理服务器切换。

架构示意图

二、 环境准备

2.1 服务器规划
角色 主机名 IP 地址 备注
NFS Master nfs-master 172.16.60.235 初始 VIP 持有者
NFS Slave nfs-slave 172.16.60.236 备用节点
虚拟 IP (VIP) - 172.16.60.244 供 K8s 集群挂载的地址

技术要求

  • 两台 NFS 节点服务器配置应尽可能一致。
  • 建议为 NFS 共享目录 (/data/k8s_storage) 使用独立的硬盘或分区。
2.2 基础系统配置

在两台节点服务器上执行:

1
2
3
4
5
6
7
# 1. 关闭防火墙
systemctl stop firewalld
systemctl disable firewalld

# 2. 关闭 SELinux (需重启生效)
sed -i 's/SELINUX=enforcing/SELINUX=disabled/g' /etc/selinux/config
setenforce 0

三、 部署 NFS 服务 (主备节点相同操作)

3.1 安装与配置
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 1. 安装 NFS 软件包
yum install -y nfs-utils rpcbind

# 2. 创建共享目录
mkdir -p /data/k8s_storage

# 3. 配置 NFS 导出 (`/etc/exports`)
# 允许 K8s 节点网段 (172.16.60.0/24) 挂载
echo "/data/k8s_storage 172.16.60.0/24(rw,sync,no_root_squash)" > /etc/exports

# 4. 使配置生效
exportfs -arv

# 5. 启动并设置开机自启
systemctl enable --now rpcbind nfs-server
3.2 验证 NFS 服务
1
2
3
4
5
6
7
8
# 查看本机 NFS 导出列表
showmount -e localhost
# 输出应类似:/data/k8s_storage 172.16.60.0/24

# 在 K8s 节点上测试手动挂载 (可选)
mount -t nfs 172.16.60.235:/data/k8s_storage /mnt
df -h | grep k8s_storage
umount /mnt

四、 配置 Keepalived 实现高可用

核心要点:配置为**非抢占模式 (nopreempt)**,避免 VIP 在节点恢复后频繁来回切换,可能导致数据不一致。

4.1 安装 Keepalived

在两台节点上执行:

1
yum install -y keepalived
4.2 配置 Keepalived (主备差异)
  • Master 节点 (/etc/keepalived/keepalived.conf):
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    ! Configuration File for keepalived
    global_defs {
    router_id nfs_master
    }
    vrrp_script chk_nfs {
    script "/etc/keepalived/nfs_check.sh"
    interval 2
    weight -20
    }
    vrrp_instance VI_1 {
    state BACKUP # 初始状态都设为 BACKUP
    interface eth0
    virtual_router_id 51
    priority 100 # Master 优先级更高
    advert_int 1
    nopreempt # 关键:非抢占模式
    authentication {
    auth_type PASS
    auth_pass 1111
    }
    track_script {
    chk_nfs
    }
    virtual_ipaddress {
    172.16.60.244/24
    }
    }
  • Slave 节点:配置文件与 Master 几乎相同,仅需修改两处:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    global_defs {
    router_id nfs_slave # 修改 router_id
    }
    ...
    vrrp_instance VI_1 {
    ...
    priority 80 # 降低优先级
    ...
    }
4.3 创建 NFS 健康检查脚本

在两台节点上创建 /etc/keepalived/nfs_check.sh

1
2
3
4
5
6
7
8
9
#!/bin/bash
A=`ps -C nfsd --no-header | wc -l`
if [ $A -eq 0 ]; then
systemctl restart nfs-server.service
sleep 2
if [ `ps -C nfsd --no-header | wc -l` -eq 0 ]; then
pkill keepalived # 如果重启失败,杀死 keepalived 触发 VIP 转移
fi
fi

赋予执行权限:chmod +x /etc/keepalived/nfs_check.sh

4.4 启动与验证 Keepalived
1
2
3
4
5
6
7
8
systemctl enable --now keepalived

# 查看 VIP 绑定情况
ip addr show eth0 | grep 172.16.60.244
# 输出类似:inet 172.16.60.244/32 scope global eth0

# 测试 VIP 可连通性
ping -c 3 172.16.60.244

故障转移测试

  1. 在持有 VIP 的 Master 节点上停止 keepalived:systemctl stop keepalived
  2. 观察 VIP 是否漂移到 Slave 节点 (ip addr 命令)。
  3. 注意:由于是非抢占模式,当原 Master 节点恢复后,VIP 不会自动漂回。

五、 配置 Rsync + Inotify 实现数据同步

核心逻辑只有当前持有 VIP 的节点,才启动 inotifywait 监控进程,将数据同步到对端。 避免双向同步导致数据循环和冲突。

5.1 安装软件包

在两台节点上执行:

1
yum install -y rsync inotify-tools
5.2 配置 Rsync 服务端
  • Master 节点 (/etc/rsyncd.conf):
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    uid = root
    gid = root
    use chroot = 0
    port = 873
    hosts allow = 172.16.60.0/24
    max connections = 0
    timeout = 300
    pid file = /var/run/rsyncd.pid
    lock file = /var/run/rsyncd.lock
    log file = /var/log/rsyncd.log
    log format = %t %a %m %f %b
    transfer logging = yes

    [master_web]
    path = /data/k8s_storage
    comment = master_web
    ignore errors
    read only = no
    list = no
    auth users = rsync
    secrets file = /etc/rsyncd.passwd
  • Slave 节点:配置与 Master 基本相同,仅修改模块名:
    1
    2
    3
    [slave_web]  # 修改此处
    path = /data/k8s_storage
    ...
5.3 配置 Rsync 认证

在两台节点上创建密码文件:

1
2
3
4
5
6
7
# 1. 服务端密码文件 (`/etc/rsyncd.passwd`)
echo "rsync:123456" > /etc/rsyncd.passwd
chmod 600 /etc/rsyncd.passwd

# 2. 客户端密码文件 (`/opt/rsyncd.passwd`,用于同步时认证对端)
echo "123456" > /opt/rsyncd.passwd
chmod 600 /opt/rsyncd.passwd
5.4 启动 Rsync 服务
1
2
systemctl enable --now rsyncd
ss -tlnp | grep 873 # 验证端口监听
5.5 配置自动同步脚本 (核心)

原理:每个节点都运行一个监控脚本 (vip_monitor.sh),定期检查本机是否持有 VIP。如果持有,则启动数据同步进程 (rsync_inotify.sh) 同步到对端;如果不持有,则杀死本机的同步进程。

  1. 创建数据同步脚本 (/opt/rsync_inotify.sh):

    • Master 节点内容 (同步到 Slave):
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      #!/bin/bash
      host=172.16.60.236 # Slave IP
      src=/data/k8s_storage/
      des=slave_web # Slave 的 rsync 模块名
      password=/opt/rsyncd.passwd
      user=rsync
      inotifywait=/usr/bin/inotifywait

      $inotifywait -mrq --timefmt '%Y%m%d %H:%M' --format '%T %w%f%e' \
      -e modify,delete,create,attrib $src \
      | while read files; do
      rsync -avzP --delete --timeout=100 --password-file=${password} $src $user@$host::$des
      echo "${files} was rsynced" >> /tmp/rsync.log 2>&1
      done
    • Slave 节点内容 (同步到 Master):只需修改 hostdes 变量为 Master 的信息。
  2. 创建 VIP 监控脚本 (/opt/vip_monitor.sh):

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    #!/bin/bash
    VIP_NUM=`ip addr | grep 244 | wc -l`
    RSYNC_INOTIRY_NUM=`ps -ef | grep /usr/bin/inotifywait | grep -v grep | wc -l`

    if [ ${VIP_NUM} -ne 0 ]; then
    # 本机持有 VIP
    if [ ${RSYNC_INOTIRY_NUM} -eq 0 ]; then
    # 如果同步进程未启动,则启动它
    nohup sh /opt/rsync_inotify.sh &
    fi
    else
    # 本机不持有 VIP
    if [ ${RSYNC_INOTIRY_NUM} -ne 0 ]; then
    # 如果同步进程正在运行,则杀死它
    pkill -f rsync_inotify.sh
    pkill -f inotifywait
    fi
    fi
  3. 创建持续运行脚本 (/opt/rsync_monit.sh):

    1
    2
    3
    4
    5
    6
    #!/bin/bash
    while [ "1" = "1" ]
    do
    /bin/bash /opt/vip_monitor.sh > /dev/null 2>&1
    sleep 5 # 每5秒检查一次VIP状态
    done
  4. 设置开机自启与启动

    1
    2
    3
    4
    5
    6
    7
    chmod +x /opt/*.sh
    # 将以下内容添加到 /etc/rc.local (并确保 rc.local 有执行权限)
    echo "nohup /bin/bash /opt/rsync_monit.sh &" >> /etc/rc.local
    chmod +x /etc/rc.d/rc.local

    # 立即启动
    nohup /bin/bash /opt/rsync_monit.sh &

六、 最终验证与总结

6.1 功能验证
  1. VIP 漂移与数据同步
    • 在 VIP 当前所在节点 (如 Master) 的 /data/k8s_storage 创建文件。
    • 观察文件是否自动同步到对端节点。
    • 停止当前节点的 keepalived,触发 VIP 漂移。
    • 在新的 VIP 持有节点上创建文件,观察同步是否反向进行。
  2. NFS 服务故障
    • 在 VIP 持有节点上强制停止 NFS 服务 (systemctl stop nfs-server)。
    • 观察 nfs_check.sh 脚本是否会重启 NFS,如果重启失败,VIP 是否漂移。
6.2 部署到 Kubernetes

在 K8s 的 PersistentVolume (PV) 定义中,使用 VIP (172.16.60.244) 作为 NFS 服务器地址。这样,无论后端哪台物理服务器实际提供服务,K8s 集群都能持续访问存储。

6.3 关键注意事项
  • 非抢占模式:是此架构稳定性的关键,避免脑裂和数据不一致。
  • 单向同步:确保同一时刻只有一端向另一端同步数据。
  • 网络与性能:主备节点间网络延迟会影响同步实时性。对于超大规模或IO密集型场景,需评估 rsync 性能是否满足。
  • 数据一致性rsync 不是分布式文件系统,在主备切换瞬间,如果数据未完全同步,可能存在极小时间窗口的数据不一致风险。对于极高一致性要求场景,需考虑更高级的存储方案(如 Ceph/GlusterFS)。
  • 安全:生产环境应使用更复杂的 rsync 密码,并考虑使用 SSH 隧道加密同步流量。