一、写在前面:你到底需要哪种负载均衡?
用Nginx处理负载均衡超过10年了,说句大实话:70%的团队根本不需要Envoy。但又不得不承认,在某些场景下,Envoy能救你一命。最近几个月,国内多家云厂商和社区都在讨论K8s网关选型、Envoy与Nginx的对比,以及Envoy在生产环境落地时踩过的坑,下面我会把这些一手经验揉碎了讲。
读完本文,你至少能获得:5套能直接拿去用的Nginx配置模板、Envoy核心配置示例、一套速查式高频报错排查表,以及我用几年才换来的若干配置陷阱。
前置条件(老实说,这些要准备好)
- 一台已经装好Nginx(>=1.20,我用的是1.26,不过旧版本也差不多)或Envoy(>=1.28,实测1.28在K8s环境下有更好支持)的机器。
- 有sudo权限,至少能改/etc/nginx/nginx.conf。我的实验环境是在MacOS上做的,但核心配置在Linux上一样跑得通。
二、Nginx 生产级配置------高频场景直接抄
下面给出3个可以直接拿去用的场景模板。别一股脑全抄,根据你的业务选一个就行。
2.1 场景一:Web服务/API网关(最常用)
这个模板解决了我遇到的80%的问题。核心思路是:连接复用+被动健康检查+合理超时。
upstream backend_api {
# 后端服务器列表
server 10.0.0.1:8080 weight=3 max_fails=3 fail_timeout=30s;
server 10.0.0.2:8080 weight=2 max_fails=3 fail_timeout=30s;
server 10.0.0.3:8080 weight=1 max_fails=3 fail_timeout=30s;
# 关键:保持与后端的空闲连接,连接数建议设为后端服务器数量的两倍
keepalive 32;
}
server {
listen 80;
server_name api.example.com;
location / {
proxy_pass http://backend_api;
# HTTP/1.1是长连接的前提(HTTP/1.0默认是短连接,必须改成1.1)
proxy_http_version 1.1;
# 清空Connection头,否则可能被设置为"close"
proxy_set_header Connection "";
# 这些超时参数很关键------别让慢后端拖死整个服务
proxy_connect_timeout 3s;
proxy_send_timeout 30s;
proxy_read_timeout 30s;
# 透传客户端真实信息(否则后端日志里全是Nginx的IP)
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
预期效果 :QPS从几千提升到几万(我见过最夸张的优化是5k→50k)。关键参数keepalive 32控制每个worker与后端保持的空闲连接数,如果后端服务器有10台,keepalive 20就差不多够了------连接太多反而浪费资源。
2.2 场景二:长连接/WebSocket/直播推流
长连接场景有个致命问题:后端服务器重启后,Nginx还傻乎乎地往那个节点发请求。这个模板里加了slow_start参数(需要Nginx Plus),能让新加入的节点像温水煮青蛙一样慢慢承载流量,不至于一上来就被冲垮。
upstream backend_ws {
# 长连接场景下,被动健康检查的超时要调大
server 10.0.0.1:9000 max_fails=2 fail_timeout=60s slow_start=30s;
server 10.0.0.2:9000 max_fails=2 fail_timeout=60s slow_start=30s;
# 长连接场景可以用least_conn,让连接更均衡
least_conn;
keepalive 64; # 长连接场景下适当增加keepalive数量
}
server {
listen 80;
# 长连接的超时时间通常要调大,否则5分钟就断你一次
proxy_read_timeout 3600s;
proxy_connect_timeout 75s;
location /ws {
proxy_pass http://backend_ws;
proxy_http_version 1.1;
# WebSocket需要显式升级协议
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}
2.3 全局性能优化参数(Nginx层面)
这部分是Nginx主配置文件nginx.conf里的参数,直接影响整机性能。我在高并发项目里的标准配置是这样的:
user nginx;
# 自动匹配CPU核心数
worker_processes auto;
events {
# Linux下必须用epoll,比select/poll快10倍不止
use epoll;
# 每个worker的最大连接数(受系统ulimit限制)
worker_connections 65535;
# 一次性接受所有新连接,高并发下提升吞吐量
multi_accept on;
}
http {
# 开启sendfile和tcp_nopush,静态文件吞吐量能提升30%以上
sendfile on;
tcp_nopush on;
tcp_nodelay on;
# 长连接超时设置
keepalive_timeout 65;
keepalive_requests 1000;
# 文件描述符缓存,减少磁盘IO
open_file_cache max=100000 inactive=20s;
open_file_cache_valid 30s;
open_file_cache_min_uses 2;
}
提醒一下 :改完worker_connections后记得同步改系统级的ulimit -n,否则Nginx会报"too many open files"。还有use epoll只对Linux有效,Mac上要用kqueue。
三、Envoy 云原生配置------K8s 场景可复用
Envoy在K8s里表现确实不错,xDS协议支持动态更新,不用像Nginx那样每次改完配置都要reload。但我得先说一个坑:从Nginx切到Envoy,如果没有足够理由,别轻易做,下文第五部分会详细展开讲这个。
3.1 静态配置版(适合非K8s环境)
下面是一个完整的Envoy静态配置,涵盖HTTP连接管理和负载均衡:
static_resources:
listeners:
- name: listener_http
address:
socket_address:
address: 0.0.0.0
port_value: 10000
filter_chains:
- filters:
- name: envoy.filters.network.http_connection_manager
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
stat_prefix: ingress_http
codec_type: AUTO
route_config:
name: local_route
virtual_hosts:
- name: backend
domains: ["*"]
routes:
- match:
prefix: "/"
route:
cluster: web_service
# 超时配置很重要
timeout: 30s
http_filters:
- name: envoy.filters.http.router
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router
clusters:
- name: web_service
connect_timeout: 5s
type: STRICT_DNS
lb_policy: LEAST_REQUEST
# Outlier Detection 是被动健康检查的核心配置
outlier_detection:
consecutive_5xx: 3
interval: 10s
base_ejection_time: 30s
max_ejection_percent: 50
load_assignment:
cluster_name: web_service
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address:
address: 10.0.0.1
port_value: 8080
load_balancing_weight: 3
- endpoint:
address:
socket_address:
address: 10.0.0.2
port_value: 8080
load_balancing_weight: 2
Envoy的负载均衡策略挺全的:ROUND_ROBIN(轮询)、RANDOM(随机)、LEAST_REQUEST(最少请求,这是默认策略)、MAGLEV(一致性哈希,用于需要会话保持的场景)。
四、NGINX vs Envoy 选型参考表
做了个对比表,你对着看就知道该选哪个了。
|--------------|----------------|---------------|------------------|---------|
| 维度 | NGINX(开源版) | NGINX Plus | Envoy | 我的个人倾向 |
| 四层QPS | ~12万 | ~12万 | ~8.5万 | Nginx胜出 |
| 七层复杂策略效率 | 一般(Lua扩展) | 一般 | 较高(WASM比Lua快40%) | Envoy胜出 |
| P99延迟 | 约5ms | 约5ms | 约2ms | Envoy胜出 |
| 热更新 | reload(约5-10秒) | reload | 零中断 | Envoy完胜 |
| 内存占用 | 30-50MB/核 | 30-50MB/核 | 80-120MB/核 | Nginx完胜 |
| 主动健康检查 | 不支持 | 支持 | 支持 | Envoy胜出 |
| 配置变更 | reload/restart | API动态更新 | xDS动态下发 | Envoy胜出 |
| 上手难度 | 低 | 中 | 高 | Nginx胜出 |
| 日志/监控 | stub_status | Live Activity | 原生Prometheus | Envoy胜出 |
数据来源:综合2025年多个公开性能测试,其中四层QPS数据来自Nginx 1.23(0.3ms延迟)与Envoy 1.25(0.8ms延迟)的对比测试。
我的结论:如果你的后端是传统单体或微服务数量不超过50个,无脑选Nginx就够了。但如果是超过100个微服务的K8s环境、需要分布式追踪、金丝雀发布等高级功能,Envoy值得考虑。
五、血泪总结:我踩过的配置坑与解法
下面这些坑,每一个都是我或同行在生产环境里真金白银换来的。随便挑两个出来讲讲。
坑1:Nginx upstream 权重配置失效
某次上线后,监控显示流量分配完全不是预期的75%:25%。排查了两个小时才发现:upstream块里同时配置了ip_hash,这个指令会完全绕过权重设置。
解法 :如果用权重,必须用默认的轮询算法,不能用ip_hash、least_conn这些。也就是说要么用权重,要么用粘滞会话,不能既要又要。
坑2:proxy_pass 尾斜杠导致 404
这个坑比较经典,我在Stack Overflow上看到过好多次。直接看例子:
proxy_pass http://backend;------ 把原始URI原封不动传给后端。proxy_pass http://backend/;------ 多一个斜杠,会截掉location匹配的部分,导致路径丢失。
解法 :在location里加上proxy_set_header Host $host;。如果后端需要固定Host头,写成proxy_set_header Host "backend.example.com";也行。
坑3:No healthy upstream 全线崩溃
这类错误通常发生在所有后端服务器同时挂掉或健康检查全部失败的时候。Nginx的错误日志会显示类似这样的信息:[error] no live upstreams while connecting to upstream。
排查步骤(按顺序来):
- 检查后端服务状态:
systemctl status或docker ps -a。 - 测试网络连通性:
curl -v http://backend_ip:port/health。 - 检查健康检查配置:确保
max_fails和fail_timeout设置合理。
我的建议 :至少配置一个backup节点作为备用,或者在K8s中至少保持2个副本。后端服务最好提供专门的/health健康检查接口,别用业务接口凑合。
坑4:将 Nginx 换成 Envoy 后踩的坑
今年有个团队做了迁移,从Nginx+Lua换成Envoy,计划2周,实际花了6周。主要栽在:
- Lua脚本没法平迁 :Envoy的Lua Filter只支持5.1,连
table.pack都没有,最后把最重的鉴权逻辑拆成了Go的External Authorization Server。 - metrics数量爆炸:默认拉一次有3万多个指标,Grafana直接卡死,用stats-matcher正则过滤后指标数降到4198个。
- TLS指纹导致老Android机握手失败:Nginx用OpenSSL 1.1.1,Envoy默认用BoringSSL,指纹不一致,线上Android 7.0以下机型握手失败率2.8%。
我的建议:如果你现在的Nginx+Lua跑得好好的,不要为了"技术时髦"强行换Envoy。除非你有明确的痛点(比如需要零中断热更新、需要原生Prometheus监控、需要分布式追踪)。
六、高频报错排查表(直接查)
|--------------------------------------|---------------------|---------------------------------|
| 报错现象 | 可能原因 | 快速修复 |
| 502 Bad Gateway | 后端服务挂了 / 超时 | 检查后端状态、调大proxy_read_timeout |
| 504 Gateway Timeout | 后端响应超时 | 调大proxy_read_timeout、排查慢SQL |
| upstream timed out | 连接池满了 / keepalive耗尽 | 调大keepalive、增加worker数量 |
| no live upstreams | 所有后端都被健康检查踢出 | 检查后端/health接口、添加backup节点 |
| [emerg] duplicate upstream | 多个配置文件定义了同名upstream | 确保upstream名称唯一 |
| [emerg] host not found in upstream | upstream中使用了不可解析的域名 | 改用IP地址或在resolver中配置DNS |
| 404(upstream模式失败,单节点成功) | Host头传递错误 | 加上proxy_set_header Host $host |
写到这里,基本把生产环境中负载均衡的核心问题都覆盖了。最后想说的是:配置只是手段,真正重要的是理解你的业务流量特征。你是高并发短连接?还是长连接密集型?你的后端能否承受健康检查的探测压力?想清楚这些问题,选哪个工具其实都是次要的。
如果觉得这篇文章对你有帮助,欢迎点赞收藏,也欢迎在评论区分享你遇到过的奇葩负载均衡问题------说不定你的踩坑经历能帮到下一个人。