news 2026/9/14 13:04:05

云原生时代的LVS:Kubernetes入口高可用架构实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
云原生时代的LVS:Kubernetes入口高可用架构实战

上个月帮一家做私有化交付的公司做技术评审,他们的Kubernetes集群前面直接用Nginx做入口,高峰期Nginx的CPU被打到90%。我翻完方案第一句话就问:为什么不在Nginx前面加一层四层负载均衡?对方愣了几秒——都云原生时代了,还用LVS这种老古董吗?这个问题其实很有代表性。LVS(Linux Virtual Server)确实不是什么新鲜事物,它在Linux内核里已经存在了二十多年,但在云原生架构里,它不仅没有退休,反而是被误解最深、也最容易被忽略的一个基础组件。

这篇文章我想把LVS和云原生之间的关系彻底讲透:它为什么在Kubernetes生态里无处不在,在实际项目中是怎么和K8s集群配合的,以及当我真把它架在集群前面做流量入口时,该怎么配、会踩哪些坑。如果你是正在设计集群流量接入层的架构师、刚接手K8s集群的运维工程师,或者单纯好奇kube-proxy背后到底发生了什么,这篇文章都值得你花15分钟读完。

1. LVS不是老古董:它在云原生架构里到底承担什么角色

1.1 从IPVS说起:一台Linux服务器为什么能扛住百万级连接

先对齐一下概念。LVS(Linux Virtual Server)是一套基于Linux内核实现的负载均衡方案,它的核心载体是内核里的IPVS(IP Virtual Server)模块,配套管理工具是ipvsadm。注意一个关键点:LVS工作在四层,也就是TCP/UDP传输层。数据包进入服务器网卡后,经过内核netfilter框架的钩子,IPVS模块会根据预先配置的调度算法,把这个包转发给后端的某台真实服务器(Real Server,简称RS)。

整个过程中,负载均衡器只看包的五元组——源IP、目的IP、源端口、目的端口、协议类型——它完全不会打开包体去检查里面的HTTP头或者业务数据,也没必要拆开看。用一个不太严谨但很贴切的类比:IPVS就像一个高速收费站的分流员,他不过问每辆车里装的是什么货物,也绝不去拆开货箱检查,只看车牌号(IP)和进出口(端口),然后根据调度策略把车引到对应的车道。

正是这个设计,让单台LVS节点可以做到百万级并发连接、数十Gbps的吞吐量。在云原生环境里,容器和微服务动辄上百上千,如果每个服务的请求都要经过应用层网关去解析转发,CPU必然成为瓶颈。更高性价比的做法,就是让LVS在四层先把流量全部扛住,再按需分发到上层网关或后端服务。

1.2 云原生流量模型里的分层:四层与七层各司其职

在Kubernetes生态里,负载均衡这件事其实是分层的。NodePortLoadBalancer类型的Service本质上解决的是四层接入,IngressGateway API解决的是七层路由。很多人容易把这几个概念混在一起,实际架构设计里它们的职责是非常清晰的。

维度四层负载均衡(LVS)七层负载均衡(Nginx/Envoy/HAProxy)
工作层级TCP/UDP传输层HTTP/HTTPS应用层
转发内容仅IP + 端口可解析URL、Host、Header、Cookie
性能极高(内核态转发)较高(用户态解析与转发)
路由粒度到Service/端口级别到具体路径/域名级别
典型场景集群入口、海量流量收敛网关路由、限流、鉴权、灰度发布

不管你是用Nginx Ingress还是Envoy Gateway,它们前面都需要一个能抗住大流量冲击的四层入口。K8s集群内部东西向流量有kube-proxy在负责,但南北向流量进集群的第一道门,在绝大多数生产架构里都是四层负载均衡。LVS在这层的位置,不是"可选项",而是"默认项"。

2. LVS的四种工作模式,以及为什么DR模式是云原生首选

2.1 DR模式:真正让流量"飞"起来的DSR模型

LVS最核心、也是性能最好的工作模式是DR(Direct Routing,直接路由)。要理解DR模式,需要先理解一个概念:DSR(Direct Server Return,直接服务器返回)。

在DR模式下,数据包的流转过程是这样的:

  1. 客户端向VIP(例如10.0.0.100:80)发起请求。
  2. LVS节点收到请求包,根据调度算法选中一台真实服务器(RS),然后只改写数据帧的目标MAC地址为RS的MAC地址,再从和RS处于同一二层网络的网卡把这个帧发出去。
  3. RS收到这个帧后,发现目的IP(VIP)配置在它自己的lo:0接口上,于是正常接收并处理请求。
  4. RS处理完响应时,直接把源IP设置为VIP,通过自己的默认路由把响应包回给客户端,完全不需要经过LVS节点。

这个模型的高明之处在于:LVS只参与了请求的半个生命周期,响应流量直接由后端服务器回给客户端。这意味着LVS的瓶颈至少被砍掉了一半。对响应流量通常远大于请求流量的业务——比如视频点播、文件下载、图片服务、大页面API——DR模式的效果极其显著。

那为什么DR模式是云原生首选?因为K8s集群承载的大多是微服务API,请求包小、响应包大,DR模式正好把入口节点的压力降到最低。但DR模式不是没有代价,最大的代价就是ARP问题。VIP同时配置在LVS节点和后端RS的lo:0接口上,如果不做ARP隔离,整个二层网络会乱套。所以DR模式要求所有RS必须做两个配置:把VIP绑定在lo:0接口上,并抑制ARP通告。

下面是RS节点上必须要做的ARP抑制配置:

# /etc/sysctl.conf 中追加 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 net.ipv4.conf.eth0.arp_ignore = 1 net.ipv4.conf.eth0.arp_announce = 2

arp_ignore=1的含义是:只回答目标IP是本机接口IP的ARP请求。arp_announce=2的含义是:发送ARP通告时,总是使用出接口的IP作为源地址,而不是用VIP。这两个参数组合起来,就能让RS在持有VIP的情况下,不去抢答原本属于LVS节点的ARP请求。很多人配置完lo接口就以为完事了,实际经验是必须把all和实际业务网卡(比如eth0)一起配上,否则在某些内核版本上依然会出问题。

2.2 NAT模式:实现简单,但要认清它的瓶颈

NAT模式下,LVS充当的是一台标准网关:请求进来时,LVS把目的IP从VIP改成RS的IP;RS处理完后,响应包要先回到LVS,LVS再把源IP从RS的IP改回VIP,然后发给客户端。也就是说,请求和响应都要经过LVS。

NAT模式的优点是对后端网络没有任何要求,不需要RS绑VIP,也不需要ARP抑制。但它的问题也很明显:LVS单节点的转发能力就是瓶颈,一旦流量超过连接跟踪表容量或CPU处理能力,整个系统就扛不住了。在高并发场景下,NAT模式很难支撑大规模集群的流量接入需求。

那NAT模式在云原生里还有用吗?有。当你用LVS转发流量到Pod CIDR网段,而Pod网络和Node网络不在同一个二层时,DR模式的二层直连就无法工作,NAT模式反而是更务实的选择。

2.3 Tunnel与FULLNAT:适配容器网络复杂性的进阶形态

Tunnel模式解决的是跨网段问题。LVS把原始数据包整个封装在一个IP隧道里发给RS,RS解开封装后拿到原始包再处理,响应则直接回给客户端。这种方式适合后端RS分布在另一个机房、另一个网段,二层完全不通的场景。近几年的容器网络方案里,IPIP隧道、VxLAN这些概念,本质上和Tunnel模式的思想是一脉相承的。

FULLNAT则是云环境下非常常见的改进方案。它在NAT的基础上更进一步,不仅改目的地址,还把源地址也换成LVS自身的IP。这样一来,RS看到的请求全部来自LVS,响应包自然就会回给LVS,再由LVS返回客户端。这个模型完全规避了DR模式对二层网络的依赖,是很多云厂商SLB产品的基础实现。代价是LVS节点压力更大,更依赖连接跟踪表,对节点规格的要求也更高。

3. 落地Kubernetes:LVS在云原生生态中的三条路线

3.1 kube-proxy的ipvs模式:集群内部流量的隐形功臣

很多人在用K8s的时候根本没意识到,自己早就用上LVS了。如果你在kube-proxy启动参数里加过--proxy-mode=ipvs,那LVS其实就运行在集群的每个Node节点上。这是LVS在云原生世界里最广泛、也最隐形的一条落地路线。

kube-proxy的ipvs模式做的事情是:每当集群里创建一个Service,它就在内核IPVS里创建一个虚拟服务器;每当有新的Pod Endpoint加入,它就通过netlink接口往这个虚拟服务器里添加一条真实服务器记录。流量到达Node节点后,直接通过IPVS的哈希查找命中对应的后端Pod,然后转发过去。

为什么ipvs模式比传统的iptables模式性能好这么多?核心区别在查找机制。iptables基于规则链表顺序遍历匹配,Service数量一旦上千,规则链变长,CPU消耗呈线性上涨;ipvs基于哈希表直接索引,时间复杂度是O(1),不管集群里有多少个Service,匹配效率几乎不受影响。根据社区压测数据,Service数量超过几千个之后,iptables模式的CPU占用会明显劣化,而ipvs模式基本是一条平稳的直线。

kube-proxy的ipvs模式默认调度算法是rr(轮询)。需要会话保持的场景可以设置sh(源地址哈希),也可以根据后端权重配置wrr。这些参数都在kube-proxy的配置文件的ipvs字段里:

apiVersion: kubeproxy.config.k8s.io/v1alpha1 kind: KubeProxyConfiguration mode: "ipvs" ipvs: scheduler: "rr" syncPeriod: 30s

3.2 自建集群入口:LVS + Keepalived做高可用四层门面

对于私有化交付、物理机机房托管这类场景,没办法依赖云厂商的SLB,如果只靠Nginx Ingress做入口,流量直接打到Ingress节点,一旦Ingress节点故障,整个集群访问就断了。这时候,在Ingress层之前架一组LVS,用Keepalived维护一个VIP,流量先到VIP,再由LVS把请求转发给若干台Ingress Controller节点,是业内非常经典的方案。

拓扑结构上就是:

外部请求 -> VIP(LVS + Keepalived)-> Ingress Controller节点 -> Service -> Pod

这组LVS本质上就是一个自建的四层LoadBalancer。Keepalived通过VRRP协议保证VIP在LVS主备节点之间漂移,只要还有一台LVS节点存活,集群入口就不会断。这也是后面第4章要完整演示的方案。

3.3 云厂商的LB底座:你可能已经在用SaaS化的LVS

再往大了说,很多公有云厂商早期SLB产品就是LVS的封装或深度改造。它们把LVS的多实例管理、健康检查、证书、监控全部做成控制台能力,底层跑的依然还是那些内核模块。后来为了适配云网络的特殊性,又陆续加入了FULLNAT、Tunnel等改良模型。

理解这一点对云原生架构师很重要。当你在云上创建一个负载均衡实例并绑定到K8s Service时,你实际上大概率正在使用一个被高度封装过的"LVS"。它之所以能扛住量,正是因为底层数据面是基于LVS架构的。所以别觉得LVS是过时技术,它可能就以另一种形态陪着你跑了很久。

4. 实战:在自建K8s集群前面搭一套LVS双机高可用入口

4.1 环境规划与角色分配

直接上一个我最近在测试环境里搭过的真实配置,四台机器就能跑通。网络规划如下:

组件IP地址角色说明
LVS主节点10.0.0.31主负载均衡节点,运行Keepalived + IPVS
LVS备节点10.0.0.32备负载均衡节点,运行Keepalived + IPVS
K8s Node110.0.0.11运行Nginx Ingress Controller,NodePort暴露30080
K8s Node210.0.0.12运行Nginx Ingress Controller,NodePort暴露30080
VIP10.0.0.100对外服务的虚拟入口地址
业务域名demo.cloud-native.local解析到VIP,测试访问用

这个规划的思路是:对外只暴露一个VIP,LVS把VIP的80端口流量转发到两个K8s节点的30080端口,也就是Ingress Controller的NodePort端口。两个LVS节点通过Keepalived做高可用,两个K8s节点上的Ingress Controller通过K8s自身的Deployment多副本机制保证冗余。

4.2 Keepalived安装与VRRP配置

首先在两台LVS节点上安装Keepalived和ipvsadm:

# CentOS/RHEL yum install -y keepalived ipvsadm # Ubuntu/Debian apt install -y keepalived ipvsadm

主节点/etc/keepalived/keepalived.conf里VRRP部分的配置如下:

global_defs { router_id lvs_keepalived_1 } vrrp_instance VI_1 { state MASTER interface eth0 virtual_router_id 51 priority 100 advert_int 1 authentication { auth_type PASS auth_pass 1234 } virtual_ipaddress { 10.0.0.100/24 dev eth0 label eth0:0 } }

备节点配置基本一致,只需要把router_id改成lvs_keepalived_2state改成BACKUPpriority改成90。这里有个经验点:主备优先级差值建议控制在10到20之间。差值太小,网络抖动时容易发生反复抢占;差值太大,主节点故障恢复后无法快速把VIP抢回来,只能等备节点再次故障才能切换,同样不可控。

VRRP实例里的virtual_router_id在同一二层网络里必须唯一,否则多组Keepalived实例会互相干扰,这是排查"VIP飘来飘去"类问题时最先要检查的项。

4.3 LVS转发规则与健康检查

LVS的转发规则可以直接用ipvsadm命令配置,但更好的做法是把规则写进Keepalived的virtual_server配置块里,让Keepalived根据后端健康状态动态增删IPVS规则。主备节点都放同一份virtual_server配置即可。

virtual_server 10.0.0.100 80 { delay_loop 3 lb_algo wrr lb_kind DR persistence_timeout 60 protocol TCP real_server 10.0.0.11 30080 { weight 100 TCP_CHECK { connect_timeout 3 nb_get_retry 3 delay_before_retry 3 } } real_server 10.0.0.12 30080 { weight 100 TCP_CHECK { connect_timeout 3 nb_get_retry 3 delay_before_retry 3 } } }

解释几个关键参数:

  • delay_loop 3:健康检查周期3秒一次。
  • lb_algo wrr:加权轮询调度算法,后端权重不同时用wrr更灵活。
  • lb_kind DR:指定DR模式。
  • persistence_timeout 60:会话保持时间60秒,来自同一客户端的请求在一定时间内固定转发到同一台后端。
  • TCP_CHECK:通过TCP连接探测后端NodePort是否存活。

为什么不在这里直接用HTTP_GET探测应用层健康状态?因为LVS在这一层只负责四层转发,TCP连接探测已经足够判断NodePort背后的Ingress Controller是否活着。应用层的健康检查应该交给Ingress自身的探针去处理,不要在LVS层重复做,链路越长越难排障。

配置完成后启动服务:

systemctl enable keepalived systemctl start keepalived

启动后先看IPVS规则是否生成:

ipvsadm -Ln

正常会输出类似这样的结果:

IP Virtual Server version 1.2.1 Prot LocalAddress:Port Scheduler Flags -> RemoteAddress:Port Forward Weight ActiveConn InActConn TCP 10.0.0.100:80 wrr -> 10.0.0.11:30080 Route 100 0 0 -> 10.0.0.12:30080 Route 100 0 0

4.4 RS(K8s节点)端的VIP绑定与ARP抑制

DR模式下,后端机器必须把VIP绑定到lo:0并抑制ARP。在两台K8s节点上执行:

# 临时生效 ip addr add 10.0.0.100/32 dev lo label lo:0

要永久生效,创建/etc/sysconfig/network-scripts/ifcfg-lo:0

DEVICE=lo:0 IPADDR=10.0.0.100 NETMASK=255.255.255.255 ONBOOT=yes

注意这里有个特别重要但很多人会忽略的细节:lo:0的掩码必须配置为32位,也就是255.255.255.255。如果配成255.255.255.0这类普通掩码,lo:0会把自己的子网广播出去,在K8s节点所在的二层网络里制造广播风暴,表面上表现为整个局域网网络卡顿、偶发不通。

然后修改/etc/sysctl.conf,追加:

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 net.ipv4.conf.eth0.arp_ignore = 1 net.ipv4.conf.eth0.arp_announce = 2

执行sysctl -p让配置生效。配置完成后,用ip addr show lo确认VIP已经绑上,用cat /proc/sys/net/ipv4/conf/all/arp_ignore确认值已经是1。

4.5 连通性验证与故障演练

全部配置完成后,开始验证。先在客户端机器上访问VIP:

curl -I http://10.0.0.100

如果返回了Nginx Ingress Controller的响应头,说明整条链路已经通了。接着模拟故障:

# 在主LVS节点上 systemctl stop keepalived

然后在备节点上立刻查看VIP是否漂移过来:

ip addr show eth0 | grep 10.0.0.100

如果VIP出现在备节点上,说明VRRP高可用生效。再启动主节点的Keepalived,观察VIP是否回切。如果配置了nopreempt非抢占模式,主节点恢复后VIP不会立刻回切,等备节点再次故障时才切换,这种模式适合不希望频繁切换的生产环境;没有nopreempt则主节点一旦恢复,VIP马上回切。两种模式没有绝对优劣,取决于你对"切换成本"和"流量震荡"的容忍度。

5. 生产环境跑了大半年之后,我踩过的那些坑

5.1 conntrack满了之后,新连接直接被丢

如果你用的是NAT模式或FULLNAT模式,LVS会大量创建连接跟踪条目。系统默认的nf_conntrack_max通常是65536,在高并发下这个值很快会被打满。一旦打满,新连接会被直接丢弃,而且因为连接跟踪表满这个原因非常隐蔽,排查半天才发现不是应用问题。

解决方式:

sysctl -w net.netfilter.nf_conntrack_max=1048576 sysctl -w net.netfilter.nf_conntrack_tcp_timeout_established=300

注意nf_conntrack_max不是越大越好,每条连接跟踪记录大约占用300字节内存,100万条就是接近300MB。需要根据机器规格权衡。另外可以调低nf_conntrack_tcp_timeout_established,让已建立的TCP连接更快从跟踪表里过期释放,变相提高表的利用率。

5.2 RS端的ARP抑制配置不彻底,引发VIP冲突

DR模式最容易踩的坑就是ARP抑制配置不完整。很多人只在lo接口上配置了arp_ignorearp_announce,忽略了all接口和实际业务网卡。这样在某些内核版本上,RS收到来自其他接口的ARP请求时依然会用VIP应答,导致VIP被多台机器同时应答,流量出现随机转发到错误RS的诡异现象。

经验做法是:所有涉及arp_ignorearp_announce的配置,都要同时覆盖loall和实际业务网卡三个维度,配置后执行sysctl -p生效,再用cat /proc/sys/net/ipv4/conf/eth0/arp_ignore逐一确认。另外,新版内核里最好把net.ipv4.conf.all.arp_filter=1也加上,防止VIP在RS之间被意外互答ARP。

5.3 会话保持时间参数:设置不好,要么雪崩,要么Session乱串

persistence_timeout这个参数值得单独拿出来说。它的作用是让来自同一客户端的连接在一定时间内始终转发到同一台后端RS。逻辑上看没问题,但实际生产里两个极端都见过:

  • 设置太长(比如3600秒)时,某台RS故障或K8s滚动发布期间,大量客户端连接会同时被丢弃,表现为大面积请求失败,和雪崩效果一样。
  • 设置太短或设为0时,对需要会话保持的登录态应用不友好,用户请求被分散到多台后端,Session状态对不上。

实践建议:无状态API服务的persistence_timeout可以设置为0或5秒;需要登录态保持但接口幂等的场景,30到120秒足够;长连接和WebSocket场景,对齐网关的保持时间,通常300秒左右。

5.4 在公有云上别硬套DR模式

如果你是在公有云或者OpenStack这类虚拟化环境里,DR模式基本不可用。原因很简单:DR依赖二层广播,而云网络里的VIP漂移、ARP广播行为很多时候不受你控制。即便能ping通VIP,后端RS的响应也可能因为云网络安全策略不对等而失败。

务实的建议是:能用云厂商的负载均衡产品就直接用,别自己再造轮子;如果必须在云环境里自建,优先考虑Tunnel模式(IPIP隧道或VRF),或者直接用MetalLB这类面向K8s的原生方案,配合BGP和L2模式实现等价能力。

6. 从LVS到eBPF:下一个数据面在哪里

6.1 eBPF/XDP:更潮流的下一代数据面

以Cilium为代表,eBPF技术这几年把网络、可观测性、安全都推到了内核态可编程的层面。XDP可以在网卡驱动层直接处理数据包,比netfilter框架更早介入,延迟更低、吞吐更高。Cilium把Service负载均衡也实现成了eBPF程序,直接在节点上完成四层转发,不再依赖kube-proxy的iptables或ipvs模式。

这对LVS来说确实是竞争,但要冷静看待:eBPF对内核版本和网卡驱动有硬性要求,很多存量生产环境的内核版本并不满足条件。虽然这半年eBPF发展很快,但要完全替代LVS的成熟稳定性和二十多年沉淀下来的运维经验,还有很长的路要走。实际生产里,我看到更多的情况是LVS和Cilium并存——LVS负责四层入口,Cilium负责集群内部的网络策略和东西向流量。

6.2 我的选型建议:不追新,按场景来

结合我自己经历和观察到的生产案例,给几条务实的建议:

  • 如果集群入口流量模型稳定,K8s集群规模在中等水平(1000节点以内),LVS + Keepalived完全够用,成本低、问题少,排障路径清晰。
  • 如果集群规模很大,Service数量过万,对网络策略有复杂需求,建议直接上Cilium(eBPF),再用云厂商的LB或物理网络的BGP方案承接外部流量。
  • 如果是裸金属机房,已经有成熟的LVS运维体系,不必为了"新"而新,继续保持LVS做入口,集群内部逐步引入Cilium做网络策略和可观测性增强。

我自己见过不少团队一上来就追求Service Mesh和eBPF全上,结果因为链路复杂、排障成本高,又默默回到了更朴素的方案上。技术在演进,但LVS这种简单、稳定、可预期的基础设施,反而在云原生时代成了最难得的确定性。如果你正在设计集群流量入口,不妨就先从一套LVS双机高可用开始,把基础打牢了,再往上层叠加你想尝试的新技术。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/14 12:55:18

Python动态类型安全与属性测试实践指南

1. 动态类型系统的双刃剑特性Python作为一门动态类型语言,其核心优势在于开发效率——我们不需要在编码时显式声明变量类型,解释器会在运行时自动确定类型信息。这种特性在快速原型开发和小型项目中表现尤为突出,但同时也带来了可靠性的潜在风…

作者头像 李华
网站建设 2026/9/14 12:54:48

银行排号系统Java源码解析:MVC架构与并发控制核心设计

简介:一款基于Java的银行排号系统案例,面向需要完成课程设计或毕业设计的Java学习者,提供从项目报告、答辩PPT到源代码、数据库的完整资料,可用来理解银行排队取号、预约管理等业务场景的落地实现。压缩包整体约1.69MB&#xff0c…

作者头像 李华
网站建设 2026/9/14 12:50:19

微信小程序智能机器人:消息链路设计与云开发实战

简介:面向微信小程序开发者和人工智能对话初学者,这份智能机器人小程序源码可帮助快速掌握页面搭建、消息交互与机器人服务对接方法。压缩包共19个文件,整体体积仅15KB,其中包含5个逻辑脚本文件、4个样式表文件、3个页面结构文件、…

作者头像 李华
网站建设 2026/9/14 12:43:11

DB-GPT 启动报端口 5670 “Address already in use“ 怎么排查?

DB-GPT 启动报端口 5670 "Address already in use" 怎么排查? 【免费下载链接】DB-GPT open-source agentic AI data assistant for the next generation of AI Data products. 项目地址: https://gitcode.com/GitHub_Trending/db/DB-GPT 当你用 …

作者头像 李华