做后端的人,迟早要碰一次LVS。不管你用阿里云、腾讯云还是自建机房,流量到一定量级之后,总有一个四层入口设备挡在最前面,把流量拆散给后面的几十台机器。这个设备十有八九就是LVS,或者干脆是套着SLB外壳的LVS。LVS全称Linux Virtual Server,本质上是跑在Linux内核里的一套负载均衡框架,把请求分发到后端的真实服务器上,工作在TCP/UDP层,不走应用层解析,所以它的性能能到秒级建连几百万的水平,这是Nginx和HAProxy很难追上的。这篇内容不聊概念、不贴PPT,直接拆解LVS的核心转发链路、三种工作模式的取舍逻辑、调度算法的实际效果,再给一套我实测过很多次的生产级DR模式配置,最后把最常见的坑一个个列出来,适合正在搭集群、调高可用、排查“一会儿通一会儿不通”问题的同学照着试。
1. 为什么是LVS:负载均衡选型与核心思路
1.1 单机扛不住之后,缺的是调度而不是堆机器
后端业务早期通常是一台机器跑全部,数据库、缓存、应用全塞一起。一旦用户量涨起来,CPU先爆,紧接着连接数被打满,数据库连接池排队,整个服务从变慢到不可用,最后只能半夜爬起来扩容。这时候最常见的思路是加机器,但加了机器以后问题变成:用户怎么知道该访问哪台?DNS轮询能不能用?能,但DNS缓存和故障切换慢到让你怀疑人生,一台机器挂了用户可能还会被解析到坏节点上,继续超时。
真正的解法是加一层“统一入口”,也就是负载均衡器。它的职责不是处理业务,而是接收所有客户端请求,再按照某种策略发给后端的多台服务器。负载均衡器本身不存状态、不解析业务逻辑,只做转发和调度,所以它必须做到两件事:一是转发性能足够高,不能成为新瓶颈;二是调度逻辑足够灵活,能应对不同后端权重、不同连接时长、以及后端故障的场景。LVS之所以在这个位置站稳脚跟,就是因为内核态转发这条路,让它在性能上碾压了用户态方案。
1.2 内核态转发才是LVS的真正底牌
Nginx和HAProxy也有负载均衡能力,为什么还要上LVS?核心区别在“走不走内核协议栈”。Nginx是工作在应用层的进程,收到请求后必须先把整个TCP数据包从内核拷贝到用户态,解析HTTP头,再把连接转发给上游,这一步会带来不小的CPU开销和内存拷贝开销。连接量一旦到十万、百万这个级别,用户态的进程调度、epoll循环、上下文切换都会成为瓶颈。
LVS的实现则完全不同。它的核心叫IPVS(IP Virtual Server),早早就被合并进Linux内核的netfilter框架里。Director收到一个数据包后,IPVS模块在TCP握手阶段就被内核钩住,根据调度算法选好后端机器,直接改写数据包的目标IP和端口,然后由内核转发出去。整个过程不经过用户态,不存在“把包拷给进程”这种操作。所以LVS能支撑的并发连接数轻松上百万,转发吞吐量能跑满网卡,这种量级对Nginx来说几乎是不可能完成的任务。
从工程选型角度说,LVS适合放在最外层做流量入口,负责“快速把包分给一堆机器”;Nginx适合放在业务前面做反向代理,负责HTTP路由、缓存、限流和SSL终止。两者是协作关系而不是替代关系。我见过很多生产架构是LVS接公网流量,转发给一组Nginx,再由Nginx转发给应用服务,每一层只做自己擅长的事。
1.3 等开销负载均衡在真实架构里的位置
最近“等开销负载均衡”这个词经常出现,其实就是“让后端每台机器消耗近似一致”的调度思想。LVS的加权轮询、加权最少连接、基于源地址的哈希,本质都是在追求这一点。但严格说,HTTP短连接、长连接、WebSocket、数据库四层转发,对“等开销”的定义完全不一样。
短连接场景里,连接数就是最好的压力指标,所以最少连接算法很合适。但如果是数据库这类长连接场景,连接数无法代表实际负载,一条慢查询就能让某台机器CPU爆掉,这时候唯一靠谱的是让应用层做读写分离和连接池控制,LVS只能保证新连接能均匀落下来,控制不了已有连接的负载倾斜。所以在设计整套架构时,不要指望单个负载均衡器解决所有“负载不均”问题,LVS负责连接分发层面的均衡,业务层还要自己做限流、熔断和灰度控制。
2. 核心技术拆解:四层转发、三种工作模式与调度算法
2.1 四层转发到底在转发什么
LVS工作在TCP/UDP层,所以它不关心包里面是HTTP还是HTTPS,也不关心请求URL是什么。当客户端发来一个TCP建连请求,Director上的IPVS模块会在三次握手阶段就介入:它看到SYN包后,立刻从调度表里选一台后端,然后把数据包的目标IP替换成后端的真实IP,再把包发出去。后端回包的时候,根据工作模式不同,回包可能直接回给客户端,也可能绕回Director再转发。
这里有三个关键点容易被忽略。第一,LVS只转发“符合调度规则的包”,每个Virtual Service对应一个VIP端口组合,比如192.168.1.10:80,只有访问这个地址和端口的流量才会被IPVS接管。第二,连接状态是记录在Director内核里的,它用ip_vs_conn结构体保存客户端IP、后端IP、端口、超时时间等信息,所以后续同一个连接的包都能找到对应的后端。第三,LVS不会对数据包做重组、不解析应用数据,这也是它能保持高性能的原因。
2.2 DR模式、NAT模式、TUN模式,到底选哪个
LVS三种经典工作模式各有适用场景,先看对比:
| 模式 | 是否修改MAC | 是否修改IP | 回包路径 | 部署要求 |
|---|---|---|---|---|
| DR(直接路由) | 改目标MAC | 不修改,VIP不变 | 后端直回客户端 | 后端和Director在同一二层网络 |
| NAT(网络地址转换) | 不改 | 改目标IP和源IP | 必须经过Director | 后端只需要网关指向Director,跨网络也可以 |
| TUN(IP隧道) | 不改 | 封装一层新IP头 | 后端直回客户端 | 后端支持隧道协议,可跨网段 |
DR模式在实际生产中最常见,原因是它把回包压力分散到了后端机器上。客户端请求进来,Director通过改写目标MAC地址,把原始请求包原封不动地转给选中的RealServer,RealServer配置了VIP,所以能正常处理,然后回包时直接以VIP作为源地址回给客户端。请求经过Director只有一次,回包完全不占Director带宽。这种模式最适合大流量下载、视频、API网关等场景。
NAT模式则适合后端和Director不在同一网段的场景。Director收到请求后,把目标IP改成RealServer的IP,并把源IP改成自己的IP,RealServer回包先回到Director,Director再把源IP改回VIP,把回包交给客户端。缺点是所有进出流量都过Director,Director的网卡带宽和连接状态表会成为瓶颈。实际部署时,NAT模式常用于早期小规模集群或云环境里VIP和后端跨VPC的场景。
TUN模式用IPIP隧道把原始包封装后发给后端,封装后新的源IP是Director,目标IP是RealServer。后端收到后解封装处理,回包直接回给客户端。TUN能跨网段传播,但需要后端支持隧道配置,对MTU、防火墙规则都更敏感。我实际项目里用TUN的场景很少,因为大多数同机房架构用DR就够,没必要把隧道复杂度引入到每台后端上。
2.3 调度算法不能只会轮询
LVS支持的调度算法非常多,刚开始接触的人容易眼花,其实只要按业务特征选就很简单。
| 算法 | 简称 | 适用场景 | 需要注意的点 |
|---|---|---|---|
| 轮询 | rr | 后端配置完全相同的场景 | 无法感知后端负载,慢机器也会被平均分到 |
| 加权轮询 | wrr | 后端机器配置不同,权重不同 | 权重只是静态比例,不反映实时负载 |
| 最少连接 | lc | HTTP短连接高并发 | 能感知连接数,但对长连接不敏感 |
| 加权最少连接 | wlc | 大多数生产场景首选 | 综合考虑连接数和权重 |
| 基于目的地址的哈希 | dh | 同一目的地址固定发到同一后端 | 适合缓存场景,但要注意故障切换 |
| 基于源地址的哈希 | sh | 保持会话、做灰度 | 可以把同一客户端IP粘到一台后端 |
| Sed / NQ | sed / nq | 特殊均衡场景 | 主要解决最空闲服务器被频繁选中的问题 |
我自己的经验是:通用Web服务直接用wlc,配合keepalived做健康检查基本够用。如果客户端和服务端有状态,比如WebSocket推送,就加-p持久时间配置,让同一来源IP在一段时间内始终落到同一台后端。注意持久时间不能太长,不然流量突发时个别机器会把连接堆满,也不能太短,否则粘性会话会反复横跳。
3. 实操过程与核心配置:从零搭建一套LVS DR集群
3.1 拿到需求先做拓扑和IP规划
假设有三台机器,一台做Director(LVS调度器),两台做RealServer(后端业务服务器),全部部署在同一个二层网络里,业务是一个放在80端口的API服务。目标是通过LVS的DR模式,把请求分发到两台后端上,并且用VIP统一对外提供服务。
IP规划如下:
| 角色 | 机器名 | 内网IP | 说明 |
|---|---|---|---|
| Director | lvs01 | 192.168.1.10 | 主调度器,承载转发流量 |
| RealServer 1 | web01 | 192.168.1.20 | 业务节点1 |
| RealServer 2 | web02 | 192.168.1.30 | 业务节点2 |
| VIP | - | 192.168.1.100 | 客户端统一访问的地址 |
Director上的公网IP我用一个网卡绑定,VIP直接作为辅助IP挂在现有网卡上,RealServer的环回接口也绑定同一个VIP。这里的关键是RealServer的VIP一定要绑定在lo:0上,并且拒绝arp广播,否则RealServer会抢答VIP的ARP请求,导致部分流量绕过Director直接打到RealServer上,整个负载均衡就失去意义了。
3.2 Director上的LVS配置
Director上首先要确保内核支持IPVS,现在主流Linux发行版都默认编译了ip_vs模块。先加载相关模块,然后安装管理工具ipvsadm:
modprobe ip_vs modprobe ip_vs_wrr modprobe ip_vs_sh modprobe ip_vs_lc modprobe ip_vs_wlc yum install -y ipvsadm在Director上添加VIP到网卡。以CentOS/RHEL系为例,可以直接用ip命令临时添加,方便测试:
ip addr add 192.168.1.100/32 dev eth0然后创建虚拟服务和后端RealServer:
ipvsadm -A -t 192.168.1.100:80 -s wlc ipvsadm -a -t 192.168.1.100:80 -r 192.168.1.20:80 -g -w 1 ipvsadm -a -t 192.168.1.100:80 -r 192.168.1.30:80 -g -w 1 ipvsadm -L -n命令参数说明:-A表示添加虚拟服务,-t指定TCP协议和VIP端口,-s指定调度算法为wlc;-a表示添加RealServer,-r指定后端IP和端口,-g表示使用DR模式,-w设置权重。这里的-g是最容易写错的地方,如果误写成-m就变成NAT模式,-i就变成TUN模式,后端回包路径会完全对不上,排查起来很痛苦。
如果想把这些配置持久化到下次启动自动加载,可以执行:
ipvsadm-save > /etc/sysconfig/ipvsadm systemctl enable ipvsadm3.3 RealServer上的隐藏配置:ARP抑制
RealServer如果不做任何配置直接加进LVS集群,肯定会出问题,因为你把VIP配到lo:0上之后,RealServer会对VIP的ARP请求直接应答,客户端发往VIP的包就可能直接被某台RealServer收到,从而绕过Director。
正确做法是在每台RealServer上执行:
ip addr add 192.168.1.100/32 dev lo:0 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_announcearp_ignore=1表示只应答目标地址是本机网卡地址的ARP请求,arp_announce=2表示ARP应答里使用目标IP所属网卡的地址。两个参数组合的效果是:RealServer对VIP的ARP请求不抢答,VIP彻底变成Director的“私有财产”。
最后在RealServer上把系统的rp_filter关掉,否则内核会因为反向路径过滤把来自Director但源IP不是本机IP的包丢掉,现象就是LVS怎么配都不通:
echo 0 > /proc/sys/net/ipv4/conf/all/rp_filter echo 0 > /proc/sys/net/ipv4/conf/lo/rp_filter3.4 验证集群有没有真的跑起来
配置完不要直接对外宣称完成,先用几个命令看状态。在Director上执行ipvsadm -L -n --stats可以看到每个RealServer当前的连接数和累计转发数。再配合curl的-x或者直接用curl --resolve test.example:80:192.168.1.100,把请求发到VIP上,观察后端日志。
如果还不放心,可以写一个最简单的循环脚本做冒烟测试,随便找一个能区分主机的响应头来验证:
for i in $(seq 1 100); do curl -s http://192.168.1.100/ | grep -o "web[0-9]*" done | sort | uniq -c正常情况下输出里web01和web02的数量应该接近。如果发现只有一台后端有流量,先别急着改配置,继续往下看排查清单。
4. 常见问题与排查技巧实录
4.1 VIP能ping通,但业务始终不通
这是搭LVS DR时遇到最多的问题。VIP能ping通,说明Director和VIP配置没问题,但业务不通可能有三种原因。第一种是RealServer上的业务服务监听的IP不对,后端服务往往默认监听127.0.0.1或者只监听服务器自己的内网IP,不会监听VIP,所以即便VIP是在本机lo:0上,服务进程也接不到包。解决办法是让服务监听0.0.0.0。
第二种原因是rp_filter过滤。前面已经提到,RealServer收到的包目标虽然已经变成了RealServer内网IP,但源IP是客户端的公网IP,而从RealServer的角度看,自己的默认路由根本不会把源IP为客户端IP的包送回来,于是内核认为这是非法路径,直接丢弃。把rp_filter改成0就能解决。
第三种原因是防火墙。别笑,生产环境真的会有人在RealServer上开着firewalld,默认zone会DROP掉来自Director的转发包。用iptables -L -n或者firewall-cmd --list-all检查一下,临时放行可以,长期建议在后端机器上把Director的IP加进白名单。
4.2 LVS转发正常,回包却不经过Director
DR模式下回包不经过Director本身就是设计行为,但很多人误以为问题出在这里。真正要区分的是:如果客户端收到的响应来自VIP,但数据包没有经过Director,这是正常的;如果客户端根本收不到响应,说明RealServer发出回包时,源IP不是VIP,而是一个普通内网IP,或者路由没走对。
检查方法很简单,在RealServer上用tcpdump -i any host 192.168.1.100抓包,看回包时源IP是不是VIP。如果源IP不是VIP,说明RealServer没有把VIP正确加到lo:0,回包的源地址由路由表决定,它选了eth0的内网IP。生产环境里我还会随手关掉RealServer上对VIP的arp广播,再检查ip addr show lo里面是否存在VIP,很多时候都是这两项配置因为重启机器丢了,没写进开机自启脚本。
4.3 Director单点故障怎么办
LVS本身是转发节点,Director挂了,整个集群就瘫了。所以生产环境几乎不会只部署一台Director,而是两台以上配合Keepalived做VIP漂移。
Keepalived的原理很简单:两台Director抢同一个VIP,正常情况下VIP在master上,master定期发送VRRP广播;backup收不到master的广播后,会立刻把VIP抢到自己身上,继续转发流量。同时Keepalived还可以做RealServer的健康检查,一旦某台后端挂掉,就从LVS的转发列表中自动摘除,恢复后再重新加入。
实际配置我习惯把Keepalived的健康检查脚本单独拎出来,脚本里用ipvsadm -L -n | grep检测后端是否在列表里,再配合curl做HTTP层健康检查。这样既能保证后端进程存活时被正确调度,也能避免“进程活着但接口已经假死”的情况继续接收流量。
4.4 会话保持和连接数监控
LVS做会话保持有两种方式。一种是在创建虚拟服务时加-p参数,例如ipvsadm -A -t 192.168.1.100:80 -s wlc -p 600,表示同源IP的用户在600秒内都调度到同一台RealServer。另一种是用sh算法做源地址哈希,效果相似。需要注意的是,-p的粒度是IP而不是Cookie,同一个NAT出口的所有用户会被绑定到同一台后端,这在大楼、校园网场景下会造成负载严重倾斜。如果你后端的用户识别需要精确到会话,建议用应用层方案,LVS的IP级保持只能作为兜底。
监控方面,我建议至少盯四个指标:ip_vs_conn的总连接数、Director网卡的进出包速率、每台RealServer的Active连接数和Inactive连接数、以及Keepalived的状态切换次数。这些数据可以用node_exporter配合Grafana直接展示,不用额外开发,比每周手动看一次ipvsadm -L -n靠谱得多。
4.5 压测工具的选择也要注意
很多人用ab去测LVS,发现结果和预期差很远。这不一定是LVS的问题。ab是纯HTTP压测工具,走的是用户态socket,单机ab自身能压出的并发就有限,瓶颈经常在压测机上。更合理的方案是用wrk、hping3或者iperf组合测试。hping3可以直接构造SYN包测四层转发能力和连接建立速率,wrk测七层完整链路,iperf测纯带宽吞吐。
我踩过的坑是:压测时把并发线程开得过大,结果Director的IPVS连接表满了,新连接被丢弃,ipvsadm -L -n --stats里能看到大量non-active连接堆积。这时候不是LVS性能不行,而是连接表条目太多且TIME_WAIT没有及时回收。调整内核参数net.ipv4.ip_vs_conn_tab_bits需要重新编译内核,不建议线上改动;更实际的做法是调低后端服务的keepalive超时时间,让无效连接尽快释放,别再挂在Director的会话表里。
5. 踩过坑之后的几点实战体会
5.1 配置不写成文件,三天后必忘
LVS最容易让人翻车的地方不是搭建,而是维护。很多人用ipvsadm把命令敲一遍,VIP和RealServer都通了,就把这事放下。等过两周机器重启,网卡配置丢失、ipvsadm没设置开机自启、Keepalived的脚本也没落盘,整个集群恢复不了,只能重新敲命令。
我的习惯是所有涉及VIP、IPVS规则、ARP参数、rp_filter的配置全部写成初始化脚本,丢到/usr/local/lvs/目录下,并且用systemd unit管理。每次变更都要同步更新脚本,让机器“一把梭”能从裸机恢复到既定状态。运维最怕的不是配置复杂,而是配置没被记录,下次出问题时只能靠猜。
5.2 LVS不是万能的,架构层次要看清楚
这几年看到很多团队把LVS捧成万金油,所有的负载均衡都往里塞。其实LVS处理四层没问题,但如果你需要在负载均衡层做按域名分流、按URL重写、请求头修改、限流、熔断,应该交给上层的Nginx或者网关服务去做。硬要把七层逻辑塞进LVS,只会得到一堆难以维护的iptables规则。
我目前认为最舒服的分层方式是:最外层用LVS管理VIP和机器级别的调度,中间层用Nginx做域名到服务集群的路由,最里层才是业务服务。这样每一层职责单一,出问题时排查路径也很清晰:先看LVS转发是否正常,再看Nginx有没有把请求送到正确的上游,最后才是应用日志。按照这个顺序找问题,基本不会在半夜抓瞎。
5.3 健康检查比调度算法更值得反复打磨
调度算法在流量均衡上的作用,其实赶不上健康检查对稳定性的贡献。RealServer进程活着但连接池满了、磁盘满了、数据库连不上,这些情况单纯靠“端口通不通”根本发现不了。Keepalived自带的健康检查只能做TCP握手,所以一定要扩展成脚本来做HTTP检查,最好还能返回响应时间。
我在公司里的做法是Healthcheck脚本每分钟执行一次,用curl -s -o /dev/null -w "%{http_code} %{time_total}"探测后端的/healthz接口,如果连续两次超时或者返回非200,就调用ipvsadm -d把这台后端摘掉;等恢复后自动ipvsadm -a加回来。这套东西逻辑不复杂,但能救回很多次由于后端假死导致的线上事故。最后再提醒一句:LVS的配置变更要留操作记录,集群拓扑变更后一定要重跑一遍压测,别在小改动上翻大跟头。