1. LVS到底解决什么问题
LVS(Linux Virtual Server)是章文嵩博士在1998年发起的开源项目,也是国内开发者主导的最早一批顶级开源项目之一。简单说,LVS就是把一堆Linux服务器组织成一个对外看起来像一台服务器的虚拟集群,通过IP层负载均衡把请求分发给集群里的真实服务器(RS)。它运行在内核态,转发性能极高,单机并发能力可以轻松达到百万级别,这是Nginx这类七层负载均衡很难企及的数字。
我第一次真正部署LVS是很多年前做电商大促备战的时候。当时流量预估比平时高好几倍,单台Nginx已经接近瓶颈,又不能在业务代码层面大动干戈,最后就是靠LVS在四层把流量均匀摊到多台Nginx入口上,一台挂了自动摘除,流量翻了三四倍,后端服务基本无感。这个场景直到今天依然是LVS最典型的用例:作为整个接入层的最前端,用极高的性能承接海量并发连接,再往下游分发。
LVS适合谁?如果你正在搭建服务器集群、需要水平扩展Web服务、或者研究K8s的kube-proxy时被iptables/IPVS模式搞懵,那这篇就是给你写的。K8s的kube-proxy的IPVS模式,底层用的就是LVS的ipvs内核模块。把LVS搞明白了,很多分布式系统的网络原理你都能一通百通。
需要先明确一个边界:LVS工作在OSI模型的四层(传输层),它不关心HTTP头部、URL、Cookie这些七层内容,只看IP和端口。所以它比Nginx这种七层负载均衡更快,但做不了基于URL的精细化路由。两者不是替代关系,而是配合关系——LVS在最前面扛流量,Nginx在后端做业务路由。这也是目前绝大多数中大型互联网企业接入层的标准架构。
LVS的核心组件主要有三个:ipvs(IP Virtual Server)是工作在内核态的负载均衡模块,真正干活的;ipvsadm是用户态的管理工具,负责配置和查看转发规则;Keepalived则负责后端健康检查和VIP漂移,实现高可用。这三个东西配合起来,才能组成一个完整的、可用的负载均衡集群。下面我一个个拆开讲,先把模式讲透,再给实操配置。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. LVS的三种工作模式:NAT、DR、TUN
LVS有三种工作模式:NAT(Network Address Translation,网络地址转换)、DR(Direct Routing,直接路由)、TUN(IP Tunneling,IP隧道)。这不是三个等价选项,而是三种适用场景完全不同的方案。我建议新手先别急着敲命令,把三种模式的流量路径彻底搞明白,后面排错会省一半时间。
2.1 NAT模式:最容易理解的网关模式
NAT模式把LVS节点当成一个网关。请求进来时,LVS把目标IP从VIP(虚拟IP)改成后端RS的真实IP,然后转发出去;RS处理完响应的数据包再回到LVS,LVS把源IP改回VIP,返回给客户端。
这个模式最大的特点就是请求和响应都必须经过LVS。好处是RS可以跑在私有网段,不用修改任何内核参数,甚至RS上的操作系统都不一定非得是Linux,只要支持ARP和TCP/IP协议栈就行。缺点是LVS本身容易成为瓶颈,因为进出的流量都要过它。如果业务是下载站、视频流这种下载量大、上传量小的场景,NAT模式会非常吃亏,因为响应流量的大头全压在LVS上。
所以我的建议是:NAT模式只适合小规模集群、测试环境,或者RS与客户端不在同一个二层网络、DR模式做不了的情况下用。生产环境,尤其是大流量场景,基本都选DR。
2.2 DR模式:生产环境最常用的模式
DR模式的核心思想是改写MAC地址。请求到达LVS后,LVS不改IP,只把数据链路层的目的MAC地址改成后端RS的MAC地址,然后扔到局域网里。RS收到包后,发现IP是自己的(VIP配置在lo接口上),就正常处理,响应时直接以VIP作为源IP返回给客户端,完全绕开LVS。
这个设计非常巧妙:请求流量小,经过LVS没问题;响应流量大,却直接从RS各自返回,LVS彻底摆脱了响应瓶颈。所以DR模式是目前生产环境采用最广泛的LVS模式,很多大型网站的接入层就是DR模式。
但DR模式有两个前提条件,缺一不可。第一,LVS和RS必须在同一个二层网络,因为要改MAC地址直接投递;第二,RS必须禁用对VIP的ARP响应,否则客户端直接ARP请求VIP时,RS会抢答,造成VIP冲突。第二个问题就是经典的"ARP问题",后面我专门讲。
配置DR模式时,每个RS上通常要执行这类操作:把VIP绑定到lo接口上,同时设置arp_ignore=1和arp_announce=2,让RS只响应目标IP是本机物理接口IP的ARP请求,避免抢答VIP。这个操作是所有DR模式部署里必须做的,漏掉任何一台RS,都会导致集群行为异常,而且非常难排查。
2.3 TUN模式:跨机房部署的隧道方案
TUN模式用IP隧道技术,LVS把请求包再封装一层新IP包,通过隧道发给远端的RS。RS解封装后处理请求,响应直接返回客户端。
这个模式唯一的优势就是RS可以与LVS跨网段甚至跨地域部署,适合多地多机房、RS分散部署的场景。但坏处也很明显:IP封装解封装有额外开销,性能比DR模式差;RS也需要支持隧道协议并配置相关内核参数;而且很多云环境对IPIP隧道支持不好。运维复杂度比DR高一大截,所以除非你有明确的跨机房诉求,否则一般不用TUN。
三种模式对比如下:
| 对比项 | NAT | DR | TUN |
|---|---|---|---|
| RS网络要求 | 私有网段即可 | 与LVS同二层网络 | 可跨网段 |
| RS操作系统要求 | 无特殊要求 | 支持ARP协议即可 | 需支持IPIP隧道 |
| RS是否需修改内核参数 | 否 | 必须防ARP抢答 | 需配置隧道相关参数 |
| 请求是否经过LVS | 是 | 是 | 是 |
| 响应是否经过LVS | 是 | 否 | 否 |
| LVS性能瓶颈 | 大(进出都过) | 小(只过请求) | 中(封装有开销) |
| 生产环境使用率 | 低 | 极高 | 低 |
我自己的经验:除非有特殊网络架构约束,否则无脑选DR模式。它性能最好,配置虽然比NAT多两步,但都是固定套路,踩过一次坑之后终身不会忘。
3. 实操配置:以DR模式为例完整部署
下面我以一套最小化但完整的DR模式集群为例,从拓扑规划开始,把配置每一步都过一遍。我假设你已经有三台Linux服务器,这个实验在虚拟机里做也可以,关键是把流程跑通。
3.1 拓扑规划与IP分配
先规划清楚角色和IP,这是我每做一个项目都会要求自己先做好的事。这里采用生产环境最常用的架构:
| 角色 | 主机名 | IP | 说明 |
|---|---|---|---|
| LVS负载均衡器(主) | lvs01 | 192.168.1.10 | 绑定VIP:192.168.1.100 |
| LVS负载均衡器(备) | lvs02 | 192.168.1.11 | 绑定VIP:192.168.1.100(备用) |
| RS真实服务器01 | rs01 | 192.168.1.20 | 提供Web服务,lo绑定VIP |
| RS真实服务器02 | rs02 | 192.168.1.21 | 提供Web服务,lo绑定VIP |
VIP(Virtual IP)就是对外提供服务的虚拟IP,客户端访问的是这个IP,而不是任何一台RS的具体IP。VIP可以由主LVS持有,主挂了备LVS自动接管,这就是Keepalived做的事,后面专门讲。
先说一下我为什么在主备上都准备了两台LVS。生产环境单台LVS本身也是一个单点,LVS挂了整个入口就断了,所以高可用集群里至少要有两台LVS节点,用Keepalived的VRRP协议做VIP漂移。这个在设计阶段就要想好,不要等跑起来再补。
3.2 RS端配置:解决ARP问题
DR模式下,每台RS都要执行下面的脚本。我来逐行解释为什么需要这些配置,而不是让你直接复制了事。
bash复制# rs_config.sh 在每台RS上执行
VIP=192.168.1.100
ifconfig lo:0 $VIP netmask 255.255.255.255 up
route add -host $VIP dev lo:0
echo "1" > /proc/sys/net/ipv4/conf/lo/arp_ignore
echo "2" > /proc/sys/net/ipv4/conf/lo/arp_announce
echo "1" > /proc/sys/net/ipv4/conf/all/arp_ignore
echo "2" > /proc/sys/net/ipv4/conf/all/arp_announce
第一行把VIP绑定到RS的lo接口的别名lo:0上,掩码必须是255.255.255.255,也就是只匹配这个IP本身。这里有个新手容易犯的错:有人会写成ifconfig lo:0 $VIP netmask 255.255.255.0,一旦掩码不是全32位,RS会认为自己拥有整个网段,路由表直接错乱,后果很严重。
后面的arp_ignore和arp_announce就是解决前面说的ARP抢答问题。arp_ignore=1的意思是:当本机收到ARP请求时,只有请求的IP正好是本机某个接口的IP,才回应。arp_announce=2的意思是:发送ARP通告时,始终使用出口接口上的IP地址作为源地址,而不是使用VIP。两个参数配合,就保证了VIP虽然是RS的本地地址,但RS永远不会对外宣告"我拥有VIP",从而避免与LVS的VIP冲突。
这里要注意,/proc/sys/net/ipv4/conf/all/和/proc/sys/net/ipv4/conf/lo/下的值要同时设置,因为内核在ARP处理时会取两者中约束更严格的那个。只改lo不换all,很多内核版本下不生效,这个坑我见过不止一个人踩。
3.3 LVS端配置:ipvsadm实战
LVS端做的事情就是安装ipvsadm、配置VIP转发规则、把RS加进来。在LVS节点上执行:
bash复制# 安装ipvsadm
yum install -y ipvsadm # CentOS/RHEL系列
apt install -y ipvsadm # Debian/Ubuntu系列
# 添加一个虚拟服务,-A表示新增,-t表示TCP,VIP:80对外提供服务
ipvsadm -A -t 192.168.1.100:80 -s wrr
# 给虚拟服务添加两台RS,-r指定RS地址,-g表示DR模式,-w设置权重
ipvsadm -a -t 192.168.1.100:80 -r 192.168.1.20 -g -w 1
ipvsadm -a -t 192.168.1.100:80 -r 192.168.1.21 -g -w 2
# 查看规则
ipvsadm -L -n
我解释一下这三条命令的完整含义。-s wrr指定调度算法是加权轮询(Weighted Round Robin),两台RS权重分别是1和2,意味着rs02会被分配两倍于rs01的请求。-g是DR模式的标志,对应NAT模式是-m,TUN模式是-i,这三个参数别搞混了,写错模式的话整个转发链路全断。
配置完之后,用ipvsadm -L -n看到的输出大概是:
text复制IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
-> RemoteAddress:Port Forward Weight ActiveConn InActConn
TCP 192.168.1.100:80 wrr
-> 192.168.1.20:80 Route 1 0 0
-> 192.168.1.21:80 Route 2 0 0
看到Route这个单词,就说明RS已经以DR模式挂载成功了。ActiveConn和InActConn表示当前活跃和不活跃连接数,这是判断LVS转发是否正常的第一手数据。压测的时候盯着这两列看,如果只有一台RS的计数在涨,那大概率是调度算法或网络问题,后面排查章节我会展开。
3.4 用sysctl持久化内核参数
前面RS端的ARP参数是临时生效的,重启就没了。生产环境要做持久化,把参数写进/etc/sysctl.conf:
bash复制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
然后执行sysctl -p让配置立即生效。LVS端的VIP绑定和ipvsadm规则也可以用同样的思路做开机自启,不同发行版方式不一样,CenOS/RHEL可以用/etc/rc.local,或者把ipvsadm规则保存到/etc/sysconfig/ipvsadm(执行ipvsadm-save生成)。生产环境我更推荐用Keepalived统一管VIP和规则,这样主备切换时规则也会跟着漂移,省心很多。
4. 调度算法怎么选:从原理到实战
LVS支持的调度算法很多,但绝大多数人实际用到的就三四种。我先讲清楚算法分类和适用场景,再给一个可以直接参考的选型思路。
4.1 静态调度算法:不看后端负载,按既定策略分配
静态算法指LVS不看RS当前负载状态,只按照固定策略选择RS。最常用的有两个。
轮询(RR,Round Robin):请求依次轮流发给每台RS,A、B、A、B这样转圈。如果所有RS配置完全一样、处理能力相同,RR是最简单公平的算法。
加权轮询(WRR,Weighted Round Robin):给每台RS配一个权重,权重高的RS分到的请求多。比如rs01权重1,rs02权重2,那么每3个请求里,rs01大概处理1个,rs02处理2个。这是我最常用的算法,因为生产环境很少有完全同配的机器,老机器权重低一点、新机器权重高一点,是合理的做法。
4.2 动态调度算法:根据后端连接数实时调整
动态算法会看RS当前的负载情况,主要是连接数,然后动态调整分配策略。
最少连接(LC,Least Connections):哪个RS当前活跃连接数最少,就给谁发。这个算法适合长连接场景,比如WebSocket、数据库连接池等,因为连接时长差异大,RR会导致连接堆积在不均匀的机器上。
加权最少连接(WLC,Weighted Least Connections):最小连接数除以权重后再比较,取结果最小的RS。综合了"机器能力"和"当前负载"两个维度,是目前很多生产环境的默认推荐算法。
最短期望延迟(SED,Shortest Expected Delay):在WLC基础上,公式变成(连接数+1)/权重,更偏向权重高的机器。
动态算法的前提是LVS能实时拿到每台RS的连接数,这个方法本质上是"推测负载",并不精确。如果你的后端请求处理时间差异极大,比如有的接口耗时10毫秒,有的耗时2秒,动态算法也未必能很好地均衡。
4.3 算法选型建议和我的真实经验
我的选型经验,可以归纳成下面几条简单规则:
| 业务类型 | 推荐算法 | 原因 |
|---|---|---|
| 后端RS配置完全一致,请求耗时相近 | RR | 最简单,开销最小 |
| 后端RS配置不一致,新旧机器混用 | WRR | 按权重区分机器能力 |
| 长连接业务,如WebSocket | LC或WLC | 避免连接堆积 |
| 大部分标准Web服务 | WRR或WLC | 均衡效果好,可控性强 |
还有一个很重要的认知:LVS的调度算法是"分发时决定",它不会因为你后端某台机器已经卡死就自动少发请求。动态算法里的连接数只能反映TCP层的连接状态,不能反映应用层的健康程度。比如后端进程出现死锁,TCP连接还挂着,LVS依然会把新请求分过去。所以,健康检查必须靠Keepalived这层来做,不要指望调度算法能处理故障。这就是为什么我反复强调LVS和Keepalived是一套组合拳。
5. 用Keepalived做健康检查和VIP漂移
LVS本身不提供后端健康检查功能,RS挂了他还在傻傻转发。Keepalived就是来补这个短板的:一方面通过VRRP协议实现LVS节点的主备切换,另一方面主动探测RS的健康状态,一旦发现异常就自动把RS从转发规则里摘掉。
5.1 Keepalived的核心作用
Keepalived最初是为LVS设计的,后来才独立成通用的高可用方案。它的工作逻辑是这样的:主备LVS节点之间通过VRRP协议发送心跳报文,主节点定期宣告"我活着",备节点收到就不动作;一旦备节点连续几个周期收不到主节点的心跳,就认为主节点挂了,立刻把VIP绑定到自己网卡上,并接管ipvsadm规则。整个切换过程对客户端完全透明,VIP没变,TCP连接可能会断,但新连接立刻就能用。
Keepalived的健康检查则是LVS规则的"守门员":每隔几秒用TCP或HTTP方式探测后端RS,探测失败就执行ipvsadm -d把RS从规则里删除,恢复后再加回来。这个机制保证了LVS永远不会把请求分发给一台已经挂掉的机器。
5.2 主LVS节点的Keepalived配置
下面是主LVS节点的典型配置:
bash复制# /etc/keepalived/keepalived.conf lvs01主节点
global_defs {
router_id lvs01
}
vrrp_instance VI_1 {
state MASTER
interface eth0
virtual_router_id 51
priority 100
advert_int 1
authentication {
auth_type PASS
auth_pass 123456
}
virtual_ipaddress {
192.168.1.100/24 dev eth0
}
}
virtual_server 192.168.1.100 80 {
delay_loop 6
lb_algo wrr
lb_kind DR
protocol TCP
real_server 192.168.1.20 80 {
weight 1
TCP_CHECK {
connect_timeout 3
nb_get_retry 3
delay_before_retry 3
}
}
real_server 192.168.1.21 80 {
weight 2
TCP_CHECK {
connect_timeout 3
nb_get_retry 3
delay_before_retry 3
}
}
}
备节点配置几乎一样,只有两个关键区别:state改成BACKUP,priority改成比主节点小的值,比如90。virtual_router_id 51必须两边一致,这是VRRP分组的标识,不一致的话两台机器各玩各的,VIP就会冲突。advert_int是心跳间隔,默认1秒,我习惯保持默认,太短会增加无谓的广播报文,太长则故障切换时间变长。
配置完成后,systemctl start keepalived,然后用ip addr show看VIP是否绑定在了eth0上。正常情况下,主节点能看到192.168.1.100/24,备节点看不到。同时用ipvsadm -L -n确认LVS规则已经被Keepalived自动加载。
5.3 故障切换测试和脑裂问题
配置完一定要做切换演练,不要等到线上出故障才发现配置有问题。测试方法很简单:
bash复制# 在主节点上停掉Keepalived,模拟主节点宕机
systemctl stop keepalived
正常情况下,几秒内备节点会接管VIP,你可以从另一台机器持续ping 192.168.1.100验证,整个过程只有几个ICMP包丢失。再用ip addr show在备节点上确认VIP存在,说明漂移成功。
这里有一个生产环境必须警惕的问题:脑裂。如果主备之间的VRRP心跳链路断了,比如交换机端口故障、网络抖动,两台LVS都会认为对方死了,同时把VIP绑到自己的网卡上。此时VIP在网络里出现两份,客户端请求会随机到达其中任意一台,导致服务异常。防止脑裂的办法在架构层面:VRRP心跳应该走独立的物理链路或者专门的带外管理网络,尽量避免和业务流量共用同一链路。Keepalived本身没有自动防脑裂的机制,这是很多新手不知道的坑。
6. 常见报错与排查实录
LVS部署过程中,有几个报错是高频出现的。网上搜"lvs中报错different numbers of ports"能搜出一堆提问贴,说明这是很多人都遇到过的坎。我把自己踩过的、帮别人排过的问题整理成速查表,每个都给排查思路。
6.1 "different numbers of ports"报错怎么处理
这个报错发生在用ipvsadm -A添加虚拟服务时,系统提示端口数不一致。原因非常具体:你在同一个VIP上先添加了一个多端口服务,再尝试添加单端口服务,或者反过来。
举个例子,如果你先执行了:
bash复制ipvsadm -A -t 192.168.1.100:80-90 -s rr
那么192.168.1.100:80-90这个端口范围覆盖了80到90一共11个端口。此时再尝试:
bash复制ipvsadm -A -t 192.168.1.100:80 -s rr
内核就会发现,你想添加的80端口已经属于前面那个端口范围了,于是报"different numbers of ports"。解决方法是:删掉或修改原有的端口范围规则,或者把新的虚拟服务改成与现有范围一致的端口定义。如果只是业务需要从单端口扩展到多端口,建议规划好端口范围后的完整规则,删掉重建,避免这种冲突。
这个报错提醒我们一个很重要的设计原则:LVS的VIP和端口组合是唯一的,一个VIP下的虚拟服务端口范围要一次规划好,不要今天加一个80,明天加一个443,后天再扩一个8080-8090,很容易陷入自相矛盾的配置里。
6.2 后端RS始终收不到请求
这是DR模式最经典的坑,症状是LVS配置看着完全正常,ipvsadm -L -n也能看到RS,但测试时请求全部失败,后端日志里一条访问记录都没有。
排查路径按顺序走:
- 查RS的ARP配置:确认RS上
arp_ignore和arp_announce已经设置,且lo:0的VIP绑定正确。这是DR模式的第一大坑,也是最常见的坑,超过一半的问题是这里出的。可以直接在RS上看ip addr show lo,确认VIP已经在lo接口上。 - 确认LVS和RS在同一个二层网络:DR模式要求二层可达,如果中间隔了路由器,MAC改写后数据包根本到不了RS。用
ip neigh看看LVS能否解析到RS的MAC地址。 - 在RS上抓包验证:在RS上执行
tcpdump -i any host 192.168.1.100 -n,然后从客户端发起一个请求。如果RS能收到目标IP为VIP的包,说明LVS转发路径是通的,问题出在响应路径或RS的服务上;如果RS一个包都收不到,问题就在LVS侧或二层网络。 - 确认RS的Web服务监听在正确端口:检查服务是否启动,端口是否监听,防火墙是否放行。这一步看似废话,但很多人排查了半天,最后发现是RS上的iptables拦了包,或者服务压根没起。
6.3 其他高频问题速查
| 症状 | 可能原因 | 排查命令或手段 |
|---|---|---|
| VIP ping不通 | VIP未绑定、Keepalived未启动 | ip addr show检查VIP |
| 主备切换后服务不可用 | 备节点ipvsadm规则未同步 | ipvsadm -L -n确认规则 |
| LVS转发正常但连接数不均衡 | 调度算法与业务不匹配 | 观察ActiveConn分布 |
| RS响应慢但连接数不高 | 后端应用瓶颈,与LVS无关 | 检查RS的CPU、磁盘等 |
| 客户端连接频繁超时 | LVS服务端口未放行或RS异常 | 查iptables,查Keepalived日志 |
修改了/etc/sysctl.conf不生效 |
忘记sysctl -p |
执行并验证参数 |
还有一个小技巧,排查时看日志比猜高效得多。Keepalived的日志一般在/var/log/messages(CentOS系)或journalctl -u keepalived(Systemd类系统)里,它会明确告诉你VIP漂移、健康检查失败、RS摘除/恢复这些关键事件。我在线上排故障,第一步永远是先看日志,再动手试,顺序不能反。
7. 从LVS延伸出去的几个方向
讲到这,LVS的核心内容基本覆盖完了。最后聊几句LVS在更宏观的架构里的位置,以及我做完这套东西之后总结的几点体会。
第一,LVS和Nginx不是二选一的关系,而是互补的。LVS管四层转发,扛流量;Nginx管七层路由,做业务分发。典型的接入层架构是"LVS(多台,Keepalived做高可用)→ Nginx集群 → 应用服务器"。理解了这一层,你在看很多公司公布的架构图时就不会懵。
第二,LVS的IPVS模式被K8s的kube-proxy吸收后,成了云原生网络的重要基石。K8s的Service负载均衡,在IPVS模式下就是每条规则对应一个ClusterIP,请求按调度算法分给后端的Pod。所以搞懂LVS,对理解K8s的Service、NodePort、ClusterIP这些概念都有直接帮助。如果你后面要搭K8s集群、排查K8s网络问题,LVS这套底子会让你事半功倍。
第三,集群高可用和故障转移是运维永恒的主题。LVS + Keepalived这套组合虽然年代久远,但稳定可靠,直到今天依然是很多企业的标配。相比各种花哨的新方案,它在绝大多数场景下都是最省心、最高性能、最不容易出幺蛾子的选择。我个人的经验是:不要为了追新而追新,方案选型要看团队维护能力和业务规模,LVS这套东西用十年了,它的上限和下限都很清楚,不会给你惊喜,但更不会给你惊吓。
最后给刚上手的朋友一个建议:别急着在生产环境操作,先用三台虚拟机把DR模式集群跑通,亲手配一遍ARP参数、ipvsadm规则、Keepalived主备切换,再把RS主动停掉看LVS怎么摘除它。这一套流程走完,你对Linux集群和负载均衡的理解会有一个质的飞跃。踩过的坑越多,后面遇到生产事故时心里就越有底。
