做高可用负载均衡这些年,LVS-DR + Keepalived这个组合一直是我在生产环境里用得最多、也最放心的方案。它解决的核心问题就两个:一是流量怎么均匀分发到后端多台机器,二是负载均衡器自己挂了业务怎么不中断。这个部署我前阵子刚在生产环境完整做了一遍,整个过程从选型、配置到故障排查都有不少值得记录的地方,正好把完整思路整理出来,给准备做双机热备、或者想把单点负载均衡升级成高可用架构的同学一个参考。
这篇文章不是那种照抄官方文档的配置粘贴,我会把每个关键参数为什么这样写、每个坑是怎么踩出来的都讲清楚。无论你是刚接触 LVS 的新手,还是已经见过 LVS 但没亲手搭过主备模式的运维,这篇都能直接用上。
1. 部署前的方案选型与架构设计
1.1 为什么选 LVS-DR,而不是 NAT 或 TUN
LVS 有三种工作模式:NAT、DR、TUN。我之前在另一套环境里用的是 NAT 模式,那套环境后端服务器是跨网段的,没法走 DR;但这次部署的所有节点都在同一个二层网络,而且业务是典型的 HTTP 请求,我就直接锁定了 DR 模式。
先简单说下区别。NAT 模式下,请求进来负载均衡器要改目标 IP,回程报文还得原路返回给负载均衡器再转出去,相当于所有进出流量都压在 LB 上,流量一大 LB 就成了瓶颈。TUN 模式是把报文用 IPIP 隧道封装转发,它要求后端服务器支持隧道协议,网络配置也复杂一些。DR 模式不一样,它只改报文的目标 MAC 地址,负载均衡器把请求直接丢给后端真实服务器,后端服务器处理完直接把响应回给客户端,回程流量完全不经过 LB。
用生活里的例子类比,NAT 模式像是小区门卫,进出都要登记检查,人多了就排队;DR 模式像只检查进门的访客,出门不管,效率自然高出一个量级。这也是为什么 DR 模式特别适合那种“请求小响应大”的互联网业务——真实服务器回给客户端的页面、图片、接口数据都很大,但这些流量都不占用 LB 的带宽和处理能力。
但 DR 模式有个硬性前提:负载均衡器和后端真实服务器必须在同一个物理二层网络里,中间不能有路由器隔离,因为它们之间是靠 MAC 地址转发的。如果你们的网络环境跨了 VLAN,或者后端服务器在云上的不同子网,DR 模式就跑不通,这时候只能回头考虑 NAT 或者 TUN。
1.2 用 Keepalived 做双机热备的理由与模式取舍
没有 Keepalived 的 LVS 是很脆弱的。负载均衡器一旦宕机,所有后端服务器都收不到新请求,整个业务就直接瘫了。Keepalived 是目前最经典的开源双机热备软件,它用 VRRP 协议在主备两台 LB 之间维持心跳,主节点每隔一段时间发组播报文告诉备节点“我还活着”,连续几个周期收不到,备节点就立刻把 VIP(虚拟 IP)抢过来,同时把 LVS 规则也一并接管,实现秒级切换。
这里要说清楚一个容易混淆的点:Keepalived 做的是负载均衡器本身的高可用,它和 LVS 的后端健康检查是两回事。Keepalived 可以通过配置对后端真实服务器做 TCP、HTTP 健康检查,发现某台 RS 挂了就自动从 LVS 转发列表里摘掉,这个能力通常我们都直接用上,省掉另外部署监控脚本的麻烦。
双机热备有两种常见姿态:主备模式和双主模式。主备模式就是一台 MASTER 一台 BACKUP,同一时间只有一台持有 VIP 对外服务,另一台待命;双主模式是两台都分别持有一个 VIP,平时同时干活,对方挂了就把对方的 VIP 也接管过来。双主模式看起来资源利用率更高,但要求流量入口必须能把请求均分到两个 VIP 上,要么靠 DNS 轮询,要么靠交换机做 ECMP,复杂度明显上来了。生产环境求稳,我这次还是用最标准的主备模式,一台扛不住再谈扩容,先把故障切换做扎实。
1.3 整体架构与 IP 规划
这次的拓扑结构是这样的:客户端访问的是 VIP,VIP 正常情况下绑定在主负载均衡器 LB1 上;LB1 和 LB2 之间跑 VRRP 心跳;请求到达 LB 后,LVS 根据调度算法把请求转发给后端某一台真实服务器 RS。后端一共 3 台,都是 Nginx 提供 Web 服务。
IP 规划我列一下,方便后面配置对照:
| 角色 | 主机名 | IP 地址 | 说明 |
|---|---|---|---|
| 虚拟 IP | VIP | 10.0.100.100 | 对外提供服务,主备间漂移 |
| 主负载均衡器 | lb1 | 10.0.100.11 | Keepalived MASTER |
| 备负载均衡器 | lb2 | 10.0.100.12 | Keepalived BACKUP |
| 后端真实服务器 | rs1 | 10.0.100.31 | Nginx,权重 1 |
| 后端真实服务器 | rs2 | 10.0.100.32 | Nginx,权重 2 |
| 后端真实服务器 | rs3 | 10.0.100.33 | Nginx,权重 2 |
| 网关 | gw | 10.0.100.1 | 所有节点默认网关 |
注意一个细节:DR 模式下,LB 和 RS 的默认网关都指向同一个物理网关,回包才能直接出去。因为 RS 收到的是目标 IP 为 VIP 的请求,但自己的业务网卡 IP 是 10.0.100.31,它要根据路由表把响应报文从 eth0 发出去,默认网关必须是真实网络的网关,这一点后面配置 RS 的时候不能搞错。
2. 必须搞懂的核心参数与工作原理
2.1 DR 模式的关键:VIP 绑定与 ARP 抑制
DR 模式能跑通,靠的是两个关键动作:后端 RS 上要把 VIP 绑定到 lo 接口的 lo:0 别名上,同时必须抑制 ARP 响应。
为什么 RS 要绑定 VIP?因为负载均衡器转发过来的报文,目标 MAC 是 RS 的网卡 MAC,但目标 IP 是 VIP。RS 的内核收到这个包后,要判断这个 IP 是不是本机的,如果不是就直接丢弃。把 VIP 绑到 lo:0 上,相当于告诉内核“这个 VIP 我也认领了”,报文才能正常进入协议栈。这个绑定只影响收包,不影响 IP 地址配置,所以叫“在环回口上挂个虚拟地址”。
那为什么要抑制 ARP?如果不做任何处理,RS 绑定了 VIP 之后,当客户端或者网关广播询问“谁是 10.0.100.100”时,RS 也会回答“我是”,这样一来网络里就有多台机器声称自己拥有 VIP,交换机学到 MAC 地址后会把本该发给 LB 的包直接扔给某台 RS,负载均衡就完全失效了。抑制 ARP 的意图就是让 RS 只收包、不应答,对外保持“隐身”,让全网络只认 LB 的 MAC。
对应的 sysctl 参数是这两个:
net.ipv4.conf.all.arp_ignore = 1 net.ipv4.conf.all.arp_announce = 2 net.ipv4.conf.eth0.arp_ignore = 1 net.ipv4.conf.eth0.arp_announce = 2arp_ignore 设为 1,表示只回答目标 IP 是本接口 IP 的 ARP 请求;arp_ignoare 设为 2(应为 arp_announce=2)表示发送 ARP 应答时使用最优本地地址,避免把 VIP 当成源 IP 暴露出去。这里有个常见操作失误:只写了 all 没写 eth0,或者只写了 eth0 没写 all,导致抑制不彻底。保险做法是两个都写,这样不管报文从哪个接口进来,行为都是一致的。
2.2 Keepalived 主备配置的关键参数
Keepalived 的配置核心是 vrrp_instance,它定义了一个 VRRP 组。两个节点要组成一个高可用组,virtual_router_id 必须一致,这个 ID 范围是 1 到 255,在同一个二层网络里不能和其他 VRRP 组冲突,否则会互相干扰,出现莫名其妙的主备频繁切换。
优先级 priority 的范围是 1 到 254,数值越大越优先成为 MASTER。虽然 state 字段也写了 MASTER 和 BACKUP,但真正决定谁当主的是 priority。有人认为 state 写 MASTER 的节点就一定是主,其实不完全是,两台设备同时起来时,priority 高的先抢占,后面 state 为 BACKUP 的即使 priority 更高也不会主动抢(默认配置下),只有等当前 MASTER 挂了才会接管。理解这个逻辑,才不会在切换测试里被吓到。
advert_int 是 VRRP 心跳报文发送间隔,默认 1 秒。这个值不建议调太大,生产环境我通常保持默认,因为切换速度直接受它影响。按默认配置,备节点连续 3 个周期收不到主节点的心跳就会接管,也就是大约 3 秒。如果你把 advert_int 改成 0.5,切换可以更快,但也会让网络抖动更容易触发切换,属于用稳定性换速度,风险自担。
主备配置里还有个必须一致的坑:auth_pass 认证密码,两台要完全一样,而且旧版 Keepalived 要求不超过 8 位。新版虽然放宽了长度限制,但为了兼容不同版本,我建议还是控制在 8 位以内最稳。密码不一致或者超长,备节点会一直报错起不来,这是部署时非常经典的一个隐形故障点。
2.3 LVS 调度算法、会话保持与超时控制
网上流传的“等开销负载均衡”这个词,其实就是说 LVS 把流量尽量均匀地摊到每台后端服务器上。LVS 支持很多调度算法,生产环境我常用的就三种:rr(轮询)、wrr(加权轮询)、wlc(加权最少连接)。
rr 最好理解,来一个请求轮流发给下一台后端,适合后端配置完全一致的场景。wrr 在 rr 基础上加权重,适合后端机器配置有高低之分的场景,我这套环境里 rs2、rs3 配置高,权重给 2,rs1 配置低权重给 1。wlc 则是动态看每台后端当前的活跃连接数,把新请求发给连接数最少的那台,适合长连接或者请求处理时间差异大的场景。如果你的后端是大量短请求,rr 和 wrr 已经够用;如果后端请求耗时参差不齐,wlc 更合理。
会话保持也是一个必须提前想清楚的点。如果业务依赖 Session 或者登录态,比如用户登录后请求必须一直落在同一台后端上,LVS 就要开启持久性。在 Keepalived 的 virtual_server 配置里加 persistence_timeout 60,意思是同一个源 IP 在 60 秒内都会被转发到同一台 RS,单位是秒。但注意这是有代价的:持久性开得太长,会导致流量在几台 RS 之间分布严重不均,比如公司出口是同一个 NAT IP,成百上千人都算同一个源 IP,请求全被甩到一台机器上。所以这个参数能用则用,能短则短。
LVS 还维护着一张连接跟踪表,记录当前的 TCP/UDP 连接状态。默认超时策略在某些场景下会让表里的死连接堆积,比如 TCP 连接已经四次挥手断开了,但跟踪表里还认为它活着,占着连接数和后端资源。我习惯部署完成后主动调一下超时时间:
ipvsadm --set 120 30 30这三个数字分别对应 TCP 空闲超时、TCP FIN 等待超时和 UDP 超时,单位是秒。120 秒足够覆盖绝大多数 HTTP 请求,又不会让死连接占着茅坑不拉屎。
3. 从零开始的完整部署实操
3.1 环境准备与系统初始化
系统我用的是 CentOS 7.9,内核 3.10,LVS 的功能本来就编译在内核里,不需要额外打补丁。Ubuntu 20.04 这些发行版也完全没问题,安装的包名稍有区别,配置思路一模一样。两台 LB 上需要装 ipvsadm 和 keepalived,三台 RS 上装 Nginx 或者 httpd 都行,这里为了验证方便我用的是 Nginx,每台 RS 的默认页面写成不同的主机名,方便后面判断请求到底被转发到了哪台。
装包之前先把系统基础环境清干净:
# 两台 LB 上执行 yum install -y ipvsadm keepalived # 三台 RS 上执行 yum install -y nginx systemctl enable --now nginx然后确认 LVS 内核模块加载正常:
modprobe ip_vs lsmod | grep ip_vs能搜到 ip_vs 相关输出就说明内核支持没问题。如果没有输出,说明 ip_vs 没有编译进当前内核,需要检查内核模块目录里有没有对应文件,正常 CentOS 发行版内核都是带了的。
SELinux 我直接设成了 disabled,因为 LVS 转发和 Keepalived 绑定 VIP 这些操作在 enforcing 模式下容易出现权限类问题,生产环境如果必须开 SELinux,需要额外调布尔值,比较繁琐。防火墙我在内网环境直接关掉了,如果你是在有安全要求的网络里,需要放行 VRRP 协议(协议号 112)和 VIP 对应的业务端口,否则主备心跳会被防火墙拦掉,脑裂直接找上门。
3.2 后端真实服务器 RS 的配置
RS 的配置是整个部署成败的关键点,也是最容易被遗漏的地方。我在一台 RS 上执行的初始化脚本如下:
#!/bin/bash # 在每台 RS 上执行 # 第一步:把 VIP 绑定到 lo:0 ifconfig lo:0 10.0.100.100 netmask 255.255.255.255 up # 第二步:添加路由,确保回包不经过 lo:0 route add -host 10.0.100.100 dev lo:0 # 第三步:ARP 抑制 cat >> /etc/sysctl.conf <<EOF net.ipv4.conf.all.arp_ignore = 1 net.ipv4.conf.all.arp_announce = 2 net.ipv4.conf.eth0.arp_ignore = 1 net.ipv4.conf.eth0.arp_announce = 2 EOF sysctl -p这里有个特别容易忽略的细节:lo:0 的掩码必须写成 255.255.255.255,也就是 32 位掩码,而不是 24 位。如果写成 24 位掩码,RS 会觉得 VIP 和自己业务网卡 IP 在同一个网段,路由表会产生一条指向 lo 的网段路由,导致回包源地址和路由出现混乱,业务请求即使进来了也回不去。这个坑我踩过不止一次,每次排查到最后都是掩码问题。
为了让配置在重启后依然生效,建议把前两步写进 /etc/rc.local,或者更规范的做法是写一个 systemd service。生产环境我习惯写个独立脚本放在 /usr/local/bin/,然后用 systemd 管理,这样重启后行为可控,也方便单独查看状态。
配置完成后用两条命令验证:
ip addr show lo:0 cat /proc/sys/net/ipv4/conf/all/arp_ignore输出分别能看到 VIP 绑定和 arp_ignore 为 1 就说明 RS 侧准备工作完成。
3.3 主节点 LB1 的 Keepalived + LVS 配置
主节点 LB1 的 keepalived.conf 是整套配置的核心,我直接贴出完整的生产配置,逐段解释:
global_defs { router_id lb1 enable_script_security } vrrp_instance VI_1 { state MASTER interface eth0 virtual_router_id 51 priority 150 advert_int 1 nopreempt authentication { auth_type PASS auth_pass 12345678 } virtual_ipaddress { 10.0.100.100/24 dev eth0 label eth0:0 } } virtual_server 10.0.100.100 80 { delay_loop 6 lb_algo wrr lb_kind DR persistence_timeout 0 protocol TCP real_server 10.0.100.31 80 { weight 1 TCP_CHECK { connect_timeout 3 nb_get_retry 3 delay_before_retry 3 } } real_server 10.0.100.32 80 { weight 2 TCP_CHECK { connect_timeout 3 nb_get_retry 3 delay_before_retry 3 } } real_server 10.0.100.33 80 { weight 2 TCP_CHECK { connect_timeout 3 nb_get_retry 3 delay_before_retry 3 } } }global_defs 里的 router_id 只是本机标识,主备可以不一样,不影响选举。enable_script_security 是给后续可能用 notify 脚本时减少权限问题用的,现在写上不亏。
vrrp_instance 里的 nopreempt 我要专门说一下。加上这个参数后,即使主节点恢复了,也不会主动把 VIP 抢回来,VIP 会继续留在备节点上,直到备节点也挂了才切回。这能避免因为主节点重启、网络抖动导致的频繁主备切换,对业务更友好。但 nopreempt 只在 state 为 MASTER 的节点上配置生效,而且两台都要配相同的状态才协调——生产环境我的习惯是两台都配 nopreempt,保持行为一致。
virtual_server 块就是 LVS 的规则定义。lb_algo wrr 表示使用加权轮询算法,lb_kind DR 表示转发模式是 DR。delay_loop 6 是健康检查的间隔,每 6 秒检查一次后端 RS。real_server 里的 weight 对应我在 IP 规划里定的权重,TCP_CHECK 则是对后端 80 端口做 TCP 连接探测,连续 3 次失败就把这台 RS 摘除。
配置写好后先做语法检查再启动:
keepalived -t -f /etc/keepalived/keepalived.conf systemctl enable --now keepalived启动后立刻用两条命令看效果。ip addr 应该能看到 eth0 上挂了 10.0.100.100;ipvsadm -Ln 应该能看到三个 real_server 的转发规则。
3.4 备节点 LB2 的配置与 VRRP 心跳验证
备节点的配置和主节点高度相似,差异点极少,我用 diff 对比一下核心区别:
# LB1 与 LB2 配置差异 - state MASTER - priority 150 + state BACKUP + priority 100其他内容完全一致。注意 virtual_router_id 要相同,VIP 要相同,authentication 要相同,real_server 列表要相同。很多新手在备节点上习惯性地复制主配置,却忘了改 state 和 priority,结果两台都是 MASTER 或者两台 priority 一样,VRRP 选举就会出问题,表现为主备互相抢 VIP,业务时断时续。
备节点启动后,验证重点不是看 VIP 在不在,而是看它不在——正常情况下 VIP 只存在于主节点上。用 tcpdump 抓 VRRP 报文是最直观的验证方式:
tcpdump -i eth0 vrrp -n能持续看到主节点发的 VRRP 组播报文,说明主备心跳正常,备节点处于待命状态。如果两台 LB 都能抓到对方的 VRRP 包,说明组播没有被交换机过滤,这是后面高可用切换能正常工作的基础。
3.5 高可用切换与负载均衡效果验证
配置全部就位后,先用客户端连续请求 VIP,验证负载均衡效果:
for i in $(seq 1 10); do curl -s http://10.0.100.100/hostname; done我看到的输出是 rs1、rs2、rs3 轮流出现,rs2 和 rs3 出现的次数明显更多,正好符合权重 1:2:2 的预期。这一步能确认 LVS 转发链路是通的,RS 配置没有问题。
接下来做故障注入,验证高可用切换。在主节点上直接执行 systemctl stop keepalived,模拟主节点异常宕机。观察备节点的日志和 IP:
tail -f /var/log/messages ip addr show eth0备节点在 2 到 3 秒内会收到 MASTER 下线通知,然后把 VIP 配置到自己网卡上,LVS 规则也一并接管。这期间客户端如果正在发请求,会出现极短时间的连接失败或者超时,但下一个请求就恢复正常了,业务感知就是闪断一下。实测下来,Keepalived 默认配置下从主节点挂掉到 VIP 漂移完成,大约 2 到 3 秒,这个指标对绝大多数业务都是可以接受的。
再验证一下后端故障摘除:在 rs1 上把 Nginx 停掉,等一个健康检查周期,再执行 ipvsadm -Ln,能看到 10.0.100.31 这行的 Forward 状态变成了空,说明它已经被摘除。把 Nginx 重新拉起,等健康检查恢复,它又会自动回到转发列表里。整个负载均衡集群就具备了后端故障自愈能力。
4. 生产实战中的常见问题与排查技巧
4.1 keepalived exited with permanent error config 的完整处理
部署 Keepalived 时,最常见的启动报错就是这个 keepalived exited with permanent error config,意思是配置文件存在严重错误,进程直接拒绝启动。我处理过不少次这个问题,归纳下来原因就那么几类。
第一类是括号和缩进问题。Keepalived 配置对块结构要求严格,每个大括号闭合必须匹配,如果 virtual_server 块里少了一个右括号,或者 real_server 块嵌套位置错了,启动就会报错。这种错误用肉眼看很难发现,最好的办法就是执行语法检查:
keepalived -t -f /etc/keepalived/keepalived.conf它会明确告诉你在第几行附近出现语法错误,基本能定位到位置。
第二类是参数值越界。priority 范围是 1 到 254,写成 300 就会报错;virtual_router_id 范围是 1 到 255,写成 0 或者超过 255 都报错;auth_pass 如果超过当前版本限制也会报错。
第三类是名称冲突。vrrp_instance 的名称在同一台机器上不能重复定义,我见过有人一个配置文件里写了两遍 VI_1,Keepalived 直接拒绝启动。还有 vrrp_instance 的名称不要用特殊字符、不要用纯数字开头,老老实实用字母下划线和数字组合最安全。
第四类是 interface 写错。interface eth0 里的网卡名必须真实存在,如果你机器上的网卡叫 ens33 或者 enp0s3,写成 eth0 必然报错。这种问题在老虚拟机模板和新物理机上很常见,排查时先 ip addr 确认网卡名再写配置。
解决思路很简单:先跑一遍 keepalived -t 语法检查,按提示修正,再 systemctl start keepalived。不要跳过语法检查直接 systemctl start,那样报错信息更隐晦,排查效率低很多。
4.2 VIP 能 ping 通但业务不通的排查顺序
这个问题现象是:从客户端 ping VIP 能通,但 curl VIP 端口不通或者超时。按照我的经验,从三个层面逐层排查。
第一层看 LVS 规则。在 LB 上执行 ipvsadm -Ln,确认转发规则还在、real_server 列表里有没有被摘除、权重有没有被改成 0。如果规则没了,多半是 keepalived 重启时配置没生效,或者健康检查把 RS 全摘了。如果所有 RS 都被摘除,LVS 就没有可用的转发目标,VIP 的包进来会被丢弃。
第二层看 LB 到 RS 的连通性。在 LB 上直接 telnet RS 的 80 端口,确认 LB 和 RS 之间网络是通的。如果从 LB 能通但客户端不通,问题基本锁定在 RS 侧。
第三层看 RS 的 ARP 和 VIP 配置。执行 ip addr show lo:0 确认 VIP 绑定还在,执行 cat /proc/sys/net/ipv4/conf/all/arp_ignore 确认抑制参数没被重置。很多情况下是有人重启了 RS 但开机自启脚本没生效,或者改 sysctl.conf 后没有 sysctl -p,导致 ARP 参数丢失,客户端请求被 RS 的 VIP 应答抢走,流量根本到不了 LB。
还有一个容易被忽略的原因:RS 的防火墙。如果 RS 上开了 firewalld 且没有放行 VIP 的 80 端口,LVS 转发过来的请求会被 RS 本地防火墙丢掉,表现就是 VIP 能 ping 通但业务超时。排查时先临时 systemctl stop firewalld 试试,如果通了,再去配置放行规则。DR 模式下,RS 的默认网关如果配错,回包发不出去,也会造成请求超时,这个用 tcpdump 在 RS 上抓包看回包是否发出就能判断。
4.3 脑裂问题的识别与恢复
脑裂是双机热备里最危险的问题,它的本质是主备之间的 VRRP 心跳断了,但两台机器都还活着,于是备节点认为主节点挂了,把自己提升为 MASTER,最后出现两台机器同时持有 VIP、同时对外提供服务的状态。
脑裂的典型危害是流量被两个入口同时接收,后端 RS 上的连接状态混乱,客户端请求可能一会儿到主一会儿到备,整个集群行为不可预期。识别脑裂最简单的方法是在第三台机器上同时执行两次 ping VIP,然后看 ARP 表里 VIP 对应的 MAC 地址是不是发生了变化。如果在短时间内 VIP 的 MAC 在两个网卡 MAC 之间跳来跳去,基本可以断定发生了脑裂。
预防脑裂要从心跳链路的独立性入手。VRRP 组播报文走的是业务网卡,如果交换机端口做了隔离策略、或者防火墙把协议号 112 的报文过滤了,心跳就断了。我在生产上会再加一条独立的物理心跳线,让 VRRP 走专用接口,业务网断了也不影响主备通信。如果条件限制只能走一条链路,那就得保证交换机上 VRRP 组播畅通,并且不要随意配置端口隔离和风暴控制策略。
脑裂发生后的恢复策略也很讲究。不要单纯手动把备机重启,那样可能会让两边同时出现竞争。正确顺序是:确认主节点业务正常,在备节点上执行 systemctl stop keepalived,释放 VIP,然后检查主节点重新持有唯一 VIP,最后再启动备节点的 keepalived。如果配了 nopreempt,备节点恢复后不会抢 VIP,节奏更可控。
4.4 流量分配不均和会话保持的坑
部署完成初期,我发现流量分配并不像预期那样均匀,rs1 的连接数明显偏多。排查下来原因有两个,分享一下。
第一个原因是客户端和交换机 ARP 缓存残留。DR 模式下客户端发出的请求要先经过网关,网关 ARP 缓存里如果残留了旧 VIP 对应的 MAC,流量就会一直被送到某台特定的 RS 上(如果该 RS 曾经错误应答过 VIP 的 ARP)。解决办法是在 RS 上彻底确认 ARP 抑制生效,然后在网关或客户端上清一下 ARP 缓存,等缓存重新学习到 LB 的 MAC,流量分布就正常了。
第二个原因是 persistence_timeout 设置过长。我最初为了测试会话保持把这个参数设成了 600 秒,结果公司内网出口就一个公网 IP,所有测试请求看起来都是同一个源 IP,LVS 就把它们全部定向到了同一台 RS,后面几台机器几乎空闲。这就是“等开销负载均衡”最常被破坏的场景——不是 LVS 算法有问题,而是持久性配置把算法架空了。排查方法是临时把 persistence_timeout 设为 0,再观察流量分布;如果分布变均匀,就说明问题出在持久性配置上,需要结合实际业务把超时时间调到合理值。
还有一个和 DR 模式强相关的坑:LVS 的 ipvsadm 规则里看不到 any 端口的转发,因为 DR 模式下 LVS 只转发到目标端口,如果后端服务的端口改了,但 keepalived 的 virtual_server 配置没同步改,流量会被 LVS 原样扔到 RS 的错误端口上。每次改后端端口,记得同步更新 keepalived 配置,这是一条非常朴素的运维经验。
踩过几次坑之后,我现在的部署习惯已经很固定了:所有 RS 先跑一遍 VIP 绑定和 ARP 抑制脚本并做开机自启,然后主备节点分别写配置、跑 keepalived -t 语法检查再启动,最后用 tcpdump 和 ipvsadm 两层验证。这套 LVS-DR + Keepalived 的方案陪着我扛过了不少业务高峰,四层高可用负载均衡里它依然是开源场景下最稳的组合。如果你刚做完部署,最后再提醒一件事:切换测试时记得观察客户端侧的业务日志,负载均衡层切换再快,业务层如果没做连接重试,一次闪断也可能造成报错;把客户端的超时重试机制加上,这套架构才算是真正完整闭环。