☰
LVS-DR + Keepalived 高可用负载均衡架构详解与实战
2026/10/1 6:10:06 网站建设 项目流程

1. 为什么选 LVS-DR + Keepalived?先把架构账算清楚

1.1 负载均衡的本质:流量怎么才算“被平均”

很多刚接触高可用的人会把注意力全放在负载均衡器本身,觉得只要前面挂了台转发设备,请求就会自动分散到后端。其实负载均衡的核心问题有两个:一是“流量怎么分”,二是“分了之后后端能不能正常回包”。这两件事看着简单,实际牵扯到协议栈的行为、ARP 的响应机制、甚至内核参数的调整。很多部署翻车,都是栽在第二件事上。

我在生产环境里做过几轮选型对比,最终固定使用 LVS-DR + Keepalived 这套组合。LVS 工作在 Linux 内核的 IPVS 模块上,转发效率非常高,不像 Nginx 那样要经过用户态和内核态之间反复拷贝。DR 模式的全称是 Direct Routing,数据包进来之后,LVS 只负责改二层 MAC 地址,把请求原封不动转发给后端的真实服务器,回包由真实服务器直接返回给客户端。这意味着负载均衡器基本不承载回程流量,压力小很多,瓶颈自然也就低。

1.2 Keepalived 的角色:它不只是“看门狗”

Keepalived 在这套方案里的作用很容易被低估。很多人以为它只是监控 LVS 进程挂了就重启,其实核心是它的 VRRP 实现。VRRP 协议让多台 LVS 节点共享一个虚拟 IP,主节点周期性发送心跳报文,备份节点收不到心跳后就会接管 VIP,整个过程对客户端透明。

Keepalived 还会主动探测后端真实服务器的健康状态,探测失败了就从 LVS 转发表里摘掉该节点,恢复了再重新加回来。这个能力非常关键,因为 LVS 本身不具备后端健康检查功能,如果没有 Keepalived,后端某台机器挂了,LVS 依然会把请求调度过去,那部分用户就直接断连。

1.3 和 Nginx、HAProxy 相比,优势短板都在哪

先说我的结论:动态请求转发、需要 HTTP 层路由规则的场景,用 Nginx 更顺手;需要精细的四层 ACL 或内容交换策略,HAProxy 是好选择;但如果你要的是超高转发性能和稳定的故障转移,LVS-DR 几乎是绕不开的选项。

LVS-DR 的短板也很明显:它只能做四层转发,不能识别 URL、Cookie 这类七层信息,无法做基于内容的精细化路由。另外它不修改数据包内容,只改 MAC 地址,所以后端和负载均衡器必须在同一个二层网络里。部署前如果没看清楚网络拓扑,直接拿跨网段的机器硬上,流量根本通不了。这个坑我在早期排障时踩过,后面会专门讲。

2. 部署前的网络规划与硬件准备

2.1 角色划分:谁当主、谁当备、谁来干活

我这次部署用的是一套标准的三层架构:两台 LVS 节点组成主备关系,两台 Nginx 或 Apache 作为后端真实服务器。先说节点命名,后面配置里会直接用到。

  • LVS-Master:主负载均衡节点,IP 为 192.168.1.10
  • LVS-Backup:备负载均衡节点,IP 为 192.168.1.11
  • Web-1:后端真实服务器 1,IP 为 192.168.1.21
  • Web-2:后端真实服务器 2,IP 为 192.168.1.22
  • VIP:虚拟 IP 192.168.1.100,由 Keepalived 动态绑定到当前主节点

这里最容易被忽略的点是:VIP 必须和真实服务器在同一个广播域里。DR 模式要求后端真实服务器能用 VIP 接受请求,但同时又不能响应 VIP 的 ARP 请求,否则客户端的包会被真实服务器抢走。这个矛盾需要靠后面的 ARP 抑制配置来解决。

2.2 内核参数与基础软件准备

操作系统我建议用 CentOS 7.9 或兼容的 Linux 发行版,内核版本不要太老,保证有完整的 IPVS 模块。软件只需要装 Keepalived,LVS 本身不依赖额外的用户态程序,内核里已经集成了。

安装命令很简单:

yum install -y keepalived ipvsadm

ipvsadm 是管理 IPVS 转发表的工具,虽然生产配置由 Keepalived 生效,但调试时必须用它查看实时状态。内核模块确认一下:

modprobe ip_vs lsmod | grep ip_vs

如果模块没加载,先确认内核是否支持,再检查是否被安全策略禁用了。这一步看似基础,但我在不少机器上遇到过内核模块加载失败的情况,原因五花八门,有的是内核版本编译时裁剪了 IPVS 功能,有的是因为内核安全模块拦截。别急着往下配,先把它跑通。

2.3 网络拓扑设计的几个细节

VIP 所在网段我建议单独划一个子接口,比如 ens33:0。不要直接改主网卡的 IP,这样切换时恢复麻烦,而且容易造成地址冲突。Keepalived 配置里指定 VIP 绑定的网卡名,切换时由 VRRP 自动完成地址迁移。

后端真实服务器的网卡 IP 保持原有的 192.168.1.21 和 192.168.1.22,但必须在 lo 回环接口上额外绑定 VIP,同时设置严格的 ARP 过滤规则。这样做的理由是:真实服务器在处理请求时,发现目标地址是 VIP,如果本机没有 VIP 就会直接把包丢掉;绑上 VIP 才能收到数据,但如果不抑制 ARP 响应,同一网段的其他设备会学到 VIP 的 MAC 地址指向真实服务器,导致 LVS 转发失效。

3. 核心配置实操:Keepalived 与 LVS-DR 落地过程

3.1 Keepalived 主节点配置拆解

主节点的配置文件在 /etc/keepalived/keepalived.conf。我把生产上跑过的配置简化后贴出来,逐段说清楚。

global_defs { router_id LVS_MASTER vrrp_skip_check_adv_addr vrrp_strict vrrp_iptables } vrrp_instance VI_1 { state MASTER interface ens33 virtual_router_id 51 priority 100 advert_int 1 authentication { auth_type PASS auth_pass 1111 } virtual_ipaddress { 192.168.1.100/24 dev ens33 label ens33:0 } } virtual_server 192.168.1.100 80 { delay_loop 6 lb_algo wrr lb_kind DR nat_mask 255.255.255.255 persistence_timeout 0 protocol TCP real_server 192.168.1.21 80 { weight 2 TCP_CHECK { connect_timeout 3 nb_get_retry 3 delay_before_retry 3 } } real_server 192.168.1.22 80 { weight 1 TCP_CHECK { connect_timeout 3 nb_get_retry 3 delay_before_retry 3 } } }

global_defs 里的 vrrp_strict 启用了严格的 VRRP 模式,它会自动配置防火墙规则,限制 VRRP 报文只从对应接口进出。如果你机器上本来就有自定义防火墙规则,要特别注意这个选项,可能造成 Keepalived 的 VRRP 组播包被拦截。后文排障部分我会详细说一个和它直接相关的真实案例。

virtual_server 块定义了负载均衡转发表。lb_kind 必须写成 DR,这是模式的关键;lb_algo 我用了 wrr 加权轮询。为什么不用默认的 rr?因为 Web-1 和 Web-2 两台机器的配置并不完全一样,Web-1 内存更大,权重给 2,Web-2 给 1,这样流量大致按 2:1 分配。如果你想让所有后端“等开销负载均衡”,也就是完全平均分配,就用 lb_algo rr,权重字段不生效,每个节点轮着转发一个请求,真实服务器的配置必须一致。

这里还有一个关键参数 nat_mask 255.255.255.255。很多人照着网上老教程写 255.255.255.0,结果配置一加载就异常。原因是 DR 模式下,LVS 会把后端真实服务器当作“子网内的单点主机”来处理,掩码设为全 255 表示只匹配这一个 IP,而不是整个网段。Keepalived 同步到内核 IPVS 表时,如果掩码不对,转发逻辑就会错乱。

3.2 备节点配置:只改三处即可

备节点配置文件结构和主节点几乎一样,只需改动三处:

  • router_id 改成 LVS_BACKUP
  • state 改为 BACKUP
  • priority 降为 90

其他内容完全一致,尤其是 virtual_router_id、认证密码、VIP 和 virtual_server 定义,必须保持一致,否则两台节点无法组成同一个 VRRP 组,会发生双主或者互相抢占的诡异现象。

备节点的 priority 为什么不能设成 100?因为在 VRRP 协议里,优先级数值高者成为 Master。如果两台都是 100,主节点挂了还能靠比较 IP 地址大小来选主,但主节点恢复后行为就不确定了。设成 100 和 90,保证正常情况下 Master 稳定在主节点,备节点永远处于竞选等待状态。

3.3 真实服务器的 ARP 抑制:整个方案最容易翻车的地方

真实服务器上要做的配置有两部分:绑定 VIP 到回环接口,然后设置 ARP 过滤规则。

cat > /etc/sysconfig/network-scripts/ifcfg-lo:0 <<'EOF' DEVICE=lo:0 IPADDR=192.168.1.100 NETMASK=255.255.255.255 ONBOOT=yes NAME=lo:0 EOF ifup lo:0

然后写入 ARP 抑制参数:

cat >> /etc/sysctl.conf <<'EOF' net.ipv4.conf.lo.arp_ignore = 1 net.ipv4.conf.lo.arp_announce = 2 net.ipv4.conf.all.arp_ignore = 1 net.ipv4.conf.all.arp_announce = 2 EOF sysctl -p

这两个参数要连在一起理解。arp_ignore 设为 1 表示只回答目标 IP 是本接口 IP 的 ARP 请求。注意 lo 接口上绑的 VIP 是虚拟地址,而进入真实服务器的 ARP 请求目标 IP 是 VIP,源 IP 是客户端或交换机的地址,arp_ignore 会判断目标 IP 是否属于接收接口。arp_announce 设为 2 表示发送 ARP 报文时,源 IP 选主机上所有接口里最合适的那个,避免回包时源地址写成 VIP 导致路由错乱。

我见过不少教程只贴 sysctl 参数,不解释原理,结果有人把网卡主 IP 也写进 lo:0 的配置文件里,造成真实服务器无法正常访问外网。正确做法是只有 VIP 绑在 lo:0 上,原来的业务 IP 继续留在 eth0 上不动。

3.4 启动 Keepalived 并验证转发表

主备节点先检查配置语法:

keepalived -t -f /etc/keepalived/keepalived.conf

这个命令我在每次修改配置后都会跑一遍,能提前发现括号不匹配、参数拼写错误这类低级问题。确认没问题后启动服务:

systemctl start keepalived systemctl enable keepalived

启动后立刻在主节点上用 ip addr 看 VIP 是否有绑定:

ip addr show ens33

如果看到 192.168.1.100/24 出现在 ens33 或 ens33:0 上,说明 VRRP 已经正常起来。再到备节点上确认 VIP 还没被绑定,备节点应该只有自己的物理 IP。

然后查看 LVS 的转发表:

ipvsadm -Ln

正常会输出类似下面的内容:

IP Virtual Server version 1.2.1 Prot LocalAddress:Port Scheduler Flags -> RemoteAddress:Port Forward Weight ActiveConn InActConn TCP 192.168.1.100:80 wrr -> 192.168.1.21:80 Route 2 0 0 -> 192.168.1.22:80 Route 1 0 0

Forward 列显示 Route,说明 DR 模式生效。如果显示 Tunnel 或者 Masq,那就是配置里的 lb_kind 没写对。

4. 高可用切换实测与故障排查实录

4.1 主节点宕机演练:VIP 切换要多久

配置完成后一定要做实际演练,别等到真出事了才发现切换有问题。我最常用的测试方法是直接关机或停掉 Keepalived 服务。

停掉 Master 上的 Keepalived:

systemctl stop keepalived

正常情况下,备节点在 3 秒内(advert_int 1 乘以 3 个通告周期)收不到 Master 的心跳,就会抢占 VIP。在备节点上执行:

ip addr show ens33

一旦看到 VIP 已经绑定,就说明切换成功。此时再用另一台机器访问 http://192.168.1.100,业务应该不受影响,请求继续被分发到后端真实服务器。

值得注意的一个细节:如果你的客户端缓存的 ARP 表项还指向旧 Master 的 MAC 地址,切换后头几个请求可能会失败,或者出现延迟。这是因为客户端在发起连接时,会优先使用本地 ARP 缓存里的地址,等到缓存超时后重新广播 ARP 请求,才会拿到新 Master 的 MAC。在真实场景里,同网段的交换机通过免费 ARP 广播会相对快地更新转发表,但如果你在公网环境或跨了三层设备,这个收敛时间会长一些。解决手段是在 Keepalived 配置里把 advert_int 调小,或者配合交换机配置快速收敛。不过实际业务中这个窗口通常可以接受,真要是秒级不可用都不行,那得考虑其他更高规格的容灾方案。

4.2 一个高危报错的全过程复盘:keepalived exited with permanent error config

最近有不少同行反映启动 Keepalived 时报错,日志里写着 keepalived exited with permanent error config. 我仔细看过几个现场,大多是配置解析阶段直接失败,进程根本起不来。

典型的错误原因有几种:

  • virtual_server 或 real_server 块内缺少必要指令,比如 real_server 里没写 TCP_CHECK 或 HTTP_GET 健康检查定义
  • authentication 块缺少 auth_type 或 auth_pass
  • virtual_router_id 两端不一致,VRRP 组匹配不上
  • 配置里写了 lb_kind DR 但 real_server 的端口定义错误,或者健康检查类型写错

其中最常见的,是 real_server 块里的健康检查配置缺失。Keepalived 从 1.3 版本开始对配置解析更严格,以前能容忍的写法现在直接拒绝启动。

排查思路是这样:先看日志:

journalctl -u keepalived -n 50

日志会直接告诉你哪一行解析失败。如果没有明确行号,就把配置里每个块单独注释掉,二分定位。我建议新手一开始就用最小配置验证 Keepalived 的 VRRP 功能:只保留 global_defs 和一个 vrrp_instance,其他全部注释。等 VRRP 能正常切换,再把 virtual_server 块解开,逐段加回来。这比一次性写完所有配置再回头找错误要好用得多。

4.3 真实服务器收不到流量:先查 ARP 再查防火墙

还有一个高频问题:Keepalived 起来了,VIP 也绑上了,但访问 VIP 时请求没有被转发到后端的真实服务器,或者真实服务器收到包但不回包。

前半段问题,绝大多数是 ARP 抑制配错了。验证方法很简单,在客户端机器上执行:

arping -I eth0 -c 3 192.168.1.100

看返回的 MAC 地址是不是当前 Master 节点的 MAC。如果是真实服务器的 MAC,说明 ARP 抑制没有生效,VIP 被真实服务器抢答了。回去检查 sysctl 参数,特别是 net.ipv4.conf.all.arp_ignore 是否真的设为 1。

后半段问题,通常是防火墙拦了回包。真实服务器上确认一下:

iptables -L -n

如果有默认 DROP 策略,记得放行 80 端口。还有 SELinux 也可能干扰,生产环境建议先 setenforce 0 测试,确认是它的问题后再决定是否长期关闭或用策略放行。

4.4 关于 vrrp_strict 和防火墙的一个实例

前面提到 vrrp_strict 会让 Keepalived 自动配置防火墙规则。在一个客户的部署环境里,主备节点之间能互相 ping 通,但 VRRP 报文始终不通,主备一直处于竞争状态。通过抓包发现 keepalived 的组播报文根本没有到达对端。

后来发现问题是 Keepalived 自动添加的 iptables 规则和系统原有的 firewalld 规则冲突。vrrp_strict 模式的默认行为是只允许 VRRP 报文通过,其他协议全拦。而 firewalld 的公共区域策略又把 VRRP 组播地址 224.0.0.18 拦掉了。我当时的处理是保留 vrrp_strict,但在系统防火墙里加入显式放行:

firewall-cmd --permanent --add-rich-rule='rule protocol value="112" accept' firewall-cmd --reload

协议 112 就是 VRRP。加完之后主备心跳恢复正常。这里要提醒的是:每个环境的防火墙基线不一样,同样一条规则在 A 环境好用,在 B 环境可能就和现有策略打架。改完必须重新过一遍主备切换测试。

5. 监控体系与运维细节优化

5.1 健康检查参数该怎么调

TCP_CHECK 的三个参数我实际跑下来的经验值是 connect_timeout 3、nb_get_retry 3、delay_before_retry 3。这套组合意味着对每个真实服务器最多探测 3 次,每次超时 3 秒,失败后等待 3 秒再试。如果后端服务真的挂了,Keepalived 会在 9 秒左右把节点摘除。

如果你的后端是数据库或缓存这类对延迟更敏感的服务,建议把连接超时调成 2 秒,重试次数不变,摘除时间能缩短到 6 秒。要注意的是,调快意味着对后端波动更敏感,后端偶尔出现 1 秒慢查询就把节点摘掉,这种误判反而影响稳定性。生产环境里我会结合后端监控数据决定阈值,不建议机械地抄一套配置。

5.2 权重设计与容量预估怎么做

很多人以为权重只是随便填个数字,实际它决定了流量的分配比例,和真实服务器的硬件配置、带宽、应用特性都有关系。

我之前给一个客户做容量评估时,两台后端机器配置分别是 4 核 8G 和 8 核 16G。一开始权重都设成 1,结果 8 核机器长期空闲,4 核机器 CPU 打满。后来把权重调成 2:1,8 核机器承担大约 2/3 流量,4 核机器承担 1/3,整体吞吐提升明显。

如果后端应用是无状态接口,比如纯 API 服务,可以大胆用 wrr。如果涉及会话保持,比如登录状态存在内存里,LVS 层可以通过 persistence_timeout 设置会话保持超时时间,比如 600 秒。但要注意这个参数会直接影响负载均衡的“等开销”特性,同一用户的请求总是被分到同一台后端,两台机器之间可能出现流量倾斜。本质上这是会话状态和有状态应用的权衡,不是 LVS 能自动帮你解决的。

5.3 日志和指标监控的几个推荐动作

Keepalived 默认日志走系统日志,通过 journalctl 查看。建议加一个独立的监控脚本或接入现有的 Prometheus + Alertmanager 体系,监控以下指标:

  • VIP 是否绑定在当前预期的主节点上
  • IPVS 转发表中的 ActiveConn 是否持续增长
  • 后端真实服务器的 TCP 健康检查是否全部通过
  • VRRP 状态是否长期处于 MASTER 或 BACKUP

实践中我发现,单纯看进程存活不靠谱。Keepalived 进程可能在,但 VRRP 已经脑裂了,或者 VIP 绑定的接口异常。写监控时不要只看 systemctl status 的结果,而是定期执行 ip addr show 和 keepalived 的 vrrp 状态输出,把真实网络状态作为监控对象。

6. 我踩过的那些坑,再多说几句

先说说我最容易忽略的一个事:备节点上的 LVS 转发表。刚开始搭建时,我把 virtual_server 配置完整写在了主备两台机器上,但备节点的 ipvsadm -Ln 输出里的 Forward 列一直显示 Masq。折腾了很久才发现是备节点某个历史配置文件残留导致 IPVS 表存在旧条目,Keepalived 启动时没有完全清理干净。重启备节点网络服务和 Keepalived 后,把残留表项清掉:

ipvsadm -C

再重启服务,一切恢复正常。这个经验告诉我,调试 LVS 时必须养成分步验证的习惯,先看 Keepalived 的 VRRP 状态,再看 IPVS 表,最后测真实链路。一步到位反而容易把问题搅成一锅粥。

另外一个容易被忽略的点是时间同步。VRRP 报文太敏感,如果主备节点系统时间偏差过大,Keepalived 会认为对端异常,导致频繁切换。我在维护规范里要求所有节点统一配置 NTP 或 chrony,这也属于高可用的基础保障。

如果你的后端真实服务器操作系统版本不统一,比如一台 CentOS 7、一台 Ubuntu,ARP 参数的写法略有差异,但逻辑是一样的:关注 lo 接口上的 VIP 绑定和 sysctl.conf 中的 arp_ignore / arp_announce。Ubuntu 上持久化配置用 netplan 或 /etc/sysctl.d/ 下的独立文件,不要把 CentOS 的脚本原样拷过去,细节差异会带来意想不到的麻烦。

说实话,LVS-DR + Keepalived 这套方案能经受住这么多年的生产验证,靠的是它架构简单、转发路径清晰。但正是因为它简单,任何一个环节的细节出错都会导致整套系统表现诡异。配置文件的每个参数、每个 IP 地址的绑定位置、每条防火墙规则,都值得你多花十分钟搞清楚背后的原因。我在每一次部署结束后都会把当时的完整配置和踩坑记录归档,下次再遇到类似问题能省下大量排查时间。做基础设施维护这行,真正的资产不是某个配置模版,而是对每一行配置为什么这么写的理解深度。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询