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 避免恢复即抢回的二次闪断、心跳与业务链路分离防脑裂。最后强调:搭完不算完,按第五节把进程故障、整机故障、恢复不抢占各演练一次并留记录,这套高可用才真正可用。







