当 HTTP/3(QUIC over UDP)遇上经典的 LVS DR + Nginx 负载均衡架构,有些坑你不踩一遍根本想不到。
架构概览
┌─────────────────────────────┐
│ 客户端 │
│ (Chrome / Firefox / curl) │
└──────────┬──────────────────┘
│
┌──────────────┼──────────────┐
│ │ │
TCP 80 TCP 443 UDP 443
(HTTP) (HTTPS/H2) (QUIC/H3)
│ │ │
▼ ▼ ▼
┌──────────────────────────────────────┐
│ Keepalived + LVS-DR │
│ VIP: 192.168.5.5 │
│ lb_kind DR | protocol TCP/UDP │
└──────────────┬───────────────────────┘
│
┌───────────┴───────────┐
│ │
▼ ▼
┌──────────────┐ ┌──────────────┐
│ Nginx-01 │ │ Nginx-02 │
│ 192.168.5.8 │ │ 192.168.5.9 │
│ HTTP/1.1 │ │ HTTP/1.1 │
│ HTTP/2 │ │ HTTP/2 │
│ HTTP/3 ✓ │ │ HTTP/3 ✓ │
└──────────────┘ └──────────────┘
│ │
└───────────┬───────────┘
│
▼
┌────────────────┐
│ Backend │
│ Servers │
└────────────────┘核心链路:客户端 → LVS(VIP)→ Nginx 真实服务器 → 后端应用。HTTP/3 的特殊性在于它跑在 UDP 443 上,而传统的 LVS + Nginx 架构只考虑了 TCP。
一、Keepalived 必须新增 UDP 虚拟服务
问题
HTTP/3 基于 QUIC 协议,运行在 UDP 443 上。如果你的 keepalived 只配了 TCP 80 和 TCP 443 的 virtual_server,UDP 443 的流量根本不会被 LVS 接管——客户端的 HTTP/3 请求会直接绕过 VIP,打到某一台 Nginx 上,导致负载不均甚至不可达。
解决方案
在 keepalived.conf 中新增 UDP 443 的虚拟服务:
# TCP 80 - HTTP
virtual_server 192.168.5.5 80 {
delay_loop 5
lb_algo wrr
lb_kind DR
protocol TCP
persistence_timeout 60
real_server 192.168.5.8 80 {
weight 1
TCP_CHECK {
connect_timeout 3
retry 2
delay_before_retry 2
connect_port 80
}
}
real_server 192.168.5.9 80 {
weight 1
TCP_CHECK {
connect_timeout 3
retry 2
delay_before_retry 2
connect_port 80
}
}
}
# TCP 443 - HTTPS / HTTP2
virtual_server 192.168.5.5 443 {
delay_loop 5
lb_algo wrr
lb_kind DR
protocol TCP
persistence_timeout 60
real_server 192.168.5.8 443 {
weight 1
TCP_CHECK {
connect_timeout 3
retry 2
delay_before_retry 2
connect_port 443
}
}
real_server 192.168.5.9 443 {
weight 1
TCP_CHECK {
connect_timeout 3
retry 2
delay_before_retry 2
connect_port 443
}
}
}
# UDP 443 - HTTP3 (QUIC) ← 新增
virtual_server 192.168.5.5 443 {
delay_loop 5
lb_algo wrr
lb_kind DR
protocol UDP # ← 关键:UDP
persistence_timeout 60
real_server 192.168.5.8 443 {
weight 1
UDP_CHECK { # ← UDP 健康检查(比 TCP 简单)
connect_timeout 3
}
}
real_server 192.168.5.9 443 {
weight 1
UDP_CHECK {
connect_timeout 3
}
}
}注意事项
- TCP 443 和 UDP 443 是两个独立的
virtual_server,不能合并。LVS 内部按(protocol, port)作为元组区分。 lb_kind DR对 UDP 同样适用,Direct Return 模式不依赖传输层协议。- UDP 健康检查能力有限:
UDP_CHECK只能做连通性探测(发一个包看有没有回应),不像TCP_CHECK有retry、delay_before_retry等精细选项。如果 Nginx 的 UDP 443 不可用,LVS 感知到的速度可能比 TCP 慢。 - 持久化(persistence_timeout):HTTP/3 支持连接迁移(connection migration),客户端可能在 TCP 和 UDP 之间切换。建议两边的
persistence_timeout保持一致(如 60s),或者根据实际场景调大。
二、Nginx reuseport 冲突
问题
Nginx 开启 HTTP/3 时,配置通常是:
listen 443 ssl;
listen 443 quic reuseport;如果你有多个 server block(多个域名)都配置了 listen 443 quic reuseport,nginx -t 会报错:
nginx: [emerg] duplicate listen options for 0.0.0.0:443原因
reuseport 是内核的 SO_REUSEPORT 套接字选项,同一个端口只能有一个 socket 持有它。两个 server block 都写 reuseport,等于两个 socket 抢同一个端口的 reuseport,内核拒绝。
解决方案
只有一个 server block 保留 reuseport,其他去掉。 Nginx 会通过 SNI(TLS 握手中的 Server Name Indication)自动路由到正确的 server block。
# 域名 A - 保留 reuseport(主 server block)
server {
listen 443 ssl;
listen 443 quic reuseport; # ← 只有这里用 reuseport
server_name a.example.com;
# ...
}
# 域名 B - 去掉 reuseport
server {
listen 443 ssl;
listen 443 quic; # ← 不带 reuseport
server_name b.example.com;
# ...
}流程
客户端 HTTP/3 请求 b.example.com:443/UDP
│
▼
内核收到 UDP 443 包 → 只有 reuseport 的 socket 在收
│
▼
Nginx worker 通过 SNI 解析出 "b.example.com"
│
▼
匹配到域名 B 的 server block → 正常处理两个域名的 HTTP/3 流量都走同一个 reuseport socket 进来,Nginx 内部按 SNI 分发,不影响功能。
三、防火墙 / 安全组
必须放行的端口
| 端口 | 协议 | 用途 |
|---|---|---|
| 80 | TCP | HTTP(通常 301 到 HTTPS) |
| 443 | TCP | HTTPS / HTTP/2 |
| 443 | UDP | HTTP/3 (QUIC) |
很多人只记得开 TCP 443,忘了 UDP 443,导致 HTTP/3 从外网完全不可达。
iptables 示例
# 放行 UDP 443
iptables -A INPUT -p udp --dport 443 -j ACCEPT
# 如果用了 firewalld
firewall-cmd --permanent --add-port=443/udp
firewall-cmd --reload云厂商安全组
阿里云、腾讯云等云平台的安全组需要同时添加 TCP 443 和 UDP 443 的入站规则。很多人在控制台只加了 TCP,UDP 被默认拒绝。
四、Nginx 完整配置要点
一个同时支持 HTTP/2 和 HTTP/3 的 server block 关键配置:
server {
# TCP: HTTP/1.1 + HTTP/2
listen 443 ssl;
# UDP: HTTP/3 (QUIC) —— 只在第一个 server block 加 reuseport
listen 443 quic reuseport;
http2 on;
http3 on;
server_name example.com;
# SSL
ssl_certificate /etc/nginx/ssl/example.com.pem;
ssl_certificate_key /etc/nginx/ssl/example.com.key;
ssl_protocols TLSv1.2 TLSv1.3; # QUIC 要求 TLS 1.3
ssl_prefer_server_ciphers on;
ssl_session_tickets off; # 建议关闭,QUIC 自带 0-RTT
# HTTP/3 通告(告诉浏览器可以用 H3)
add_header Alt-Svc 'h3=":443"; ma=86400' always;
# ... 其他配置 ...
}几个容易忽略的点
- QUIC 强制要求 TLS 1.3:
ssl_protocols里如果没有TLSv1.3,HTTP/3 直接不能用。 ssl_session_tickets off:QUIC 的 0-RTT 机制自带会话恢复能力,开启 session tickets 可能导致重放攻击风险。Alt-Svc头必须有:这是浏览器发现 HTTP/3 的唯一途径。没有这个头,浏览器会一直用 HTTP/2。http3 on可以全局开,也可以单 server block 开:如果只想部分域名开 HTTP/3,就在对应 server block 里单独加。
五、LVS DR 模式的 UDP 转发原理
DR 模式工作流程
1. 客户端发送 UDP 包到 VIP(192.168.5.5:443)
│
2. LVS Director 收到包,根据调度算法选一台 Real Server
│
3. Director 将包的目标 MAC 改为 Real Server 的 MAC,目标 IP 保持 VIP 不变
│
4. Real Server 收到包(因为绑定了 VIP,能识别目标 IP)
│
5. Real Server 处理后直接回给客户端(不经过 Director)关键:Real Server 必须绑定 VIP
LVS DR 模式下,Real Server 需要在 lo 接口上绑定 VIP,并配置 ARP 抑制:
# Real Server 上执行(开机自启脚本)
#!/bin/bash
VIP=192.168.5.5
# lo 接口绑定 VIP
ifconfig lo:0 $VIP broadcast $VIP netmask 255.255.255.255 up
# ARP 抑制:不响应 VIP 的 ARP 请求
echo "1" > /proc/sys/net/ipv4/conf/lo/arp_ignore
echo "2" > /proc/sys/net/ipv4/conf/lo/arp_announce
echo "1" > /proc/sys/net/ipv4/conf/all/arp_ignore
echo "2" > /proc/sys/net/ipv4/conf/all/arp_announce注意:这个配置对 TCP 和 UDP 都生效,不需要额外为 UDP 做特殊处理。DR 模式工作在 MAC 层,与传输层协议无关。
六、常见排障清单
| 现象 | 可能原因 | 排查方法 |
|---|---|---|
| HTTP/2 正常,HTTP/3 不通 | UDP 443 未放行 | nc -u -zv VIP 443 测试 UDP 连通性 |
| HTTP/3 部分域名不通 | reuseport 冲突 | 检查所有 server block,确保只有一个用 reuseport |
| LVS 不转发 UDP 流量 | keepalived 没配 UDP virtual_server | 检查 ipvsadm -Ln 是否有 UDP 443 条目 |
| 浏览器不升级到 HTTP/3 | 缺少 Alt-Svc 响应头 | curl -I https://example.com 检查响应头 |
| QUIC 连接建立失败 | TLS 版本不对 | 确认 ssl_protocols 包含 TLSv1.3 |
| LVS 调度不均 | persistence_timeout 太大 | 减小持久化超时或确认是否需要会话保持 |
| Real Server 收不到包 | ARP 问题 | arping -I eth0 VIP 检查 ARP 是否正常 |
七、性能调优建议
Nginx 侧
# QUIC 连接池大小(影响并发 HTTP/3 连接数)
quic_retry on; # 防放大攻击,建议开启
# UDP buffer 调优(高并发场景)
server {
listen 443 quic reuseport;
# ...
}内核侧
# UDP 接收缓冲区(高并发 QUIC 场景)
sysctl -w net.core.rmem_max=8388608
sysctl -w net.core.rmem_default=1048576
# UDP 发送队列
sysctl -w net.core.wmem_max=8388608
# 连接跟踪表(QUIC 连接数多时可能不够)
sysctl -w net.netfilter.nf_conntrack_udp_timeout=30
sysctl -w net.netfilter.nf_conntrack_udp_timeout_stream=120keepalived 侧
# 缩短 UDP 健康检查间隔(更快感知故障)
virtual_server 192.168.5.5 443 {
delay_loop 3 # TCP 用 5,UDP 可以更短
protocol UDP
# ...
}八、总结
在 LVS DR + Nginx 架构上启用 HTTP/3,核心就三件事:
- Keepalived 加 UDP 虚拟服务——让 LVS 能调度 UDP 443 流量。
- Nginx
reuseport只开一次——多域名场景下避免 duplicate listen。 - 防火墙放行 UDP 443——最容易忘的一步。
其他都是锦上添花。把这三点做对,HTTP/3 就能在你的架构里跑起来。
写于 2026 年 6 月 15 日,基于实际踩坑经验整理。