Keepalived + HAProxy 高可用负载均衡部署实战

单机跑 HAProxy 做负载均衡有个绕不过去的问题:HAProxy 自己就是单点,它一挂,后面再多后端也访问不了。经典解法是两台机器各跑一份 HAProxy,再用 Keepalived 维护一个虚拟 IP(VIP),VIP 在哪台机器上,流量就进哪台。本文把这套方案完整搭一遍:HAProxy 的 frontend/backend 与健康检查、Keepalived 的 VRRP 实例与优先级、用脚本探测 HAProxy 存活实现自动切换,并讨论脑裂防护与切换演练。适用环境为任意主流 Linux 发行版,两台机器处于同一二层网段(VRRP 需要同网段通信)。

前置条件

  • 两台机器 node-01(192.0.2.11)与 node-02(192.0.2.12),同网段互通,已规划一个空闲的 VIP(192.0.2.100);
  • 后端应用服务器至少两台(下文用 192.0.2.21 与 192.0.2.22,监听 8080);
  • 两台机器均可安装 keepalived 与 haproxy,时间已同步(查日志对照时间线时很重要);
  • 清楚 VIP 网段的网络策略:部分云环境禁用 VRRP 多播或需申请高可用 VIP,云上部署前先确认,本文默认自建机房/虚拟化环境。

一、架构:VIP 漂移与流量分工

分工先讲清,后面的配置才不容易乱。Keepalived 只负责一件事:决定 VIP 这一刻绑在哪台机器上。它基于 VRRP 协议,两台以优先级选举 MASTER,MASTER 持有 VIP 并周期性发通告,BACKUP 收不到通告就接管。HAProxy 只负责流量:请求打到 VIP 后进入本机 HAProxy,由它做七层分发或四层透传,并持续对后端健康检查、自动摘除故障节点。两个组件的健康是联动的:HAProxy 进程死了,即使机器还活着,VIP 也应漂走——这是靠 Keepalived 的脚本探测实现的,是本文最关键的一处配置。

二、HAProxy 配置

两台 HAProxy 的配置要完全一致:主备不一致,切换后行为变化会引发二次故障。下面示例用七层 HTTP 模式分发到两台后端,带健康检查与统计页面。

# /etc/haproxy/haproxy.cfg(两台机器保持一致)
global
    log /dev/log local0
    maxconn 4000
    daemon

defaults
    mode http
    log global
    option httplog
    timeout connect 5s
    timeout client  30s
    timeout server  30s
    retries 3

frontend web_in
    bind *:80
    default_backend web_pool

backend web_pool
    balance roundrobin
    option httpchk GET /healthz
    http-check expect status 200
    server app01 192.0.2.21:8080 check inter 3s fall 3 rise 2
    server app02 192.0.2.22:8080 check inter 3s fall 3 rise 2

# 统计页面只绑定本机回环,配合 SSH 隧道查看,避免暴露到公网
listen stats
    bind 127.0.0.1:8404
    stats enable
    stats uri /stats
    stats refresh 10s

几个参数要理解透:inter/fall/rise 决定摘除与恢复速度,fall 3、inter 3s 意味着后端故障后约 9 秒被摘除,太敏感会因抖动误摘;option httpchk 用应用的 /healthz 接口做七层检查比 TCP 探活可靠,后端活着但假死时 TCP 检查是查不出来的。需要四层透传(如数据库、Redis)时,把 mode 改为 tcp、健康检查改为 option tcp-check 即可。改完先 haproxy -c -f /etc/haproxy/haproxy.cfg 校验再 reload。

三、Keepalived 配置:VRRP 实例与抢占取舍

Keepalived 的核心配置是 VRRP 实例:两台共用同一个 virtual_router_id 与 VIP,靠 priority 分主备,state 只是初始状态,真正的角色由优先级选举决定。抢占模式(默认)下,原 MASTER 恢复后高优先级节点会把 VIP 抢回来,好处是角色与规划一致,坏处是刚恢复的机器可能不稳,来回漂移造成两次闪断;非抢占(nopreempt)让 VIP 留在当前持有者上、只在其故障时才切换,生产更推荐。要注意 nopreempt 在 state 为 BACKUP 时才符合预期,通常两台都写 BACKUP + nopreempt,由优先级决定首任 MASTER。

# node-01 /etc/keepalived/keepalived.conf
global_defs {
    router_id LB_NODE_01
    script_user root
    enable_script_security off
}

# 探活脚本:HAProxy 进程不在,就降低本机优先级,触发 VIP 漂移
vrrp_script chk_haproxy {
    script "/etc/keepalived/check_haproxy.sh"
    interval 2
    weight -30
    fall 2
    rise 2
}

vrrp_instance VI_WEB {
    state BACKUP
    interface eth0
    virtual_router_id 51
    priority 110
    advert_int 1
    nopreempt
    authentication {
        auth_type PASS
        auth_pass change-me-123
    }
    virtual_ipaddress {
        192.0.2.100/24 dev eth0
    }
    track_script {
        chk_haproxy
    }
}

node-02 只需改三处:router_id 改为 LB_NODE_02、priority 改为 100、其余一致。auth_pass 是 VRRP 的弱口令,主要防误配串扰,不作安全边界。探活脚本 weight 取 -30 的设计是:node-01 正常时 110 高于 node-02 的 100,HAProxy 挂掉后降为 80,VIP 自动漂移;fall 2 表示连续两次探测失败才生效,避免单次抖动误切。

# /etc/keepalived/check_haproxy.sh —— 记得 chmod +x
#!/bin/bash
# 进程存在且本机 80 端口可连通,才算 HAProxy 真正可用
pgrep -x haproxy >/dev/null 2>&1 || exit 1
curl -s -o /dev/null -m 2 http://127.0.0.1:80/ || exit 1
exit 0

四、脑裂的成因与防护

脑裂指两台都认为自己是 MASTER、同时持有 VIP,客户端 ARP 指向混乱,流量随机落到两边,表现为间歇性异常。成因主要是 VRRP 通告到不了对端:网卡或交换机故障、二层链路中断、防火墙拦了 VRRP 协议(协议号 112)或多播地址 224.0.0.18。防护分三层:链路层,心跳走独立网卡或 VLAN,与业务流量分离;协议层,环境不支持多播时改用 unicast_peer 单播点对点发通告;判定层,在探活脚本里叠加对端是否可达与 VIP 是否已被对端持有的交叉检查,避免网络分区时双双自立。另有个常见坑:virtual_router_id 与同网段其他 VRRP 组冲突会互相干扰,规划时统一登记 router_id。

# 不支持多播的环境,在 vrrp_instance 中改用单播(两台互填对方地址)
    unicast_src_ip 192.0.2.11
    unicast_peer {
        192.0.2.12
    }

五、切换演练流程

高可用方案没演练过就等于没有。标准演练按预期、执行、观察、回退走一遍,全程记录时间线。第一步基线确认:VIP 在 node-01,用 ip addr 确认绑定,并持续用脚本请求 VIP 观察成功率与延迟。第二步模拟进程故障:在 node-01 停掉 HAProxy,预期 VIP 在数秒内(由 advert_int 与脚本 fall 周期决定,约 5 秒量级)漂到 node-02,请求短暂失败后恢复。第三步模拟整机故障:关 node-01 网卡或关机,验证 VIP 同样能漂移。第四步恢复验证:拉起 node-01 的 HAProxy,因配了 nopreempt,VIP 不应抢回,确认无二次闪断。第五步复盘:核对切换时长与失败请求数是否可接受,写进演练记录。演练在业务低峰执行并提前通知相关方。

# 演练观察命令组
ip addr show eth0 | grep 192.0.2.100          # 确认 VIP 当前归属
journalctl -u keepalived -f                  # 观察角色转换日志
systemctl stop haproxy                       # 模拟进程故障
curl -s -o /dev/null -w '%{http_code}\n' http://192.0.2.100/   # 从第三方机器验证 VIP 可用性
arping -I eth0 -c 3 192.0.2.100              # 必要时主动刷新邻居 ARP

验证

  • 稳态正确:VIP 只在一台机器上出现(两台分别执行 ip addr 核对),VIP 请求成功且被分发到两台后端(统计页里两台后端的会话计数都在增长);
  • 切换达标:停掉 MASTER 上的 HAProxy 后,VIP 在预期时间内漂到对端,持续请求脚本统计的失败数在可接受范围;
  • 不抢占符合预期:恢复原 MASTER 后 VIP 不回漂,手动在两台执行 keepalived -t 校验配置无语法错误。

常见问题

  • VIP 不漂移。先看 Keepalived 日志有没有收到对端通告:通告不通是网卡、防火墙拦 VRRP 或多播被禁的典型症状,先抓包看 224.0.0.18 或协议 112 有没有到达;再查探活脚本是否可执行、script_user 与 enable_script_security 是否导致脚本被拒。
  • 出现双主(脑裂)。立即保留一台、停掉另一台的 Keepalived 止血,再按第四节逐项排查通告链路;在查清前不要让两台同时运行。
  • VIP 已漂移但客户端仍访问旧机器。ARP 缓存未刷新:Keepalived 切换时默认会发免费 ARP,多数客户端会更新;个别老设备或路由器 ARP 学习慢,可在其上清缓存,或确认交换机没有拦截免费 ARP。
  • HAProxy 健康检查把后端全摘了。多是检查路径或期望状态码写错:/healthz 返回的不是 200 或后端有重定向。用 curl 在 HAProxy 机器上请求后端检查地址复现,再对着 http-check expect 逐项核对。

小结

分工可以概括为:Keepalived 管 VIP 在谁家,靠优先级选举加脚本探活实现故障漂移;HAProxy 管流量去哪台后端,靠一致的双机配置与合理的健康检查参数。落地三条纪律不要省:主备配置一致、用 nopreempt 避免恢复即抢回的二次闪断、心跳与业务链路分离防脑裂。最后强调:搭完不算完,按第五节把进程故障、整机故障、恢复不抢占各演练一次并留记录,这套高可用才真正可用。

发表回复

后才能评论