Keepalived 高可用配置实战

Keepalived 高可用配置实战

概述

在现代互联网架构中,服务的高可用性(High Availability, HA)是保障业务连续性的核心要求。当单点服务(如 Nginx 反向代理、MySQL 数据库、API 网关等)发生故障时,如何实现流量的无缝切换,避免业务中断,是每一位运维工程师必须面对的课题。

Keepalived 正是解决这一问题的成熟方案。它基于 VRRP(Virtual Router Redundancy Protocol,虚拟路由冗余协议)实现 IP 地址的漂移,配合自定义健康检查脚本,能够在主节点故障时自动将虚拟 IP(VIP)切换到备用节点,整个过程对客户端透明。

本文将围绕以下内容展开:

  • VRRP 协议的工作原理
  • Keepalived 的安装与基本配置
  • 健康检查脚本的编写与集成
  • 主备(Master/Backup)故障切换实战
  • Keepalived 结合 Nginx 和 MySQL 的典型场景

通过阅读本文,你将掌握 Keepalived 从原理到生产部署的完整流程。

VRRP 协议原理

什么是 VRRP

VRRP 是一种容错协议,它将一组运行在局域网内的路由器(或服务器)组成一个虚拟路由器,对外暴露一个虚拟 IP 地址。在这个虚拟路由器组中:

  • Master 节点:当前承载虚拟 IP 的节点,负责响应发往该 VIP 的所有请求。
  • Backup 节点:处于待命状态的节点,持续监听 Master 的心跳通告。一旦 Master 失效,优先级最高的 Backup 将接管 VIP。

VRRP 通过 RFC 5798 标准化,Keepalived 是其 Linux 平台最为流行的实现。

VRRP 工作流程

  1. 虚拟路由器组内的所有节点选举出一个 Master(根据优先级 priority 决定)。
  2. Master 周期性(默认每秒)发送 VRRP 通告(Advertisement)报文到多播地址 224.0.0.18。
  3. Backup 节点持续监听通告报文。如果在 advert_int * 3 + skew_time 时间内未收到 Master 的通告,则认为 Master 已宕机。
  4. Backup 节点中优先级最高的一个抢占成为新的 Master,接管虚拟 IP 并发送免费 ARP 报文,通知局域网内的其他设备更新 ARP 缓存。

Keepalived 的扩展能力

与标准的 VRRP 实现不同,Keepalived 引入了 vrrp_script 机制,允许通过自定义脚本对服务进行实时健康检查。当脚本检测到服务异常时,可以动态降低当前节点的优先级,从而主动触发主备切换。这使得 Keepalived 不仅能够应对服务器级别的故障,还能处理进程级别的异常。

Keepalived 安装

在 CentOS/RHEL 系统上,Keepalived 可直接通过 yum 安装:

yum install -y keepalived

对于 Ubuntu/Debian 系统:

apt-get install -y keepalived

安装完成后,主配置文件位于 /etc/keepalived/keepalived.conf,启动与管理命令如下:

# 启动
systemctl start keepalived

# 设置开机自启
systemctl enable keepalived

# 查看运行状态
systemctl status keepalived

# 热加载配置(不中断服务)
kill -HUP $(cat /var/run/keepalived.pid)

其中热加载(reload)命令在生产环境中尤为重要:它允许你在不重启 Keepalived 进程、不触发 VIP 漂移的情况下,使配置变更生效。

配置文件详解

Keepalived 的配置文件使用其独有的语法格式,以 ! 开头表示注释行(非标准 Shell 风格的 #)。配置文件主要由 global_defsvrrp_scriptvrrp_instance 三大块组成。

global_defs——全局定义

global_defs {
   notification_email {
     admin@example.com
   }
   notification_email_from keepalived@example.com
   smtp_server 127.0.0.1
   smtp_connect_timeout 30
   router_id LVS_DEVEL
}

关键参数说明:

参数说明
notification_email告警邮件接收地址列表,Master/Backup 切换时触发
notification_email_from告警邮件的发件人地址
smtp_serverSMTP 邮件服务器地址
smtp_connect_timeoutSMTP 连接超时时间(秒)
router_id当前 Keepalived 节点的唯一标识,在同一局域网内应保持唯一

在云环境或容器化部署中,SMTP 告警通常被替换为自定义的通知脚本或监控系统(如 Prometheus AlertManager),因此在生产环境中 notification_email 相关配置并非必需。

vrrp_script——健康检查脚本

这是 Keepalived 最强大的特性之一。通过定义 vrrp_script,你可以让 Keepalived 周期性执行一个脚本,并根据脚本的返回值动态调整节点优先级。

vrrp_script chk_service {
    script "/etc/keepalived/check_service.sh"
    interval 1          # 检查间隔,单位秒
    weight -2           # 优先级调整权重
    fall 3              # 连续失败多少次后判定为异常
    rise 2              # 连续成功多少次后恢复为正常
}

参数说明:

参数说明
script要执行的检查脚本路径
interval执行间隔,单位秒,默认 1 秒
weight优先级调整值。脚本失败时当前 priority 减去该绝对值,脚本恢复时加回
fall需要连续失败多少次才判定为 FAULT 状态
rise需要连续成功多少次才从 FAULT 恢复为正常

脚本约定:返回 0 表示正常,返回非 0 表示异常。weight 值直接影响优先级计算。假设当前节点的 priority 为 100,weight 为 -2,则脚本连续失败 fall 次后,有效优先级降至 98。若此时 Backup 节点的优先级为 99,则 Backup 会抢占成为 Master。

健康检查脚本示例

以下是一个检查 Nginx 进程是否存活的 Shell 脚本:

#!/bin/bash
# /etc/keepalived/check_nginx.sh
counter=$(ps -C nginx --no-heading | wc -l)
if [ "${counter}" -eq "0" ]; then
    # Nginx 进程不存在,尝试重启
    systemctl start nginx
    sleep 2
    counter=$(ps -C nginx --no-heading | wc -l)
    if [ "${counter}" -eq "0" ]; then
        # 重启失败,通知 Keepalived 降低优先级
        exit 1
    fi
fi
exit 0

对于 MySQL 服务,可以结合 mysqladmin ping 命令进行检查:

#!/bin/bash
# /etc/keepalived/check_mysql.sh
mysqladmin -u root -p'your_password' ping 2>/dev/null | grep -q "mysqld is alive"
if [ $? -ne 0 ]; then
    exit 1
fi
exit 0

在生产环境中,你也可以使用 Python 等语言编写更为复杂的检查逻辑,例如检查 API 接口的 HTTP 状态码、验证数据库读写能力等。

vrrp_instance——VRRP 实例

vrrp_instance 是 Keepalived 配置的核心,它定义了一个 VRRP 虚拟路由器。

vrrp_instance VI_1 {
    state MASTER              # 初始状态:MASTER 或 BACKUP
    interface eth0            # 绑定网卡
    virtual_router_id 51     # VRRP 路由器 ID(0-255),同一组节点必须一致
    priority 100             # 优先级(1-255),值越大优先级越高
    advert_int 1             # VRRP 通告间隔,单位秒
    authentication {
        auth_type PASS       # 认证类型:PASS(密码)或 AH
        auth_pass MyPassword # 认证密码,同一组节点必须一致(最长 8 字符)
    }
    virtual_ipaddress {
        192.168.1.100/24     # 虚拟 IP 地址
    }
    track_script {
        chk_service          # 关联健康检查脚本
    }
}

关键配置点:

  • state:MASTER 和 BACKUP 仅为初始状态。实际运行中,如果多台节点同时启动且均配置为 MASTER,则通过 priority 值竞选。state 并不强制角色。
  • virtual_router_id:同一 VRRP 组内所有节点的该值必须相同,否则节点间无法互相通告。
  • priority:优先级数字越大越优先。在多节点场景下,通常给期望成为 Master 的节点分配最高优先级。
  • advert_int:通告报文发送间隔。值越小,切换速度越快,但 CPU 和网络开销也相应增加。
  • track_script:关联前面定义的 vrrp_script,使健康检查结果直接影响本实例的优先级。

双 VIP 主备互切实战

在实际生产部署中,两台服务器往往需要同时提供服务,充分利用硬件资源。下面是一个经典的双 VIP 主备互切方案:两台服务器各承载一个 VIP 的 Master 角色,互为 Backup,实现负载分担与高可用的统一。

架构拓扑:

Server A (10.241.47.100)                 Server B (10.241.47.101)
┌─────────────────────────┐              ┌─────────────────────────┐
│  VIP1: 10.241.47.127    │              │  VIP1: 10.241.47.127    │
│  state: MASTER (pri=100)│◄── VRRP ───►│  state: BACKUP (pri=99) │
│                         │              │                         │
│  VIP2: 10.81.79.104     │              │  VIP2: 10.81.79.104     │
│  state: BACKUP (pri=99) │              │  state: MASTER (pri=100)│
└─────────────────────────┘              └─────────────────────────┘

正常运行时,Server A 承载 VIP1,Server B 承载 VIP2,两台服务器各自处理业务流量。当 Server A 宕机后,VIP1 漂移到 Server B,Server B 同时承载两个 VIP;反之亦然。

Server A 配置(主节点视角)

! Configuration File for keepalived

global_defs {
   notification_email {
     admin@example.com
   }
   notification_email_from keepalived@example.com
   smtp_server mail.example.com
   smtp_connect_timeout 30
   router_id ServerA
}

vrrp_script chk_gateway_port {
       script "/etc/keepalived/check_gateway.py"
       interval 1
       weight -2
       fall 3
       rise 2
}

vrrp_instance VI_Gateway1 {
    state MASTER
    interface bond6
    virtual_router_id 231
    priority 100
    advert_int 1
    authentication {
        auth_type PASS
        auth_pass gateway_vip1
    }
    virtual_ipaddress {
        10.241.47.127/24
    }
    track_script {
       chk_gateway_port
    }
}

vrrp_instance VI_Gateway2 {
    state BACKUP
    interface bond6
    virtual_router_id 232
    priority 99
    advert_int 1
    authentication {
        auth_type PASS
        auth_pass gateway_vip2
    }
    virtual_ipaddress {
        10.81.79.104/24
    }
    track_script {
       chk_gateway_port
    }
}

Server B 配置(备节点视角)

! Configuration File for keepalived

global_defs {
   notification_email {
     admin@example.com
   }
   notification_email_from keepalived@example.com
   smtp_server mail.example.com
   smtp_connect_timeout 30
   router_id ServerB
}

vrrp_script chk_gateway_port {
       script "/etc/keepalived/check_gateway.py"
       interval 1
       weight -2
       fall 3
       rise 2
}

vrrp_instance VI_Gateway1 {
    state BACKUP
    interface bond6
    virtual_router_id 231
    priority 99
    advert_int 1
    authentication {
        auth_type PASS
        auth_pass gateway_vip1
    }
    virtual_ipaddress {
        10.241.47.127/24
    }
    track_script {
       chk_gateway_port
    }
}

vrrp_instance VI_Gateway2 {
    state MASTER
    interface bond6
    virtual_router_id 232
    priority 100
    advert_int 1
    authentication {
        auth_type PASS
        auth_pass gateway_vip2
    }
    virtual_ipaddress {
        10.81.79.104/24
    }
    track_script {
       chk_gateway_port
    }
}

配置要点总结:

  1. 每个 VRRP 实例(virtual_router_id 231 / 232)在两个节点上的角色(state)和优先级(priority)互为镜像。
  2. 认证密码 auth_pass 在同一个 VRRP 实例的两端必须完全一致,否则节点间无法正常通信。
  3. 两个 VRRP 实例共享同一个 vrrp_script 检测脚本,确保任一服务异常都能触发 VIP 漂移。
  4. fall 3rise 2 的设置可以避免因网络抖动造成的频繁切换(flapping)。

Keepalived 结合 Nginx 的高可用方案

Nginx 作为反向代理和负载均衡器,是企业架构中常见的流量入口。通过 Keepalived 为 Nginx 提供 VIP 漂移,可以消除 Nginx 的单点故障。

方案架构

            ┌───────────┐
            │  Client   │
            └─────┬─────┘
                  │
            ┌─────▼─────────────────┐
            │   Virtual IP (VIP)    │
            │   10.241.47.127       │
            └─────┬───────┬─────────┘
                  │       │
        ┌─────────▼──┐ ┌──▼─────────┐
        │  Nginx A   │ │  Nginx B   │
        │ (Master)   │ │ (Backup)   │
        └─────┬──────┘ └──────┬─────┘
              │               │
        ┌─────▼───────────────▼─────┐
        │     Backend Servers       │
        └───────────────────────────┘

Nginx 健康检查脚本

#!/bin/bash
# /etc/keepalived/check_nginx.sh

# 检查 Nginx 进程是否存在
if ! pidof nginx &>/dev/null; then
    # 尝试启动 Nginx
    /usr/sbin/nginx -c /etc/nginx/nginx.conf
    sleep 1
    if ! pidof nginx &>/dev/null; then
        # 启动失败,告知 Keepalived 切换
        exit 1
    fi
fi
exit 0

在 Keepalived 配置中关联:

vrrp_script chk_nginx {
    script "/etc/keepalived/check_nginx.sh"
    interval 2
    weight -5
    fall 2
    rise 1
}

vrrp_instance VI_Nginx {
    state MASTER
    interface eth0
    virtual_router_id 51
    priority 100
    advert_int 1
    authentication {
        auth_type PASS
        auth_pass nginx_ha
    }
    virtual_ipaddress {
        10.241.47.127/24
    }
    track_script {
        chk_nginx
    }
}

关键细节:非抢占模式

在某些场景下,你可能希望 Primary 节点恢复后不要立即抢回 VIP(避免二次切换造成连接中断)。此时可以在 vrrp_instance 中添加 nopreempt 参数,并配合 state BACKUP 使用(因为 nopreempt 仅在 state 为 BACKUP 时生效):

vrrp_instance VI_Nginx {
    state BACKUP      # nopreempt 仅在 BACKUP 状态下生效
    nopreempt
    priority 100
    # ... 其余配置 ...
}

Keepalived 结合 MySQL 的高可用方案

对于 MySQL 主从复制的架构,Keepalived 可以确保应用始终将读写请求指向当前的主库 VIP。

MySQL 健康检查脚本

#!/bin/bash
# /etc/keepalived/check_mysql.sh

MYSQL_USER="root"
MYSQL_PASS="your_password"
MYSQL_HOST="localhost"

mysqladmin -u${MYSQL_USER} -p${MYSQL_PASS} -h${MYSQL_HOST} ping 2>/dev/null \
    | grep -q "mysqld is alive"

if [ $? -ne 0 ]; then
    exit 1
fi

# 可选:进一步检查是否为可写状态(只读时为从库角色)
READ_ONLY=$(mysql -u${MYSQL_USER} -p${MYSQL_PASS} -h${MYSQL_HOST} \
    -e "SELECT @@read_only;" -sN 2>/dev/null)
if [ "${READ_ONLY}" -eq "1" ]; then
    exit 1
fi

exit 0

该脚本不仅检测 MySQL 进程是否存活,还通过 SELECT @@read_only 判断当前实例是否为可写主库。当 MySQL 主从发生切换导致旧主库变为只读时,该脚本会返回失败信号,Keepalived 随即降低优先级并触发 VIP 漂移,使 VIP 始终指向可写的 MySQL 实例。

运维与故障排查

查看 VIP 状态

# 在 Master 节点上查看 VIP 是否绑定
ip addr show bond6 | grep 10.241.47.127

# 查看 Keepalived 日志
journalctl -u keepalived -f

验证 VRRP 通信

# 通过 tcpdump 抓取 VRRP 通告报文
tcpdump -i bond6 vrrp -n

如果 Backup 节点无法收到 Master 的通告报文,请检查:

  • 两台服务器之间的网络连通性
  • 防火墙是否放行了 VRRP 协议(IP 协议号 112)
  • virtual_router_idauth_pass 是否一致

模拟故障切换

# 方法一:手动停止 Keepalived
systemctl stop keepalived

# 方法二:关闭网卡接口
ip link set bond6 down

# 方法三:手动杀死被监控的服务
systemctl stop nginx

在 Backup 节点观察日志,journalctl -u keepalived -f 应输出类似 Entering MASTER STATE 的日志信息,并使用 ip addr show 确认 VIP 已绑定到 Backup 节点。

处理脑裂(Split-Brain)

脑裂是 HA 方案中的常见风险,表现为两台节点同时认为自己是 Master,双端同时绑定同一 VIP,导致 IP 冲突和网络异常。预防措施:

  1. 严格保证 VRRP 通信:确保 VRRP 多播报文在节点间可达,必要时使用独立的网卡或 VLAN 传输心跳。
  2. 合理设置优先级差:主备节点的 priority 差值建议不小于 5,避免因微小的优先级波动导致切换。
  3. 使用仲裁脚本:在健康检查脚本中加入对第三方仲裁节点(如网关 IP)的 ping 检测。如果自身存活但无法 ping 通网关,说明自身网络可能出现异常,应主动释放 VIP,避免脑裂。
#!/bin/bash
# 仲裁检测:检查到网关的连通性
GATEWAY="192.168.1.1"
ping -c 2 -W 1 ${GATEWAY} &>/dev/null
if [ $? -ne 0 ]; then
    # 网关不通,自身网络可能异常,放弃 VIP
    exit 1
fi
exit 0

总结

Keepalived 作为一款轻量级、高性能的高可用解决方案,已经广泛应用于各类生产环境中。总结本文的关键要点:

  1. VRRP 协议是 Keepalived 的基石,通过虚拟 IP 和优先级选举实现透明的故障切换。
  2. vrrp_script 健康检查机制让 Keepalived 能够感知服务层面的异常,而不仅仅是服务器宕机。
  3. 双 VIP 主备互切方案在保证高可用的同时,充分利用了硬件资源,适合大多数中小规模部署。
  4. 结合 Nginx/MySQL 等关键服务时,务必将健康检查脚本与服务实际状态挂钩(如 Nginx 进程检测、MySQL 可写状态检测)。
  5. 运维层面需关注脑裂防护、网络连通性和防火墙规则,确保 VRRP 协议报文畅通。

合理的 Keepalived 配置加上全面的监控告警体系,能够为你的核心业务构建一道坚实的高可用防线。在生产环境部署前,务必在测试环境中充分验证各种故障场景下的切换行为,做到心中有数,遇事不慌。


本文基于 Keepalived 2.x 版本编写,部分参数在不同版本中可能存在差异,请以官方文档为准。

暂无评论