本文面向具备一定网络和系统基础的中高级工程师,旨在深度剖析如何利用 LVS (Linux Virtual Server) 和 Keepalived 构建一个生产级别的、高可用的四层负载均衡集群。我们将不仅仅停留在配置文件的讲解,而是深入到网络协议、内核工作机制和架构权衡的层面,从根源上理解这套经典组合的威力与陷阱,最终提供一套可落地的架构演进方案。
现象与问题背景
在大多数系统的初始阶段,流量入口通常是一台或一组独立的服务器,例如 Nginx 或 API Gateway。随着业务增长,这个入口迅速成为整个系统的瓶颈和单点故障(Single Point of Failure, SPOF)。一个直接的想法是横向扩展后端的 Real Server(真实业务服务器),但这引入了一个新问题:如何将客户端的流量高效、可靠地分发到这些 Real Server 上?
最初,我们可能会尝试 DNS 轮询。这是一种简单粗暴的负载均衡方式,但其缺陷是致命的:
- 缓存问题: DNS 记录在各级 DNS 服务器和客户端本地都有缓存,当后端服务器变更(上线或下线)时,流量切换延迟极高,可能长达数分钟甚至数小时。
- 无健康检查: DNS 系统无法感知后端服务器的健康状态。如果一台服务器宕机,DNS 仍然会将流量解析到这个故障地址,导致大量用户请求失败。
- 负载不均: 它无法根据服务器的实际负载或性能进行动态调度。
为了解决这些问题,我们需要一个专门的负载均衡器。常见的有七层负载均衡器(如 Nginx、HAProxy)和四层负载均衡器(如 LVS)。七层负载均衡器工作在应用层,可以理解 HTTP 协议,实现更精细的路由策略(如基于 URL、Header),但其性能开销也更大。而四层负载均衡器工作在传输层,仅处理 IP 和端口,不解析应用层数据,因此拥有极高的性能。对于需要处理海量并发连接的场景(如游戏、直播、交易系统),LVS 几乎是不二之选。
然而,引入 LVS 作为负载均衡器后,LVS 本身又成为了新的 SPOF。如果这台 LVS 服务器宕机,整个业务将彻底中断。因此,我们的核心问题演变为:如何构建一个既能实现高性能负载均衡,又能保证负载均衡层自身高可用的集群? 这就是 Keepalived 与 LVS 组合要解决的核心问题。
关键原理拆解
要理解这套方案,我们必须回到计算机网络和操作系统的基础原理。这套架构的精髓在于 LVS 负责“分发”,而 Keepalived 负责“高可用”。
LVS (Linux Virtual Server) 的内核级魔法
LVS 并非一个独立的用户态应用,而是 Linux 内核 Netfilter 框架的一部分。它在内核的 PREROUTING 链上通过 IPVS (IP Virtual Server) 模块工作,这意味着所有网络包在进入协议栈上层(如 TCP/IP 协议栈)之前就会被它拦截和处理。这种内核态的直接处理是其高性能的根源,避免了用户态和内核态之间频繁的数据拷贝和上下文切换。
LVS 主要有三种工作模式,理解它们的本质对于架构选型至关重要:
- NAT (Network Address Translation) 模式: 这是最简单直观的模式。客户端请求到达 LVS Director(调度器),Director 将数据包的目标 IP 地址修改为选定的 Real Server 的 IP 地址,然后转发出去。当 Real Server 返回响应时,数据包必须再次经过 Director,由其将源 IP 地址修改回 VIP(Virtual IP),再返回给客户端。这种模式下,Director 成为了所有进出流量的瓶颈,性能受限于 Director 的网卡带宽和 CPU 处理能力。
- TUN (IP Tunneling) 模式: Director 将客户端的原始 IP 包封装在一个新的 IP 包中(IP-in-IP),然后发送给 Real Server。Real Server 解封装后,发现内部是客户端的原始请求,处理完毕后可以直接将响应包发回给客户端,无需再经过 Director。这种模式解决了 NAT 模式下 Director 的响应瓶颈,但 IP 封装会带来额外的 overhead,且要求 Real Server 支持 IP 隧道协议。
- DR (Direct Routing) 模式: 这是生产环境中应用最广泛、性能最高的模式。Director 接收到客户端请求后,不修改 IP 地址,而是修改数据包的目标 MAC 地址,将其改为选定的 Real Server 的 MAC 地址,然后在二层网络上直接转发给 Real Server。Real Server 收到包后,发现目标 IP 是 VIP,但目标 MAC 是自己的,因此内核会接收并处理这个包。当 Real Server 处理完请求后,由于它知道 VIP,可以直接构建响应包,将源 IP 设置为 VIP,直接返回给客户端。这个过程完全绕过了 Director。
在 DR 模式下,Director 只处理入口流量,出口流量由 Real Server 直达客户端,极大地提升了整个集群的吞吐能力。但它也引入了一个棘手的问题:ARP 欺骗问题。所有 Real Server 和 Director 都配置了同一个 VIP。当网络中的设备查询这个 VIP 对应的 MAC 地址时,谁应该响应?如果都响应,会造成 ARP 冲突。解决方案是让 Real Server “隐藏” 它们的 VIP,不响应对 VIP 的 ARP 请求,这通常通过修改内核参数 `arp_ignore` 和 `arp_announce` 来实现。
Keepalived 与 VRRP (Virtual Router Redundancy Protocol)
Keepalived 的核心是实现了 VRRP 协议,这是一个用于提供网关高可用的标准协议(RFC 5798)。它的目标是在多个路由器(或 LVS 服务器)中选举出一个主节点(MASTER),由它来承载一个虚拟的、浮动的 IP 地址(即 VIP)。
VRRP 的工作机制可以概括为:
- 角色: 一个 VRRP 组内有多台设备,一台为 MASTER,其余为 BACKUP。
- 心跳: MASTER 节点会周期性地通过组播(默认地址 224.0.0.18)发送 VRRP 通告报文,向组内其他成员宣告自己的存活状态。
- 抢占与选举: BACKUP 节点会监听这个组播地址。如果在一个预设的时间(通常是3倍的通告间隔)内没有收到 MASTER 的通告,它们会认为 MASTER 已经失效。此时,所有 BACKUP 节点会根据自身的优先级(Priority)发起选举,优先级最高的节点将转变为新的 MASTER。
- VIP 接管: 新的 MASTER 会立即对外发送一个免费 ARP (Gratuitous ARP) 报文,向整个局域网宣告 VIP 现在与自己的 MAC 地址绑定。交换机收到这个报文后,会更新其 MAC 地址表,从而将后续发往 VIP 的流量全部转发到新的 MASTER 上,整个切换过程对客户端是透明的。
Keepalived 不仅实现了 VRRP,还内置了对 LVS 的集成。它能监控 LVS 池中 Real Server 的健康状态(通过 TCP 连接、HTTP 请求等方式),如果发现某台 Real Server 故障,会自动将其从 LVS 的转发表中移除。当 Real Server 恢复后,再自动加回去。这种结合,使得 Keepalived 成为了控制 LVS 和实现 Director 自身高可用的完美搭档。
系统架构总览
一个典型的基于 Keepalived+LVS 的双机热备集群架构如下:
我们将用文字来描述这幅架构图。想象一下,从上到下分为三层:客户端、负载均衡层、真实服务层。
- 客户端层: 外部用户或上游服务,它们只知道一个统一的入口地址,即 VIP。
- 负载均衡层: 这是我们的核心。它由两台配置完全相同的 LVS 服务器构成,我们称之为 LVS-MASTER 和 LVS-BACKUP。
- 它们都连接到同一个外部交换机和内部交换机。
- LVS-MASTER 正常工作时,VIP 绑定在它的公网网卡上。所有外部流量都先到达它。
- LVS-BACKUP 处于待命状态,它也在持续监听 VRRP 心跳。
- 两台服务器之间通过 VRRP 协议进行心跳检测。通常建议为心跳通信使用一个独立的网络接口,避免业务流量的干扰。
- 真实服务层: 这里是多台部署了相同业务应用的 Real Server (RS1, RS2, RS3, …)。
- 它们都连接到内部交换机。
- 每台 Real Server 都在其环回接口(loopback interface)上配置了 VIP。
- 每台 Real Server 都修改了内核的 ARP 相关参数,以避免 ARP 冲突。
数据流路径(DR 模式):
- 客户端向 VIP 发起请求(例如 TCP SYN 包)。
- 请求经过路由器和交换机,最终到达 LVS-MASTER(因为 VIP 在它身上)。
- LVS-MASTER 的 IPVS 模块根据预设的负载均衡算法(如加权轮询),选择一台健康的 Real Server(如 RS2)。
- LVS-MASTER 将数据包的目标 MAC 地址修改为 RS2 的 MAC 地址,然后将包从内部网卡扔到交换机上。源/目标 IP 均未改变。
- 交换机根据 MAC 地址将包转发给 RS2。
- RS2 的网卡收到包,内核检查发现目标 IP 是配置在环回接口上的 VIP,于是接收并处理该请求。
- RS2 处理完毕,构建响应包。源 IP 为 VIP,目标 IP 为客户端 IP。然后直接通过自己的默认网关将响应发回给客户端。
这个过程中,响应流量完全不经过 LVS-MASTER,实现了流量的直接路由。
核心模块设计与实现
让我们深入到配置和代码层面,看看这套架构如何落地。这里的配置是接地气的、可直接用于生产的示例。
LVS Director 节点配置 (MASTER & BACKUP)
两台 Director 的配置几乎完全一样,唯一的区别在于 `keepalived.conf` 中的 `state` 和 `priority`。
1. 安装 LVS 和 Keepalived 工具:
# CentOS/RHEL
sudo yum install -y ipvsadm keepalived
# 开启内核的 IP 转发
echo "net.ipv4.ip_forward = 1" | sudo tee -a /etc/sysctl.conf
sudo sysctl -p
2. Keepalived 核心配置 (`/etc/keepalived/keepalived.conf`):
这是 MASTER 节点的配置示例。BACKUP 节点的配置只需将 `state` 改为 `BACKUP`,并将 `priority` 设置为一个较低的值(如 90)。
global_defs {
notification_email {
[email protected]
}
notification_email_from [email protected]
smtp_server 127.0.0.1
smtp_connect_timeout 30
router_id LVS_DEVEL_MASTER # 标识节点,MASTER 和 BACKUP 必须不同
}
# VRRP 实例定义,用于 Director 自身的高可用
vrrp_instance VI_1 {
state MASTER # MASTER 节点设为 MASTER, BACKUP 节点设为 BACKUP
interface eth0 # VIP 绑定的接口
virtual_router_id 51 # VRRP 组 ID,MASTER 和 BACKUP 必须一致
priority 100 # 优先级,MASTER > BACKUP
advert_int 1 # VRRP 通告间隔,单位秒
authentication {
auth_type PASS
auth_pass 1111 # 简单的认证密码
}
virtual_ipaddress {
192.168.1.100/24 dev eth0 label eth0:vip # 定义 VIP
}
}
# LVS 服务定义
virtual_server 192.168.1.100 80 {
delay_loop 6 # 健康检查间隔
lb_algo wrr # 负载均衡算法:加权轮询
lb_kind DR # 工作模式:Direct Routing
persistence_timeout 50 # 会话保持时间
protocol TCP
# 第一个 Real Server
real_server 192.168.2.11 80 {
weight 100 # 权重
TCP_CHECK {
connect_timeout 3
nb_get_retry 3
delay_before_retry 3
connect_port 80
}
}
# 第二个 Real Server
real_server 192.168.2.12 80 {
weight 100
HTTP_GET {
url {
path /healthz
status_code 200
}
connect_timeout 3
nb_get_retry 3
delay_before_retry 3
}
}
}
极客解读:
- `virtual_router_id`:像是一个 VLAN ID,同一个局域网内如果有多个 VRRP 组,必须用它来区分,否则会互相干扰。
- `lb_algo wrr`:加权轮询(Weighted Round Robin)通常比简单的 `rr`(Round Robin)更好,因为它允许你为性能更强的机器分配更多流量。`wlc`(Weighted Least-Connection)在处理长连接时表现更佳,因为它会把新连接导向当前连接数最少的服务器。
- `TCP_CHECK` vs `HTTP_GET`:`TCP_CHECK` 仅仅检查端口是否可达,非常轻量。但如果你的应用进程死了,而端口还在监听(例如 Gunicorn 的 master 进程还在),`TCP_CHECK` 就会误判。`HTTP_GET` 能更好地检查应用层面的健康状况,但开销也稍大。
Real Server 节点配置
所有 Real Server 都需要执行以下配置,这是 DR 模式的关键。
#!/bin/bash
VIP=192.168.1.100
# 1. 在环回接口上配置 VIP
ifconfig lo:0 $VIP netmask 255.255.255.255 broadcast $VIP
route add -host $VIP dev lo:0
# 2. 修改内核 ARP 参数,解决 ARP 冲突
# arp_ignore=1: 当 ARP 请求的目标 IP 是本机其它接口上的地址时,不予响应
# arp_announce=2: 总是使用最适当的本地地址来宣告 ARP 响应,避免将 VIP 从非 VIP 接口宣告出去
echo "1" > /proc/sys/net/ipv4/conf/all/arp_ignore
echo "1" > /proc/sys/net/ipv4/conf/lo/arp_ignore
echo "2" > /proc/sys/net/ipv4/conf/all/arp_announce
echo "2" > /proc/sys/net/ipv4/conf/lo/arp_announce
echo "Real Server configuration finished."
极客解读:
很多新手会在这里掉坑。为什么要在 `lo` 接口上配置 VIP?因为 `lo` 接口不与任何物理硬件绑定,它是一个纯软件接口,不会向外发送 ARP 报文。将 VIP 配置在这里,再配合 `arp_ignore` 和 `arp_announce` 参数,就能完美地让 Real Server “拥有”这个 VIP 用于处理数据包,但又在网络层面“隐藏”起来,不对外广播,把 ARP 响应的权力完全交给 LVS Director。
性能优化与高可用设计
一套生产系统远不止搭起来那么简单,还需要考虑各种极端情况和性能瓶颈。
对抗“脑裂”(Split-Brain)问题
脑裂是双机热备系统中最臭名昭著的问题。想象一下,如果 LVS-MASTER 和 LVS-BACKUP 之间的心跳网络(例如连接它们的交换机)发生故障,但两台服务器本身都运行正常。此时,LVS-BACKUP 因为收不到 MASTER 的心跳,会认为 MASTER 已死,于是自己升级为 MASTER 并接管 VIP。结果,网络中同时存在两个 MASTER,都拥有同一个 VIP,这会导致严重的网络风暴和业务中断。
解决方案:
- 冗余心跳链路: 使用两条物理上独立的链路(例如网线直连 + 交换机连接,或者 bonding)进行心跳通信,大大降低单点故障概率。
- 引入仲裁机制: 在 Keepalived 的配置中,可以编写 `notify` 脚本。当状态切换时(例如从 BACKUP 切换到 MASTER),脚本被触发。这个脚本可以去 ping 一个稳定的第三方 IP(例如网关地址)。如果连网关都 ping 不通,说明是自己与网络的连接出了问题,而不是 MASTER 宕机了,此时脚本应该主动将自己的 Keepalived 服务降级或停止,避免抢占 VIP。
- Fencing(隔离): 在更严格的场景(如数据库高可用)中,当检测到可能发生脑裂时,会通过带外管理(如 IPMI)强制将旧的 MASTER 重启或断电,确保只有一个“活”的 MASTER。在 LVS 场景下,这种做法相对较重,但原理值得借鉴。
LVS Director 性能调优
虽然 LVS 性能极高,但在千万级并发连接下,其自身也可能成为瓶颈。
- 连接跟踪表(Connection Tracking Table): LVS 使用一个哈希表来跟踪所有连接的状态。这个表的大小由内核参数 `net.ipv4.ip_vs_conn_tab_bits` 控制。默认值可能较小,在高并发下会导致哈希冲突,性能下降。可以根据内存大小适当调大该值,例如 `sysctl -w net.ipv4.ip_vs_conn_tab_bits=18`,这将允许大约 262144 条连接。
- 网卡中断与 CPU 亲和性: 高网络吞吐会产生大量硬件中断。如果这些中断都由单个 CPU 核心处理,该核心会成为瓶颈。可以安装 `irqbalance` 服务来自动平衡中断,或者手动绑定网卡的中断队列到不同的 CPU 核心上(修改 `/proc/irq/{IRQ_NUMBER}/smp_affinity`),实现精细化控制。
- 关闭不必要的服务: 作为专用的负载均衡器,Director 节点应该尽可能精简,关闭所有不必要的服务和防火墙规则(`iptables` 的规则过多会影响 Netfilter 性能),确保 CPU 和内存资源完全用于网络转发。
故障切换时间分析
高可用的一个关键指标是故障切换时间(Failover Time)。
- Real Server 故障: 切换时间由 Keepalived 的健康检查配置决定。例如 `delay_loop 6`,意味着每 6 秒检查一次。如果一次检查失败,可能还有重试(`nb_get_retry`)。总的探测时间约为 `delay_loop` + (`nb_get_retry` * `delay_before_retry`)。这是一个可调的权衡:检查越频繁,发现故障越快,但对 Real Server 的压力也越大。
- Director 故障: 切换时间由 VRRP 的 `advert_int` 参数决定。默认的“死亡”判定时间是 `3 * advert_int`。如果 `advert_int` 为 1 秒,那么切换时间大约是 3 秒。缩短 `advert_int` 可以加快切换,但会增加网络中的 VRRP 广播包,带来额外开销。对于大多数应用,1 秒的设置是比较合理的。
架构演进与落地路径
对于一个从零开始的系统,我们不建议一步到位直接上最复杂的架构。一个务实的演进路径如下:
- 阶段一:单点 LVS Director。 在业务初期,流量不大,但需要对后端服务器进行负载均衡时。先部署一台 LVS Director,将后端 Real Server 管理起来。此时,解决了后端扩展性的问题,但 Director 自身是 SPOF。这个阶段的目标是验证 LVS DR 模式配置的正确性,并积累运维经验。
- 阶段二:LVS + Keepalived 双机热备(Master/Backup)。 当业务对可用性提出更高要求时,引入第二台 Director 和 Keepalived,构建本文详述的主备集群。这是性价比最高、应用最广的方案,能够抵御单台 Director 的硬件或软件故障。
- 阶段三:LVS 集群的水平扩展(ECMP)。 当单一 LVS 主备对的性能(特别是pps,每秒包处理能力)达到瓶颈时,可以考虑在 LVS Director 的上层再做一次负载均衡。例如,在接入层路由器上配置 ECMP(Equal-Cost Multi-Path routing),将流量等价地路由到多对 LVS Master/Backup 集群上。每一对集群拥有不同的 VIP。这种架构常见于大型互联网公司的入口流量处理。
- 阶段四:云原生时代的替代方案。 在 Kubernetes 等云原生环境中,四层负载均衡的功能通常由 `kube-proxy` 的 IPVS 模式或者云厂商提供的 LoadBalancer Service (如 AWS NLB, GCP TCP Load Balancing) 来实现。其底层原理与 LVS 一脉相承,但通过声明式 API 进行了封装,运维更加自动化。对于完全拥抱云原生的团队,直接使用这些成熟方案可能更高效。但理解 LVS 和 Keepalived 的工作原理,能让你在排查云原生网络问题时拥有更深刻的洞察力。
总而言之,LVS + Keepalived 是一套历经考验的、坚如磐石的四层负载均衡高可用方案。尽管现代云原生技术提供了更上层的抽象,但其背后关于虚拟 IP、健康检查、内核转发和故障切换的核心思想是永恒的。掌握它,不仅能解决当下的问题,更能让你对构建任何高可用系统都有更深入的理解。
延伸阅读与相关资源
-
想系统性规划股票、期货、外汇或数字币等多资产的交易系统建设,可以参考我们的
交易系统整体解决方案。 -
如果你正在评估撮合引擎、风控系统、清结算、账户体系等模块的落地方式,可以浏览
产品与服务
中关于交易系统搭建与定制开发的介绍。 -
需要针对现有架构做评估、重构或从零规划,可以通过
联系我们
和架构顾问沟通细节,获取定制化的技术方案建议。