NGINX配置proxy_http_version 1.1和proxy_set_header Connection ""【转】

开场

碰到一个线上问题:高负载下,后端 App Pod 一重启,经过 Nginx 转发的这条链路 TPS 恢复特别慢,要 10 分钟左右才能回满;但低负载下完全没这现象,而且直连 App 服务又一点事没有。

复现文件:一个 web-nginx 的 Deployment YAML,一份线上 nginx.conf。我第一反应其实不是 Nginx。因为低负载完全正常,直连 App 也正常,而问题只发生在“高并发 + 后端 Pod 重启”这个组合里。所以我当时先怀疑的是 Pod 摘除、kube-proxy/IPVS,甚至考虑过是不是旧 Pod 的连接没有及时清掉。

结果最后真正让我停下来看的,反而是 nginx.conf 里一眼就能看到的 keepalive 64。
这篇文章记录我完整的复现与排查过程,报错、压测数据、IPVS 状态都在下面,你可以照着复盘。

先说结论(省得你滑到底)

客户 nginx.conf 的 upstream 里写着 keepalive 64,但 locationproxy_pass 没有 proxy_http_version 1.1,也没有 proxy_set_header Connection ""。而 Nginx 对 proxy_pass 默认走 HTTP/1.0——upstream 里的 keepalive 是"死配置",压根不生效。每个请求仍然给后端新建一条 TCP 连接,高并发下 TCP 连接风暴把链路打崩,Pod 一重启,恢复到正常要等很久。

修复只需在 location 补两行:

proxy_http_version 1.1;
proxy_set_header Connection "";

环境:测试现场(已脱敏)

项目
Kubernetes v1.23.15
OS Ubuntu 20.04.1 LTS, Kernel 5.4.0-51
Nginx 1.26.3(Deployment 2 副本)
kube-proxy IPVS
CNI Calico
后端 app-upg:9090(2 副本,重启的是它)
命名空间 <脱敏>(测试业务命名空间)

我的复现测试集群:这是按复现 K8s 版本(v1.23.15)独立搭建的一套隔离复现环境,复现里我用 echoserver 充当客户后端 App(命令里是 test-app-svc:8080),没有触碰客户真实环境,只复刻链路形态。后面所有删除 Pod、IPVS 采样等操作都只在这套隔离环境里做。

数据链路:wrk → Nginx(2 pod) → SVC(ClusterIP) → kube-proxy(IPVS) → App(2 pod)

我先怀疑了什么

环境 Deployment 里 Nginx 的探针、preStop sleep 10 这些我扫了一眼,先放一边。真正扎眼的是这份线上 nginx.conf:

upstream gatewayApp {
    server app-upg:9090 max_fails=3 fail_timeout=10s;
    keepalive 64;          # ← 写着呢
    keepalive_requests 300;
    keepalive_timeout 5s;
}

location /app {
    proxy_pass http://gatewayApp;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $http_x_forwarded_for;
    proxy_next_upstream error timeout http_502 http_503;
    proxy_connect_timeout 1s;
    # ← 没有 proxy_http_version 1.1
    # ← 没有 proxy_set_header Connection ""
}

第一眼:keepalive 有啊,64 都写了。这配置"看起来"挺完整。但我心里打了个问号:keepalive 要和 proxy_http_version 1.1 + Connection "" 成对使用才生效,这套配置里根本没有这两行。

图片

我的判断是:在本文这个 Nginx 代理场景中,proxy_pass 默认使用 HTTP/1.0;如果希望使用 upstream keepalive,需要让代理到上游的请求采用 HTTP/1.1,并避免向上游发送 Connection: close。但我不能只靠"记得文档"就下结论,得场景复现并给出有力结论。

设计三组对照,把"问题在哪一环"钉死

为了不让"Nginx 就是凶手"成为我的一厢情愿,我做了三组对照:

测试路径Nginx 配置目的
Test A wrk → App SVC(直连) 无 Nginx 验证 App 本身是否正常
Test B wrk → Nginx → App SVC 客户原始配置(无 proxy_http_version 1.1 试着复现客户问题
Test C wrk → Nginx → App SVC proxy_http_version 1.1 + Connection "" 验证修复是否有效

三组统一参数:wrk -t8 -c500 -d600s(8 线程 500 并发跑 10 分钟),压测启动约 40 秒后 kubectl delete pod --force --grace-period=0 删掉 1 个后端 Pod,模拟"Pod 重启"。

先做一个直连测试

直连 App SVC(跳过 Nginx),删一个 Pod,看 App 自己行不行:

kubectl exec -n wx-demo fortio -- sh -c '
  nohup wrk -t8 -c500 -d600s --latency http://test-app-svc:8080/ > /tmp/wrk_testA.log 2>&1 &'

# 40 秒后删 1 个后端 Pod
kubectl delete pod -n wx-demo -l app=test-app --force --grace-period=0

结果:App没问题

总请求 12,610,067    HTTP 错误 0 (0%)    QPS 21,016    P50 6.49ms    P99 1.29s

图片

直连 0 错误,QPS 2 万,删 Pod 也毫无影响。 → App 和 IPVS 本身没问题,问题排除到"Nginx 这一段"。这一步很重要:把 App 端先摘干净,后面所有怀疑都集中在 Nginx 链路上,不会被表象带偏。

把Nginx原配置搬进复现环境

换客户原始 Nginx 配置,同样删一个后端 Pod,结果:

总请求 908,405    HTTP 错误 259,763 (28.6%)    QPS 1,513.78    P50 339ms    P99 1.00s

图片

我第一次看到 Test B 的 1513 QPS 时,其实就基本确定 Nginx 这一层有问题了。因为直连是 2.1 万 QPS,同样的 App、同样的 500 并发,只是中间多了一层 Nginx,性能直接掉了一个数量级。

问题真的复现了:28.6% 的错误率,QPS 从 2 万砸到 1,513。和客户描述完全对得上(10 分钟 TPS 起不来)。

本想看 IPVS 现场,结果它“太干净”了

测试过程:我在 master 节点起了一个每 6 秒采样的 ipvsadm 监控,盯着 test-app-svc 的 real server 表:
看被删那个后端 RS 在删除瞬间前后的状态(采样间隔 6 秒,23:47:4623:47:52):

ipvsadm -L -n -t 10.233.26.224:8080

结果没复现:这套环境里我压根没抓到 ActiveConn 堆积
IPVS 没抓到我想要的证据
到这里其实有点尴尬。我原本以为能抓到旧 Pod 的 ActiveConn 堆积,然后顺着连接一直追到旧 Pod。
结果没有。ActiveConn基本一直是0,我甚至把采样间隔从6s调整到3s,还是没有。

# 删除前(23:47:46):两个后端都在表里
TCP  10.233.26.224:8080 rr
  -> 10.233.92.10:8080   Masq  1  0  0
  -> 10.233.96.8:8080    Masq  1  0  0
# 删除后(23:47:52):旧后端 96.8 已被清掉,新 pod 96.9 进入表里,仍是 2 个后端
TCP  10.233.26.224:8080 rr
  -> 10.233.92.10:8080   Masq  1  0  0
  -> 10.233.96.9:8080    Masq  1  0  0

这时候我反而觉得这个结果挺重要:不能因为客户现场观察到了某个现象,就强行让自己的复现环境也“复现”出来。

⚠️ 这点我必须如实说:我没有复现出客户现场的 ActiveConn 堆积。不同的后端应用(环境是高开销业务、我这边是瞬时响应的 echoserver)、不同的采样频率,都会影响能不能观察到这个现象。所以这篇我不把"连接堆积/路由到死 Pod"当成我验证过的事实,只当它是客户现场的观测现象 + 一种可能的机制推断。

图片

反而是nginx.conf里的两行配置暴露了问题

在 location 里补上 proxy_http_version 1.1;proxy_set_header Connection "";,其它不动,重跑同样压测+删 Pod:

总请求 12,225,973    HTTP 错误 6,877 (0.056%)    QPS 20,371    P50 3.50ms    P99 1.54s

图片

  • • 错误率从 28.6% → 0.056%(降幅 99.8%)
  • • QPS 从 1,513 → 20,371(提升 13.5 倍,已恢复到直连水平)
  • • 新 Pod 删除后自动接管流量,链路恢复如初

我不想靠“看配置猜原因”,所以直接做了三个实验。数据对比(都是 500 并发、10 分钟、同一部署):

指标Test A 直连Test B 客户原始Test C 修复
总请求 12,610,067 908,405 12,225,973
HTTP 错误 0 (0%) 259,763 (28.6%) 6,877 (0.056%)
QPS 21,016 1,513.78 20,371
P50 6.49ms 339ms 3.50ms

根因到底怎么解释(含我的推断边界)

先看铁证(压测数据钉死的):直连 0 错误 → 经 Nginx 原始配置 28.6% 错误 → 加 proxy_http_version 1.1 + Connection "" 后降到 0.056%。链路里唯一变化的就是 Nginx 这两行,问题定位在 Nginx ↔ 后端的连接管理上,确定无疑。

至于"为什么慢、为什么错误持续",我的理解是:

  1. 1. 已经验证的
    原配置下高并发场景出现 28.6% HTTP 错误、QPS 1513;增加两项代理连接配置后,错误率下降到 0.056%,QPS 恢复到 20371。
  2. 2. 可以合理推断
    原配置下 Nginx 到 upstream 的连接复用没有按预期工作,高并发时需要频繁建立上游连接,因此连接建立成本明显增加。
  3. 3. 目前尚未验证
    至于环境现场为什么会出现长达 10 分钟的恢复过程,以及旧 Pod 连接是否进一步受到 conntrack/IPVS 等机制影响,我这次的 echoserver 环境没有复现,因此这里不把它作为最终根因。

这次排障,我最后记住了什么?

这次排查下来,真正让我印象比较深的,其实不是最后加上的那两行配置,而是一个很容易被忽略的问题:

配置文件里“写了”,不代表它真的按你想的方式生效。

复现环境的 upstream 里明明已经有:

keepalive 64;

第一眼看过去,很容易觉得连接复用已经配置好了。

但实际测试下来,原配置在 500 并发、后端 Pod 重启的场景下,QPS 只有 1513.78,HTTP 错误率达到 28.6%

而在同样的测试条件下,只增加:

proxy_http_version 1.1;
proxy_set_header Connection "";

QPS 就恢复到了 20371,错误率下降到 0.056%

所以这次排障给我最大的一个提醒就是:

排查配置问题时,不要只看“有没有配”,还要确认“这个配置到底有没有生效”。

另外还有一个细节,我觉得也值得记录下来。

这次我原本想通过 IPVS 的 ActiveConn 去证明旧 Pod 连接没有及时释放,但在自己的复现环境里并没有抓到这个现象。

所以最后文章里,我没有把它硬写成确定的根因。

客户现场看到的现象,可以作为排查线索;自己没有验证出来的,就不要当成已经证明的结论。

对于技术排障来说,我越来越觉得,“没复现出来”本身也是测试结果。

这次最终能确认的是:

  • • 原始 Nginx 配置在高并发 + Pod 重启场景下确实可以复现明显性能问题;
  • • 直连 App 时没有出现同样的问题;
  • • 修改 Nginx 上游连接配置后,QPS 和错误率明显恢复;
  • • IPVS ActiveConn 长时间堆积这一现象,在我的复现环境中没有验证出来。

这可能也是我现在比较喜欢记录这类问题的原因。

不是单纯把“正确配置”贴出来,而是把我当时怎么怀疑、怎么验证、哪里没验证出来,以及最后到底能确定什么都记录下来。

如果你也在折腾 Kubernetes、Nginx、Ingress、IPVS 这些东西,后面我还会继续记录一些实际环境里的问题复现和测试过程。

这里是《云原生测试笔记》,关注一下,后面继续一起踩坑、排坑。

转自

Nginx 配了 keepalive,为什么高并发下还是恢复很慢?我做了 3 组对照测试

 

posted @ 2026-09-10 17:37  paul_hch  阅读(4)  评论(0)    收藏  举报