ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

LVS集群实战:DR模式搭建、高可用与生产级调优

LVS集群实战:DR模式搭建、高可用与生产级调优

1. 为什么今天还要亲手搭一套LVS集群?——不是为了怀旧,而是为了看清流量洪峰的“骨骼”

很多人看到“LVS”第一反应是:这不早被云厂商的SLB、ALB、CLB全包圆了吗?点几下控制台就搞定,谁还 ssh 进去敲 ipvsadm?这话没错,但错在把“能用”和“懂用”混为一谈。我去年参与某电商大促保障时,监控突然报警:核心下单接口 P99 延迟从 80ms 暴涨到 2.3s,而云负载均衡控制台显示一切正常——健康检查通过、连接数未超限、带宽利用率仅 35%。最后排查发现,是云厂商默认开启的“会话保持(Session Stickiness)”策略,在突发流量下导致后端节点负载严重倾斜,3台服务器中1台承载了72%的请求,CPU打满,而另外两台空闲率超60%。问题根源不在应用层,而在负载均衡器对连接状态的调度逻辑上。

LVS 的价值,恰恰在于它把负载均衡这件事“剥得足够薄”。它不处理 HTTP 头、不解析 Cookie、不重写 URL,只在 Linux 内核 Netfilter 的 IP 层做连接转发决策。这意味着:你调一个ipvsadm -Ln就能看到当前所有活跃连接的源IP、目标IP、端口、状态、权重、连接数;你改一行ipvsadm -e -t 10.0.1.100:80 -w 3就能实时调整某台后端的权重;你执行echo 1 > /proc/sys/net/ipv4/ip_forward和iptables -t nat -A POSTROUTING -s 10.0.0.0/16 -d 10.0.0.0/16 -j MASQUERADE就能精准控制数据包走向。这种“裸金属级”的可见性与可控性,是任何封装过深的托管服务都无法提供的。

关键词里虽然没填,但标题本身已锁定三个核心域:Linux 网络栈深度操作能力、高并发流量分发原理、生产级容灾架构设计思维。这不是教你怎么配个 demo,而是带你亲手拆开一台“流量调度引擎”的外壳,看清每个齿轮怎么咬合。接下来要讲的每一步配置,背后都对应着一个真实线上故障场景的解法。比如 DR 模式里 VIP 必须绑定在 lo 接口而非 eth0,这个看似反直觉的操作,就是为了绕过 ARP 广播风暴——当上百台 LVS 节点同时响应同一个 VIP 的 ARP 请求时,交换机 MAC 表会瞬间溢出,整个二层网络陷入瘫痪。这些细节,不会出现在云控制台的文档里,但会决定你能否在凌晨三点把系统拉回来。

2. LVS 的三种工作模式,到底该选哪一种?——别被教科书概念绕晕,看真实拓扑再决策

市面上讲 LVS 模式的文章,十有八九一上来就列定义:“NAT 模式:修改目的IP和端口;DR 模式:只改 MAC 地址;TUN 模式:加 IP 隧道头……”听起来很酷,但没人告诉你:在什么物理网络条件下,哪种模式会直接失效?

我们直接拿三套真实部署环境来对照:

环境类型典型拓扑NAT 模式是否可行DR 模式是否可行TUN 模式是否可行关键限制原因
同VLAN内网集群(如IDC机柜)LVS+RS都在10.0.1.0/24网段,二层互通✅ 完全可行✅ 最优选择⚠️ 可行但冗余DR需RS禁ARP响应,TUN需额外隧道开销
跨子网混合云(LVS在IDC,RS在公有云VPC)LVS在10.0.1.0/24,RS在172.16.0.0/16,路由可达✅ 可行(需SNAT)❌ 不可行✅ 唯一选择DR要求LVS与RS二层互通,跨子网无法满足
容器化微服务集群(K8s NodePort暴露)LVS作为Ingress入口,后端是Pod IP(如10.244.1.5)⚠️ 可行但性能差❌ 不可行(Pod无lo绑定VIP能力)✅ 主流方案Pod生命周期短,lo绑定VIP不可靠

提示:很多团队踩坑是因为照搬“DR性能最好”的结论,却忽略了自己网络环境根本不支持。比如某公司把LVS部署在阿里云ECS上,后端RS也全是ECS,但默认VPC网络是三层转发(非二层广播域),强行配DR会导致RS收不到LVS发来的ARP请求,VIP永远无法响应——因为云厂商的虚拟交换机根本不会转发目的MAC为VIP的ARP Reply包。

我们以最典型的同VLAN内网集群为例,重点展开 DR 模式。它之所以成为生产首选,核心在于三点:

  1. 性能天花板最高:LVS只改MAC头,不碰IP包内容,转发延迟稳定在微秒级;
  2. RS真实IP可见:后端Web服务器日志里直接记录客户端真实IP,无需X-Forwarded-For解析;
  3. 扩展性极强:LVS节点可水平扩展,RS数量理论上无上限(受限于交换机MAC表容量)。

但DR的代价也很明确:RS必须和LVS在同一物理网段,且需手动配置ARP抑制。这个“手动”不是可选项,而是生死线。下面这段脚本,就是我在某金融客户现场手敲了三遍才确认无误的RS初始化命令:

# 在每台Real Server上执行(以CentOS 7为例) # 步骤1:绑定VIP到lo接口(关键!不能绑eth0) ip addr add 10.0.1.100/32 dev lo label lo:0 # 步骤2:禁用lo接口对VIP的ARP响应(防止RS抢答ARP请求) echo "net.ipv4.conf.lo.arp_ignore = 1" >> /etc/sysctl.conf echo "net.ipv4.conf.lo.arp_announce = 2" >> /etc/sysctl.conf sysctl -p # 步骤3:确保eth0接口仍能正常响应自身IP的ARP(避免断网) echo "net.ipv4.conf.eth0.arp_ignore = 0" >> /etc/sysctl.conf echo "net.ipv4.conf.eth0.arp_announce = 0" >> /etc/sysctl.conf

这里arp_ignore=1的含义是:只响应目标IP是本接口主IP的ARP请求;arp_announce=2则强制使用最佳本地地址(即lo:0上的VIP)作为ARP Reply的源IP。这两行配置缺一不可。我曾见过运维同事只配了第一行,结果LVS发ARP请求时,RS虽然不响应,但交换机会把VIP的MAC映射到某台RS的eth0物理MAC上,导致后续流量全部被错误导向——这就是典型的“半吊子ARP抑制”。

3. 从零构建LVS-DR集群:四步完成核心链路打通

现在我们进入实操环节。假设你有三台干净的 CentOS 7 虚拟机:

  • LVS-Master:10.0.1.10(主调度节点)
  • LVS-Backup:10.0.1.11(备用节点,Keepalived热备)
  • RS-01:10.0.1.20(后端服务器1)
  • RS-02:10.0.1.21(后端服务器2)

VIP 设定为 10.0.1.100,对外提供 Web 服务(80端口)。注意:所有操作均在 root 用户下进行,且已关闭 firewalld(或放行对应端口)。

3.1 LVS节点基础配置:让内核“睁眼看见”IPVS模块

很多新手卡在第一步:执行ipvsadm -Ln报错command not found或Can't initialize ipvs: Protocol not available。前者是没装工具,后者是内核模块未加载。先解决根本问题:

# 检查IPVS内核模块是否内置(现代Linux内核基本都已集成) lsmod | grep ip_vs # 应输出类似:ip_vs_sh 12688 0, ip_vs_wrr 12697 0, ip_vs_rr 12600 0, ip_vs 145497 6 ip_vs_rr,ip_vs_sh,ip_vs_wrr # 若无输出,手动加载(CentOS 7需启用) modprobe ip_vs modprobe ip_vs_rr modprobe ip_vs_wrr modprobe ip_vs_sh # 永久生效:写入模块加载列表 echo "ip_vs" >> /etc/modules-load.d/ipvs.conf echo "ip_vs_rr" >> /etc/modules-load.d/ipvs.conf echo "ip_vs_wrr" >> /etc/modules-load.d/ipvs.conf echo "ip_vs_sh" >> /etc/modules-load.d/ipvs.conf

注意:不要尝试yum install ipvsadm后直接运行,必须确认内核模块已就绪。我曾帮某客户排查,他们装了ipvsadm但内核没加载模块,所有命令都返回“Protocol not available”,折腾两天才发现是/etc/modules-load.d/目录权限被误设为700,root用户都读不了配置文件。

3.2 配置LVS调度规则:不只是加一条命令,而是理解每项参数的业务含义

在 LVS-Master 上执行:

# 清空现有规则(首次配置可跳过) ipvsadm -C # 添加虚拟服务:VIP:80,使用DR模式(-g参数),轮询调度(-s rr) ipvsadm -A -t 10.0.1.100:80 -s rr # 添加真实服务器(注意:-g表示DR,-w指定权重) ipvsadm -a -t 10.0.1.100:80 -r 10.0.1.20:80 -g -w 1 ipvsadm -a -t 10.0.1.100:80 -r 10.0.1.21:80 -g -w 1 # 查看当前规则(此时应看到两条ActiveConn为0的连接) ipvsadm -Ln

输出应类似:

IP Virtual Server version 1.2.1 (size=4096) Prot LocalAddress:Port Scheduler Flags -> RemoteAddress:Port Forward Weight ActiveConn InActConn TCP 10.0.1.100:80 rr -> 10.0.1.20:80 Route 1 0 0 -> 10.0.1.21:80 Route 1 0 0

这里-g参数是DR模式的标志,它告诉内核:转发时只替换目标MAC为RS的MAC,不修改IP头。而-w 1的权重值,决定了在轮询(rr)算法下,每台RS被选中的概率。如果RS-01配置更强(16核64G),而RS-02较弱(4核16G),可将权重设为ipvsadm -e -t 10.0.1.100:80 -r 10.0.1.20:80 -g -w 4,这样RS-01将获得约80%的流量。

实操心得:权重调整不是拍脑袋。我们通常先用ab -n 10000 -c 100 http://10.0.1.20/和ab -n 10000 -c 100 http://10.0.1.21/分别压测单台RS的QPS,假设RS-01达850 QPS,RS-02仅210 QPS,则权重比应设为 850:210 ≈ 4:1。这才是真正基于性能的动态调度。

3.3 验证数据平面连通性:绕过HTTP,用TCP连接测试最真实

很多人验证LVS是否成功,习惯用curl http://10.0.0.100。这其实是个陷阱——如果后端Web服务没起,curl失败你会以为LVS坏了;如果DNS缓存了VIP的旧IP,curl可能根本没走到LVS。更可靠的方法是直接测试TCP连接:

# 在任意客户端机器(非LVS/RS)执行 # 测试VIP:80端口是否可建立TCP连接(不发送HTTP请求) nc -zv 10.0.1.100 80 # 应返回:Connection to 10.0.1.100 80 port [tcp/http] succeeded! # 进一步验证:连接建立后立即断开,看LVS是否记录了ActiveConn # 在LVS-Master上执行(另开终端) watch -n 1 'ipvsadm -Ln | grep :80' # 此时ActiveConn应短暂跳变为1,证明连接已穿透LVS到达RS

这个测试的价值在于:它剥离了应用层干扰,纯粹验证LVS的数据转发链路。我曾用此法快速定位过一个经典问题:某次升级后curl总超时,但nc却秒通。最终发现是后端Nginx配置了keepalive_timeout 0,导致HTTP长连接被异常关闭,而LVS的连接跟踪表(conntrack)默认保留TIME_WAIT状态60秒,新连接被阻塞——这是典型的“LVS没问题,但应用层配置与LVS机制冲突”的案例。

3.4 部署Keepalived实现高可用:主备切换不是“自动”,而是“可预测”

单点LVS是生产大忌。我们用Keepalived实现VIP漂移。在 LVS-Master 和 LVS-Backup 上均安装:

yum install -y keepalived systemctl enable keepalived

编辑/etc/keepalived/keepalived.conf(Master版):

global_defs { router_id LVS_MASTER } 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 { 10.0.1.100/24 dev eth0 label eth0:1 } # 关键:当本机ipvs规则异常时,主动降权触发切换 track_script { chk_lvs } } # 自定义健康检查脚本 vrrp_script chk_lvs { script "/usr/local/bin/check_lvs.sh" interval 2 weight -20 }

对应的/usr/local/bin/check_lvs.sh脚本:

#!/bin/bash # 检查ipvsadm是否能正常列出规则,且至少有一条ActiveConn > 0 if ipvsadm -Ln | grep -q "ActiveConn" && ipvsadm -Ln | grep -E ":[0-9]+.*[0-9]+$" | awk '{sum+=$5} END{print sum+0}' | grep -q "^[1-9][0-9]*$"; then exit 0 else exit 1 fi

Backup节点配置仅需修改三处:

  • state BACKUP
  • priority 90(低于Master)
  • router_id LVS_BACKUP

关键经验:Keepalived的track_script不是摆设。我们曾遇到Master节点因内核bug导致ipvs连接跟踪表溢出(ip_vs_conn_tab满),此时LVS虽能响应ARP,但新连接无法建立。若无此脚本,VIP会永远卡在故障节点上。而该脚本每2秒检查一次ActiveConn总数,一旦为0即触发降权,3秒内完成VIP漂移到Backup节点——这才是真正的故障自愈。

4. 生产级加固与排障:那些文档里不会写的“血泪教训”

搭建完成只是起点。真正的挑战在于让集群在高压、突变、故障下持续稳定。以下是我在多个项目中沉淀的硬核加固点:

4.1 连接跟踪表(conntrack)溢出:LVS的“内存泄漏”式崩溃

LVS依赖内核的连接跟踪模块(nf_conntrack)记录每个连接状态。默认值极小:

# 查看当前设置 sysctl net.netfilter.nf_conntrack_max # CentOS 7 默认仅65536,对万级并发远远不够 # 临时调整 sysctl -w net.netfilter.nf_conntrack_max=524288 # 永久生效 echo "net.netfilter.nf_conntrack_max = 524288" >> /etc/sysctl.conf

但光调大还不够。更致命的是连接跟踪老化时间(timeout)设置不合理。默认TCP ESTABLISHED状态超时为432000秒(5天!),意味着一个长连接会占用conntrack表项长达5天。在HTTP短连接场景下,这会造成大量TIME_WAIT状态堆积。解决方案是精细化配置:

# 缩短TCP ESTABLISHED超时(生产建议300-600秒) echo "net.netfilter.nf_conntrack_tcp_timeout_established = 600" >> /etc/sysctl.conf # 缩短TIME_WAIT超时(默认120秒,可降至30秒) echo "net.netfilter.nf_conntrack_tcp_timeout_time_wait = 30" >> /etc/sysctl.conf # 启用连接跟踪哈希表自动伸缩(内核3.12+) echo "net.netfilter.nf_conntrack_hashsize = 65536" >> /etc/sysctl.conf

踩坑实录:某直播平台上线后,LVS节点在峰值时频繁丢包,dmesg显示nf_conntrack: table full, dropping packet。排查发现是主播推流使用RTMP长连接,ESTABLISHED超时设为5天,65536的表项在2小时内耗尽。调整为600秒后,表项峰值稳定在12000左右,问题彻底解决。

4.2 网络接口队列(txqueuelen)瓶颈:千兆网卡为何跑不满?

即使LVS CPU和内存充足,也可能遭遇网卡瓶颈。Linux默认的发送队列长度(txqueuelen)仅为1000:

# 查看当前值 ip link show eth0 | grep txqueuelen # 输出:... txqueuelen 1000 ... # 临时增大(千兆网卡建议5000-10000) ip link set eth0 txqueuelen 5000 # 永久生效(写入网卡配置) echo "ETHTOOL_OPTS=\"speed 1000 duplex full autoneg off\"" >> /etc/sysconfig/network-scripts/ifcfg-eth0 echo "TXQUEUELEN=5000" >> /etc/sysconfig/network-scripts/ifcfg-eth0

这个参数直接影响网卡驱动处理数据包的缓冲能力。当LVS每秒需转发数万包时,小队列会导致内核频繁触发中断,CPU花在上下文切换上的时间远超实际转发时间。增大后,CPU软中断(si)占比下降40%,吞吐量提升2.3倍。

4.3 RS健康检查的“假死”与“误杀”:如何让LVS真正懂业务

LVS自带的TCP端口检查(-t)只能确认端口是否开放,无法判断后端服务是否真能处理请求。比如Nginx进程存活但worker进程全部卡死,端口仍可连接。我们采用“业务级探活”:

在每台RS上部署轻量HTTP探针:

# /usr/local/bin/lvs_health.sh #!/bin/bash # 检查Nginx是否返回200,且响应时间<500ms if curl -s --max-time 0.5 -o /dev/null -w "%{http_code}" http://127.0.0.1/healthz | grep -q "200"; then exit 0 else exit 1 fi

然后在LVS节点配置Keepalived的real_server段落:

virtual_server 10.0.1.100 80 { delay_loop 3 lb_algo rr lb_kind DR protocol TCP real_server 10.0.1.20 80 { weight 1 TCP_CHECK { connect_timeout 3 nb_get_retry 3 delay_before_retry 3 } # 新增:调用业务探针 HTTP_GET { url { path /healthz status_code 200 } connect_timeout 3 nb_get_retry 3 delay_before_retry 3 } } }

经验之谈:delay_before_retry 3这个参数救过我们多次。某次RS因磁盘IO打满,curl /healthz响应时间波动在2-8秒。若设为1秒重试,LVS会在3秒内连续发起3次请求,进一步加剧IO压力,形成雪崩。设为3秒后,给了RS喘息时间,故障自动恢复率提升至92%。

5. LVS集群的“体检报告”:五项必查指标与阈值红线

上线不是终点,日常巡检才是保障。我给所有维护LVS集群的团队制定了一套“五分钟体检清单”,每天早晚各执行一次:

检查项命令健康阈值异常含义应急操作
1. VIP ARP响应arping -c 3 -I eth0 10.0.1.1003个包全部响应LVS节点未接管VIP或网络隔离检查Keepalived状态,systemctl status keepalived
2. 连接跟踪使用率conntrack -L | wc -l&sysctl net.netfilter.nf_conntrack_max使用率 < 70%conntrack表即将溢出,新连接被丢弃立即执行conntrack -F清空,并检查超时设置
3. RS连接分布均衡度ipvsadm -Ln | awk '/->/ {print $6}' | sort | uniq -c各RS连接数标准差 < 15%调度算法失效或RS性能严重不均检查RS权重,执行ipvsadm -E -t ... -w动态调整
4. LVS节点CPU软中断sar -I SUM -u 1 3 | tail -1 | awk '{print $7}'%si < 35%网卡中断处理不过来,需调大txqueuelen执行ip link set eth0 txqueuelen 10000
5. RS健康检查失败率tail -100 /var/log/messages | grep "Real server down"过去1小时失败次数 ≤ 2RS服务不稳定或网络抖动登录RS执行curl -v http://127.0.0.1/healthz

这个清单的价值在于:它把抽象的“系统健康”转化为5个可量化、可执行、可归因的具体动作。去年某次大促前,我们通过第3项检查发现RS-01连接数是RS-02的3.2倍,立即检查发现是RS-01的Nginxworker_connections设为1024,而RS-02为65536,调整后连接分布标准差从42%降至5.3%,P99延迟下降68%。

最后分享一个个人体会:LVS从来不是用来“替代”云负载均衡的,而是作为最后一道可控防线。我们在所有云SLB前面再部署一层LVS集群,不是为了省成本,而是为了在云厂商控制台失灵、API不可用、甚至整个Region故障时,能用最原始的SSH登录,手动修改几行ipvsadm命令,把流量切到灾备机房——那种指尖划过键盘、看着ipvsadm -Ln输出实时变化的掌控感,是任何图形界面都无法给予的踏实。技术演进再快,底层网络的确定性,永远是我们最值得信赖的锚点。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进