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 模式。它之所以成为生产首选,核心在于三点:
- 性能天花板最高:LVS只改MAC头,不碰IP包内容,转发延迟稳定在微秒级;
- RS真实IP可见:后端Web服务器日志里直接记录客户端真实IP,无需X-Forwarded-For解析;
- 扩展性极强: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 fiBackup节点配置仅需修改三处:
state BACKUPpriority 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.100 | 3个包全部响应 | 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小时失败次数 ≤ 2 | RS服务不稳定或网络抖动 | 登录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输出实时变化的掌控感,是任何图形界面都无法给予的踏实。技术演进再快,底层网络的确定性,永远是我们最值得信赖的锚点。