根本原因是gRPC客户端默认pick_first策略且DNS未刷新,导致只连首个Pod IP;Nginx七层代理不干预连接选择,负载均衡失效。
gRPC长连接在Nginx七层负载下为何只打一个后端
根本原因不是nginx配置漏了,而是grpc客户端默认走pick_first策略 + dns解析未刷新,导致所有请求复用首个解析出的pod ip。nginx本身对grpc的七层代理(grpc_pass)是透明转发,它不拆包、不干预连接选择——流量是否均匀,完全取决于客户端建连行为。
常见错误现象:upstream_response_time日志里90%请求都落在同一台后端,$upstream_addr固定显示单个IP;用ss -tnp | grep :port在客户端Pod里查连接,也只看到一条ESTABLISHED到某Pod的TCP连接。
- 确认Nginx Ingress Controller版本 ≥ 1.0.0(需支持
grpc_pass和HTTP/2透传) - 检查Ingress资源是否显式声明
nginx.ingress.kubernetes.io/backend-protocol: "GRPC" - 确保upstream服务使用Headless Service(
clusterIP: None),否则DNS只会返回ClusterIP,无法做多实例发现
为什么加了least_conn或round_robin也没用
Nginx的负载均衡算法对gRPC长连接基本无效。因为gRPC客户端一旦建立连接,就会长期复用该TCP连接发送多个RPC调用,而Nginx的least_conn等策略只在新建连接时生效——后续所有请求都走已有连接,Nginx根本不参与“请求级”分发。
真正起作用的是gRPC客户端内置的Balancer策略。若未显式设置,Go/Java/Python SDK默认都是pick_first,即只选DNS解析结果里的第一个地址建连。
- Go客户端必须加服务配置:
grpc.WithDefaultServiceConfig(`{"loadBalancingConfig": [{"round_robin": {}}]}`) - 地址必须带
dns:///前缀,不能是my-svc.default.svc.cluster.local:8080(否则跳过DNS解析器) - Python客户端(
grpcio ≥ 1.40)需额外禁用系统缓存:[('grpc.dns_min_time_between_queries_ms', 1000)]
如何验证DNS是否真返回多条A记录
这是最关键的前置条件。如果dig只返回一个IP,后面所有负载均衡配置都是空谈。
进任意客户端Pod执行:
dig +short my-svc.default.svc.cluster.local
正确输出应类似:
10.244.1.12<br>10.244.2.8<br>10.244.3.5
若只有一条或无响应:
- 检查Service是否为Headless:
kubectl get svc my-svc -o yaml,确认spec.clusterIP为None - 检查CoreDNS日志是否有NXDOMAIN或SERVFAIL(
kubectl logs -n kube-system deployment/coredns) - 避免在Service名后误加
.svc.cluster.local两次(如my-svc.default.svc.cluster.local.default.svc.cluster.local)
用$upstream_connect_time和$upstream_response_time定位瓶颈层
Nginx日志里的$request_time包含TLS握手、网络延迟、后端处理等全部耗时,无法区分问题在Nginx侧还是客户端侧。
在log_format中加入这两个字段:
log_format grpc_log '$remote_addr - $remote_user [$time_local] "$request" '$status $body_bytes_sent "$http_referer" "$http_user_agent" '$request_time $upstream_connect_time $upstream_response_time $upstream_addr';
重点看三组值:
- 若
$upstream_connect_time差异大(比如有的0.002s,有的0.3s),说明客户端建连阶段就已不均——大概率是DNS没刷或Balancer没生效 - 若
$upstream_response_time持续偏高且集中在某IP,说明该后端Pod本身处理慢,和负载均衡无关 - 若所有
$upstream_addr都一样,但$upstream_connect_time极小(
真正容易被忽略的是:Nginx七层gRPC负载均衡的“均匀性”,本质上依赖客户端主动重建连接的能力。没有定期重连、没有健康探测、没有DNS轮询,再好的Nginx配置也救不了单点长连接。


















