LVS负载均衡全解析:从原理到DR模式配置与超时排障
2026/9/9 23:36:49 网站建设 项目流程

前阵子有个朋友在群里问:“LVS到底还值不值得学?现在云原生一堆网关,是不是不用折腾这个了?”我的回答一直是:值得,而且很多大流量入口现在还在用LVS扛着。Linux Virtual Server这套东西从1998年走到今天,靠的不是情怀,而是它内核态转发带来的极致性能。这篇文章我打算把LVS(Linux Virtual Server)负载均衡器的原理、三种工作模式、调度算法、完整配置和排障思路从头到尾过一遍。尤其会重点聊聊被问得最多的空闲超时问题——很多人配置好后线上频繁断连,十个里有八个是栽在timeout上。不管你是刚接触四层负载均衡的运维新人,还是准备在生产环境正式落地LVS的进阶选手,这篇文章都能给你一个可以直接参考的完整路线。

1. LVS整体设计与方案选型思路

1.1 LVS是什么,它到底解决了什么问题

LVS全称Linux Virtual Server,是一套基于Linux内核实现的服务器集群方案。它的核心逻辑就一句话:对外提供一个虚拟IP(VIP),把所有请求通过内核中的IPVS(IP Virtual Server)模块,按预设的调度算法转发到后端的真实服务器(Real Server,简称RS)上。

它工作在OSI模型的第四层,也就是传输层,只看TCP/UDP的IP加端口,不关心HTTP头、URL、Cookie这些七层内容。这个特点既是它的“边界”,也是它的优势。因为不需要解析应用层数据,所以LVS的处理路径极短:数据包从网卡进来,在内核里直接改写目的地址或MAC后发出去,根本不经过用户态拷贝。对比Nginx、HAProxy这类工作在应用层的软件,LVS在同等硬件条件下能支撑的并发连接数要高出一个量级,这也是大型网站至今仍把LVS放在最前端的原因。

它解决的核心问题有三个:一是高性能,单机可以支撑百万级别并发连接;二是高可用,结合Keepalived可以实现Director节点故障自动切换;三是高扩展,后端RS可以随时加减,容量不够就加机器,对客户端完全透明。

1.2 三种工作模式怎么选

LVS有三种包转发模式:NAT、DR、TUN。理解这三者的区别,是后续配置不出错的前提。

NAT模式(VS/NAT)最直接:Director收到客户端请求后,通过DNAT把包的目的IP改成选中的RS地址,RS处理完再把响应回给Director,由Director改写源IP后转回客户端。这样来回流量都要过Director,Director很容易成为瓶颈。但好处是RS可以跨网段,RS的网关指向Director就行,架构简单,适合小规模场景。

DR模式(VS/DR)是生产中用的最多的方案。Director在转发时只改数据链路层的目标MAC地址,把帧直接发给RS,网络层的IP完全不变。RS在回环接口上绑定VIP,收到数据后发现自己就是目标地址,于是正常处理,响应时直接以VIP作为源地址回给客户端,走的是原路返回,完全不经过Director。这意味着入向流量走Director,出向流量各回各家,Director的压力小了很多,性能最好。代价是Director和RS必须在同一个二层网络里,而且RS必须做ARP抑制,否则客户端ARP请求VIP时会同时收到多台机器的响应。

TUN模式(VS/TUN)是把请求包用IP隧道封装后送给RS,RS解封装处理,响应直接回客户端。它兼顾了NAT的跨网段能力和DR的吞吐性能,但要求RS支持隧道协议,配置和排障复杂度都高一些,实际中用得不如DR普遍。

放在一起看:

对比项NATDRTUN
转发方式修改目的IP修改目标MACIP隧道封装
响应路径必须经过DirectorRS直接回客户端RS直接回客户端
网络要求可跨网段,RS网关指Director必须同二层网络可跨网段,需支持隧道
Director压力大,容易成瓶颈小,理论性能最优较小
配置复杂度中,要处理ARP中高
适用场景小规模、跨网段同机房大流量首选跨机房、异构网络

我的经验是:同机房场景无脑优先DR,反正现在机房里的RS基本都在同一个交换机下面,DR的性价比最高;只有RS分散在不同网段、又不想引入更复杂的方案时才考虑NAT或TUN。

1.3 为什么现在还要选LVS

很多人纠结:Nginx也能做负载均衡,为什么要多套一层LVS?我的理解是,它们根本不是竞争关系,而是上下游关系。

Nginx工作在七层,能拿到HTTP完整信息,适合做路由分发、缓存、限流、WAF这类精细化控制;但七层解析本身就消耗CPU,并发上来之后性能下降明显。LVS工作在四层,逻辑简单、转发快,适合挡在最前面处理海量TCP连接,把流量“粗分”给后面的七层集群。

于是就有了非常经典的“四层+七层”分层架构:客户端 -> LVS(四层负载均衡) -> Nginx集群(七层反向代理) -> 业务服务器。LVS负责扛流量,Nginx负责做业务路由,各干各的活,谁也不抢谁的资源。这个架构我现在搭系统还在用,实测在高并发场景下比单纯堆Nginx稳定得多。

2. 核心细节解析:调度算法与关键参数

2.1 静态调度算法有哪些

LVS内置了十种调度算法,先看静态的几种。

RR(Round Robin)就是轮流分发,每个请求按顺序分给下一台RS,大家机会均等。前提是RS的硬件配置、处理能力都差不多,否则弱的机器会先被打垮。

WRR(Weighted Round Robin)在RR基础上加了权重,权重越高的RS收到的连接越多。比如8核和16核的机器权重可以配成1和2。权重不是越高越好,要根据实际处理能力压测得出,拍脑袋配权重照样会把机器压垮。

SH(Source Hashing)按客户端源IP做哈希,同一个源IP的请求会固定落到同一台RS上,天然实现了会话保持,适合那些不方便用Cookie做会话粘滞的协议。但缺点也明显:某台RS挂了,哈希到它的那批客户端全部受到影响,会话集中失效的风险比较大。

DH(Destination Hashing)按目的IP做哈希,多用于多台防火墙或缓存服务器的场景,按目标地址把流量分流。日常业务负载均衡里用得少。

2.2 动态调度算法怎么选

动态算法的共同点是:每次调度时都会去看后端的实时负载状态,最常见的指标是连接数。

LC(Least Connections)把新请求分给当前活跃连接数最少的RS。这个算法在长连接场景下比较准,但在短连接风暴下,活跃连接数跳动很快,调度可能不够均匀。

WLC(Weighted Least Connections)在LC基础上结合了权重,也是LVS的默认调度算法。计算公式是:当前连接数除以权重,取结果最小的RS。它兼顾了静态权重和动态负载,大部分业务场景下用它都不会出大问题。

SED(Shortest Expected Delay)在WLC基础上加了“延迟预估”,优先选连接数除以权重后预期延迟最小的RS。NQ(Never Queue)是SED的改进版,保证不会有RS一直处于空闲排队状态,适合响应速度差异较大的后端。

LBLC(基于局部性的最少连接)和LBLCR(带复制的局部性最少连接)是专门给缓存服务器设计的。前者让相同目的IP的请求尽量走同一台缓存RS,提高缓存命中率;后者允许缓存复制到多台RS,容忍单点故障。普通业务用不上,但如果你是做缓存网关,这两个算法值得研究。

MH(Maglev Hashing)是后来加入的一致性哈希算法,它解决的是SH在RS增删时大量会话迁移的问题:只有少量连接会重新映射,适合需要平滑扩缩容的场景。

2.3 我常用的算法选型参考

算法这个东西,没有绝对的对错,只有适不适合。我整理了这份参考:

  • 后端配置均衡、无特殊要求:直接默认WLC,省心。
  • 需要会话保持、后端又不支持应用层粘滞:SH或带持久性参数的WLC。
  • 长连接为主(数据库连接池、WebSocket):LC或WLC。
  • 短连接高并发(HTTP接口):WLC配合合理的超时参数。
  • 缓存集群:LBLC或LBLCR。
  • 扩缩容频繁、尽量少的会话迁移:MH。

另外提醒一句:调度算法解决的是“转发给谁”的问题,会话保持不能只靠算法。生产环境里我通常会配合Keepalived的persistence_timeout一起用,双保险。

3. 实操过程与核心环节实现

3.1 部署前要准备什么

以DR模式为例,假设我有两台RS,一台Director,都跑在同一个网段192.168.100.0/24:

  • Director:192.168.100.10
  • RS1:192.168.100.11(权重1)
  • RS2:192.168.100.12(权重2)
  • VIP:192.168.100.100

第一步是确认内核有没有IPVS模块。LVS的核心功能都在内核里,目录系统一般默认编译了ip_vs模块,但保险起见还是手动加载一下:

modprobe ip_vs modprobe ip_vs_wlc modprobe ip_vs_sh modprobe ip_vs_mh lsmod | grep ip_vs

ip_vs加载成功后会显示一批模块。接着安装用户态管理工具:

# Debian/Ubuntu apt install ipvsadm # CentOS/RHEL yum install ipvsadm

如果ipvsadm -L -n执行时报“No such file or directory”,基本就是ip_vs模块没加载,回去加载模块再试。

3.2 DR模式完整配置步骤

先配置Director。把VIP配置到物理网卡上,然后添加虚拟服务和两台RS:

ip addr add 192.168.100.100/32 dev eth0 ipvsadm -A -t 192.168.100.100:80 -s wlc ipvsadm -a -t 192.168.100.100:80 -r 192.168.100.11:80 -g -w 1 ipvsadm -a -t 192.168.100.100:80 -r 192.168.100.12:80 -g -w 2

这里的要点:

  • -A表示添加虚拟服务,-t指定TCP协议加VIP端口,-s指定调度算法。
  • -a添加RS,-r指定RS地址,-g代表DR模式(gateway),NAT模式用-m,TUN模式用-i
  • -w配权重。
  • VIP用/32掩码,因为DR模式下VIP不应该出现在路由表中参与网段广播。

然后是两台RS上的配置。RS要把VIP绑定到回环接口上,同时必须做ARP抑制,否则客户端ARP广播问VIP时,RS也会应答,流量就不经过Director了:

ip addr add 192.168.100.100/32 dev lo echo 1 > /proc/sys/net/ipv4/conf/all/arp_ignore echo 2 > /proc/sys/net/ipv4/conf/all/arp_announce echo 1 > /proc/sys/net/ipv4/conf/lo/arp_ignore echo 2 > /proc/sys/net/ipv4/conf/lo/arp_announce

arp_ignore设为1,表示只应答目标IP是本机且属于到达网卡接口的ARP请求;arp_announce设为2,表示ARP响应使用源IP所在网卡的最佳本地地址。两行配合,RS就不会对外宣告自己有VIP了。这些配置要写进/etc/sysctl.conf和开机脚本里,别重启机器就失效。

现在从客户端访问http://192.168.100.100,请求会被转发到RS1或RS2。验证一下:

ipvsadm -L -n

输出里能看到虚拟服务和两台RS的状态,ActiveConn和InActConn会随着访问增长。再开一个终端跑ipvsadm -L -n -c,能看到连接表里每一条转发记录的来源IP、目的IP和当前状态,排障时这个命令是黄金工具。

3.3 用Keepalived管好健康检查和高可用

裸配ipvsadm的问题有两个:一是RS挂了LVS不会感知,请求照样转发过去,用户直接报错;二是Director自身单点,Director挂了整个集群就瘫了。这两件事都交给Keepalived解决。

Keepalived同时承担两个角色:VRRP负责Director高可用(两个Director抢一个VIP),IPVS管理负责维护LVS规则和RS健康检查。配置核心部分如下:

vrrp_instance VI_1 { state MASTER interface eth0 virtual_router_id 51 priority 100 advert_int 1 authentication { auth_type PASS auth_pass 1111 } virtual_ipaddress { 192.168.100.100/24 dev eth0 } } virtual_server 192.168.100.100 80 { delay_loop 6 lb_algo wlc lb_kind DR persistence_timeout 50 protocol TCP real_server 192.168.100.11 80 { weight 1 TCP_CHECK { connect_timeout 3 nb_get_retry 3 delay_before_retry 3 } } real_server 192.168.100.12 80 { weight 2 TCP_CHECK { connect_timeout 3 nb_get_retry 3 delay_before_retry 3 } } }

几个配置的坑我得单独说:

persistence_timeout是持久连接超时,单位秒。它保证来自同一个源IP的请求在50秒内都发往同一台RS,对不带会话的应用来说这是最简单的会话保持方案。但它也有副作用——某个源IP流量特别大时,会长时间压在单台RS上,导致负载不均。我见过有人把persistence_timeout设成3600,结果一台机器被打爆另一台闲着。默认值或略高即可,除非业务确实需要长时间粘滞。

TCP_CHECK是Keepalived在Director上主动向RS发起TCP连接探活。这里有个容易忽略的细节:如果RS上对应端口只允许特定来源访问,探活会失败,Keepalived会把健康RS误判为宕机。我踩过这种坑,后来习惯在RS的iptables里单独放行Director的探活IP。

另外Keepalived里的lb_kind DR-g是对应的,如果用了NAT模式记得改成NAT。两个Director的配置除了state和priority不同,其他要保持一致,否则VRRP会出现脑裂。

配置完在Director上启动Keepalived:

systemctl enable --now keepalived

此时LVS规则由Keepalived自动维护,不再需要手动执行ipvsadm命令。用ipvsadm -L -n还能看到规则,但增删由Keepalived管理。

3.4 权重和容量怎么定

权重不是拍脑袋定的。我一般先做压测:分别压RS1和RS2,得到单机QPS上限,然后按QPS比例反推权重。比如RS1压测上限是8000 QPS,RS2是16000 QPS,权重就配1比2。权重真需要调整时,用以下命令在线修改,不用重启服务:

ipvsadm -e -t 192.168.100.100:80 -r 192.168.100.11:80 -g -w 3

容量规划上,DR模式下Director只处理入向请求,理论上单台Director可以支撑的并发连接数主要受内存限制。每个连接条目在内核里占的内存很小,4GB内存的Director扛几十万并发连接是常见的。但别忘了给Director留出足够的内核连接跟踪表空间,必要时调大net.netfilter.nf_conntrack_max,否则连接数上来之后新连接会被丢弃,表现就是莫名其妙的超时。

4. 空闲超时问题深挖:线上断连的头号元凶

4.1 空闲超时是怎么发生的

被问得最多的问题就是:“我的WebSocket连上之后,过一会儿就被断了,为什么?”

这就要说到LVS的连接表超时机制。IPVS在内核里维护一张连接表,记录每一条转发连接的元数据。一条连接如果空闲太久,IPVS会认为它已经结束了,把表项清掉。默认的清理时间是:

ipvsadm -L --timeout

输出结果一般是这样:

Timeout (tcp tcpfin udp): 900 120 300

TCP空闲900秒(15分钟)、TCP FIN状态120秒、UDP空闲300秒。如果一条TCP连接在15分钟内没有数据包经过LVS,连接表项就会被回收。问题在于:表项被回收后,客户端和后端其实还在维持着这条连接(比如WebSocket的ping/pong间隔超过了15分钟,或者某些长连接应用根本不发心跳)。当下一个数据包到达LVS时,LVS查不到对应表项,只能把它当作一条新连接重新调度,结果包被发到了另一台RS上,甚至直接被丢弃,原连接就断了。

还有一种常见场景是UDP:DNS、NTP这类短请求无所谓,但部分基于UDP的实时通信服务,300秒超时远不够,一旦空闲超过5分钟就掉线。

4.2 如何调整超时参数

调整方法很简单:

ipvsadm --set 3600 120 300

这会把TCP空闲超时改为3600秒,TCP FIN超时120秒,UDP空闲超时300秒。TCP FIN保持120秒没问题,UDP如果有长时间无数据的业务,也要相应调大。

但这里我要多说一句:超时改大不是没有代价的。连接表项是内存资源,超时越长,表里堆积的死连接越多。如果业务大部分是短连接、又开着很长的TCP超时,表项会被大量无效连接占满,新连接反而无法建立。商城里卖的“统一改大超时”方案,真到线上是要出事的。正确姿势是:

  • 短连接业务:保持默认900秒就好,别动。
  • 长连接、有心跳的业务:把TCP超时调到比心跳间隔大2到3倍,比如心跳30秒,超时设90秒足够,没必要上3600。
  • 无心跳但长时间空闲的连接:首先考虑在应用层加心跳,而不是无限调大LVS超时。

另外Keepalived的persistence_timeout会和这个超时产生叠加效果。持久连接模板的超时控制的是“同一个源IP是否继续粘滞到同一台RS”,它到期后连接不会被断开,只是重新参与调度。所以如果你的会话保持失效、出现请求跑到另一台RS的情况,查的不是ipvsadm --set,而是persistence_timeout。

4.3 长连接场景的更多处理技巧

除了调超时,还有几个内核参数能优化长连接场景。

expire_nodest_conn:默认0。如果设为1,当RS不可用(被Keepalived摘除)时,连接表里指向该RS的表项会被立即清理,而不是干等到超时。这样RS恢复后,老连接不会继续被错误地转发过去。我建议开启:

echo 1 > /proc/sys/net/ipv4/vs/expire_nodest_conn

expire_quiescent_template:Keepalived把RS标记为“静止”状态时,是否清理对应的持久连接模板,默认0。如果希望摘除RS后,原先粘滞到它的客户端马上被重新散列到别的RS,就把它设成1。

sloppy_tcp:允许IPVS在连接状态不完整时继续转发TCP包。如果你的环境里出现过连接表不同步、重启Director后大量连接异常的情况,这个开关能减少连接中断,但也会削弱对异常包的过滤能力,生产环境谨慎开。

对于WebSocket这类需要长时间维持连接的业务,除了调大LVS超时,我还会在应用层加心跳,并且把心跳间隔控制在30到60秒。这样即使未来LVS超时配置被重置或迁移,也不至于两分钟就断连。

5. 常见问题与排查技巧实录

5.1 典型报错速查表

我把自己和同行遇到的高频问题整理成了表,方便遇到问题时直接对号入座。

现象可能原因解决办法
ipvsadm -L -n报“No such file or directory”ip_vs模块未加载modprobe ip_vs,重新执行
客户端访问VIP不通Director上VIP没配或Keepalived没起来检查ip addr和keepalived状态
后端RS能通,但请求总打到固定一台RS调度算法是SH,或persistence_timeout过大换WLC,调小持久超时
RS已经被摘除,请求还是过去健康检查未生效,或摘除前连接表还有残留确认TCP_CHECK配置,开启expire_nodest_conn
客户端偶尔超时,连接瞬间断开空闲超时过短,表项被回收按4.2节调整ipvsadm --set
多台RS都在回ARP,RS收不到包或Director和RS抢VIP没有做ARP抑制按3.2节设置arp_ignore/arp_announce
Director性能不错但吞吐上不去网卡中断不均、连接跟踪表满开网卡多队列,调大nf_conntrack_max
Keepalived主备一直在切VRRP配置不一致或网络丢包对比vrrp_instance配置,检查advert_int超时

5.2 用连接表定位问题的思路

排查LVS问题,我有一套固定的操作顺序。先用ipvsadm -L -n确认规则在不在,再看连接表:

ipvsadm -L -n -c

连接表里每行都有连接状态。TCP场景下如果看到大量SYN_RECV状态堆积,说明包到了LVS但RS没有正确回应,赶紧去查RS的应用端口和防火墙。如果看到NONE状态很多,多半是UDP转发或者连接已经被回收。

再看统计数据:

ipvsadm -L -n --stats ipvsadm -L -n --rate

--stats看累计包数、字节数、连接数;--rate看每秒速率。通过对比两台RS的流量是否按权重比例分配,能快速判断调度是否正常。

如果确认LVS规则正常但请求还是不通,用tcpdump在Director上看包有没有进来,再到RS上看包有没有到达:

tcpdump -i eth0 host 192.168.100.100 and port 80

包到了RS但应用没反应,问题在RS;包根本没到RS,问题在网络或LVS转发路径上。用这个方式一步步缩小范围,比瞎猜效率高得多。

5.3 我踩过的三个实战坑

第一个坑是DR模式下忘了关RS的rp_filter反向路径过滤。某些发行版默认开启了rp_filter,RS收到VIP的数据包后,发现源路由和到达接口不一致,直接丢弃,表现就是LVS转发正常但服务一直超时。解决办法是确认RS网卡配置里net.ipv4.conf.all.rp_filternet.ipv4.conf.<网卡>.rp_filter为0或者2。

第二个坑是Keepalived的virtual_ipaddress配置里忘了指定网卡。默认它会把VIP挂在默认路由对应的网卡上,你要是服务器有多个网卡,VIP可能被挂到了业务流量根本不走的接口上,导致VIP能ping通的人和真正访问你的人不是同一条路径。后来我所有VIP配置都显式写成192.168.100.100/24 dev eth0,再没出过这事。

第三个坑是线上扩容时直接对RS执行ipvsadm -a添加新机器,忘了在RS上做ARP抑制。新RS一上线,整个集群的VIP ARP响应被打乱,客户端部分流量直连新RS,全部转发绕过Director,问题持续到大半夜才定位到是ARP广播冲突。扩容操作前一定把RS侧配置检查清单过一遍,尤其是ARP和sysctl项,宁可慢一点也不要带病上线。

6. 生产环境落地的一点个人体会

LVS配置本身并不复杂,复杂的是你对自己业务的流量模型有没有清晰的认知。是短连接还是长连接,需不需要会话保持,后端机器能力是否均衡,这些问题的答案直接决定了调度算法、超时参数和持久化时间该怎么配。我每接一个项目,第一件事永远是梳理业务流量特征,再动手写配置,而不是把上一套架构的配置原封不动复制过来。

最后再分享一个实用习惯:我会把所有LVS相关参数(算法、超时、权重、持久化时间、内核开关)写进配置管理里,并标注清楚每个参数的调整理由。这样每次变更都有据可查,线上出问题回滚也快。负载均衡是流量入口,改动影响面极大,宁可多花时间做预案,也不要在半夜靠手速救命。希望这篇文章能让你少踩几个我当年踩过的坑。

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

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

立即咨询