VPS环境下四层负载均衡器极限对决:Nginx Stream vs HAProxy vs IPVS — 资源受限下的调优实战

1. 为什么VPS上的负载均衡是个坑?

VPS的虚拟化层(KVM / Xen)会引入网络I/O的软中断开销,加上有限的CPU核和内存,传统负载均衡器在高并发下容易直接打爆宿主机资源。更致命的是,大多数VPS默认内核参数是为桌面设计的——somaxconn=128tcp_tw_reuse=0netdev_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并发QPSCPU用户态%CPU软中断%
Nginx Stream(未调优)18,20367%31%
Nginx Stream(调优后)24,87145%38%
HAProxy(nbproc=4)15,43072%19%
HAProxy(nbproc=2 + splice)22,11534%34%
IPVS DR(默认参数)29,0040.3% (用户态)62%
IPVS DR(关同步+调超时)31,7820.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_sizeproxy_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