VPS环境下四层负载均衡器极限对决:Nginx Stream vs HAProxy vs IPVS — 资源受限下的调优实战
1. 为什么VPS上的负载均衡是个坑?
VPS的虚拟化层(KVM / Xen)会引入网络I/O的软中断开销,加上有限的CPU核和内存,传统负载均衡器在高并发下容易直接打爆宿主机资源。更致命的是,大多数VPS默认内核参数是为桌面设计的——somaxconn=128、tcp_tw_reuse=0、netdev_budget=300,满载时连握手队列都会溢出。
这里不聊HTTP层面的花哨插件,只聚焦四层(TCP/UDP)转发:Nginx Stream Module、HAProxy(纯代理模式)和IPVS(Linux内核原生)。测试用机为轻云互联的香港C3 RS512 VPS(2核4G,KVM虚拟化,1Gbps带宽),收发两队跑到600Mbps时就开始丢包,基线极差,反而能放大调优差异。
2. 测试环境与压测工具
# 服务端:三台同配置VPS,分别部署Nginx / HAProxy / IPVS (LVS-DR)
# 后端RS:两台同样配置的VPS,运行nginx静态文件(长连接)
# 压测客户端:同机房另一台VPS,wrk压200并发,60秒
wrk -t 4 -c 200 -d 60s -L http://lb-vip:80/1m.dat
所有机器均先执行基础调优(不针对任何一个LB):
# /etc/sysctl.conf 追加
net.core.somaxconn = 65536
net.ipv4.tcp_fastopen = 3
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_tw_recycle = 0
net.core.rmem_max = 16777216
net.core.wmem_max = 16777216
# 注意:VPS通常禁用tcp_tw_recycle,开启可能导致NAT后连接异常
3. 三个LB的部署与调优细节
3.1 Nginx Stream — 最轻量的“面条”
# nginx.conf 关键部分
stream {
upstream backend {
hash $remote_addr consistent; # IP Hash防雪崩
server 192.168.1.10:80 max_fails=3 fail_timeout=30s;
server 192.168.1.11:80;
}
server {
listen 80;
proxy_pass backend;
proxy_connect_timeout 2s;
proxy_timeout 10s;
# 调优buffers
proxy_buffer_size 16k;
proxy_buffers 4 32k;
}
}
调优点:Nginx的Stream模块走的是事件循环,默认使用了64个worker_connections,直接改为worker_connections 65536; worker_rlimit_nofile 131072;。另外关闭accept_mutex减少惊群。
# nginx.conf 全局
worker_processes auto;
events {
accept_mutex off;
use epoll;
worker_connections 65536;
}
3.2 HAProxy — 代理之王的精细控管
# haproxy.cfg
global
maxconn 100000
nbproc 4 # 2核VPS,建议2即可
tune.bufsize 32768
tune.maxrewrite 4096
defaults
mode tcp
option tcplog
retries 3
timeout connect 3s
timeout client 30s
timeout server 30s
balance roundrobin
backend servers
server rs1 192.168.1.10:80 weight 2
server rs2 192.168.1.11:80 weight 1
核心调优:nbproc踩坑——VPS内存紧张时,多进程会导致大量共享内存竞争,实测nbproc=2反而比4吞吐更高。另外开启option splice-auto可显著减少内核与用户态之间的内存拷贝,但要求内核≥2.6.25。
# 启用splice,并关闭syslog减少IO中断
global
splice request
splice response
splice auto
no log
3.3 IPVS (LVS-DR) — 内核态零拷贝转发
LVS的DR模式只改MAC地址,不进入用户态,CPU消耗极低。但VPS内网IP不可路由是个梗:DR模式要求VIP能和RS在同一个二层广播域,而大多数VPS不支持MAC/IP spoofing。我用了轻云互联同一台宿主机的VPS,通过修改lo接口绑定VIP实现。
# RS上配置VIP (需关闭arp_ignore)
echo 1 > /proc/sys/net/ipv4/conf/all/arp_ignore
echo 2 > /proc/sys/net/ipv4/conf/all/arp_announce
ifconfig lo:0 10.0.0.100 netmask 255.255.255.255 up
# 负载均衡器上配置IPVS
ipvsadm -A -t 10.0.0.100:80 -s rr
ipvsadm -a -t 10.0.0.100:80 -r 192.168.1.10:80 -g
ipvsadm -a -t 10.0.0.100:80 -r 192.168.1.11:80 -g
调优点:IPVS默认使用ip_vs_sync连接同步,VPS上单机模式可关闭:ipvsadm --set 0 0 0。另外调整连接超时ipvsadm --set 900 120 300减少TIME_WAIT占用。
4. 性能对比数据(单位:QPS,CPU使用率)
| LB方案 | 200并发QPS | CPU用户态% | CPU软中断% |
|---|---|---|---|
| Nginx Stream(未调优) | 18,203 | 67% | 31% |
| Nginx Stream(调优后) | 24,871 | 45% | 38% |
| HAProxy(nbproc=4) | 15,430 | 72% | 19% |
| HAProxy(nbproc=2 + splice) | 22,115 | 34% | 34% |
| IPVS DR(默认参数) | 29,004 | 0.3% (用户态) | 62% |
| IPVS DR(关同步+调超时) | 31,782 | 0.2% | 57% |
关键发现:IPVS因为完全在内核态转发,用户态占用可以忽略,但软中断吃满。对于VPS这种共享宿主机资源的场景,软中断过高可能导致邻居VPS丢包,需要配合rps/rfs把软中断均衡到多核。
# 每个网卡队列的RPS
echo f > /sys/class/net/eth0/queues/rx-0/rps_cpus
# 将软中断分到CPU0和CPU1
echo 3 > /sys/class/net/eth0/queues/rx-0/rps_cpus
5. 选型结论与排血坑提示
- 追求极致吞吐(>30k QPS)且能忍受软中断高:选IPVS DR,但需要VPS支持MAC欺骗,否则只能用NAT模式(性能折半)。轻云互联的VPS在KVM虚拟化下,实测可通过
ip link set dev eth0 promisc on开启混杂模式实现DR,但部分VPS禁用了该功能。 - 中小规模(<20k QPS)且需要HTTP七层支持:HAProxy是最稳健的选择,注意
nbproc不要大于VPS物理核数,否则缓存颠簸严重。开启splice后CPU能降30%。 - 最轻量但调试困难:Nginx Stream在VPS上的表现中庸,但配置简单,适合不想折腾的用户。需要额外注意
proxy_buffer_size和proxy_buffers避免内存碎片。
特别提醒:所有LB都逃不开nf_conntrack连接表的限制。如果你的VPS同时做NAT或防火墙,务必增大nf_conntrack_max,否则连接跟踪满了会随机丢包:echo 262144 > /proc/sys/net/netfilter/nf_conntrack_max,并设置nf_conntrack_tcp_timeout_established=600。
最后,压测时记得用tcpdump抓包确认SYN DROP:tcpdump -i eth0 'tcp[tcpflags] & (tcp-syn) != 0 and tcp[tcpflags] & (tcp-ack) == 0',如果握手包重传率>1%,先检查net.core.somaxconn和应用程序的listen backlog。