websocket在kubernetes ingress下频繁断连的根本原因是ingress默认60秒超时及缺失cookie会话保持,需同时配置proxy-read-timeout/proxy-send-timeout为3600秒以上并启用nginx.ingress.kubernetes.io/affinity: "cookie"。

WebSocket 在 Kubernetes Ingress 下频繁断连,根本原因不是代码写错了,而是默认配置把长连接当短请求处理了——必须显式调大超时 + 启用 Cookie 级会话保持,缺一不可。
为什么默认 Ingress 会让 WebSocket 断在 60 秒
Nginx Ingress Controller 默认把所有流量都按 HTTP 短连接处理:proxy-read-timeout 和 proxy-send-timeout 都是 60 秒。WebSocket 握手成功后若 60 秒内没数据帧(比如用户静默),Ingress 就主动关闭 TCP 连接,客户端收到 WebSocket closed before the connection is established 或直接触发 onclose 事件。
这不是后端服务的问题,也不是客户端网络问题,是 Ingress 层的硬性截断。
- 必须通过
nginx.ingress.kubernetes.io/proxy-read-timeout和nginx.ingress.kubernetes.io/proxy-send-timeout注解覆盖,默认值 60 秒完全不适用 WebSocket - 推荐设为
"3600"(1 小时),极端场景可设"86400"(24 小时),但要注意后端自身心跳是否匹配 - 仅改超时还不够:多副本部署下,后续 ping/pong 帧可能被轮询分发到不同 Pod,因无上下文直接 RST
必须用 Cookie affinity,别碰 Service 的 ClientIP
Kubernetes Service 层的 sessionAffinity: ClientIP 对 WebSocket 几乎无效——它只在 L4 生效,无法识别 Upgrade: websocket 协议,且 NAT/CDN 下大量用户 IP 冲突,导致单 Pod 过载甚至 OOM。
评估 Kubernetes 集群安全态势,覆盖 RBAC、工作负载安全、网络策略、基础设施即代码(IaC)、运行时监控和密钥管理等 30 项控制项……
真正起作用的是 Ingress 控制器在 L7 解析 HTTP 头后做的路由决策,即 nginx.ingress.kubernetes.io/affinity: "cookie"。
- 该注解会让 Ingress 在首次响应中注入一个
Set-Cookie: INGRESSCOOKIE=xxx,后续请求携带该 Cookie 即固定路由 - 可自定义 Cookie 名:加注解
nginx.ingress.kubernetes.io/session-cookie-name: "WSROUTE" - 控制扩容行为:加
nginx.ingress.kubernetes.io/affinity-mode: "balance",新 Pod 上线后部分会话会迁移;设"persistent"则永不迁移
完整 Ingress YAML 示例与关键避坑点
以下是最小可行配置,复制即用,但注意三处易错细节:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: ws-ingress
annotations:
nginx.ingress.kubernetes.io/proxy-read-timeout: "3600"
nginx.ingress.kubernetes.io/proxy-send-timeout: "3600"
nginx.ingress.kubernetes.io/affinity: "cookie"
nginx.ingress.kubernetes.io/session-cookie-name: "WSROUTE"
nginx.ingress.kubernetes.io/affinity-mode: "balance"
spec:
ingressClassName: nginx
rules:
- host: ws.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: ws-service
port:
number: 80
- 注解必须写在
metadata.annotations下,写进spec或错位缩进会导致静默忽略 -
pathType: Prefix是必须项,K8s 1.22+ 不再接受空值或省略 - 确保后端服务返回的响应头包含
Connection: upgrade和Upgrade: websocket,否则 Ingress 不会启用 WebSocket 模式
客户端和服务端还需配合做两件事
Ingress 配对只是链路一环,客户端和服务端若不做适配,照样断连。
- 客户端必须实现心跳:每 30 秒发一次
ws.ping(),避免空闲超时;不要依赖浏览器自动保活 - 服务端需启用对应心跳响应(如 Spring Boot 的
stompClient.setHeartbeat(10000, 10000)),否则 Ingress 收不到 pong 帧仍会关连接 - 如果用了 SockJS,确认启用了
withSockJS().setHeartbeatTime(5000),并关闭其 fallback 机制(如禁用 XHR long-polling),否则 Ingress 可能降级处理
最常被忽略的是:Ingress 的超时设置和服务端心跳周期必须严格对齐,比如 Ingress 设了 3600 秒,但服务端心跳间隔是 60 秒,中间若丢一个 pong,Ingress 会在第 3601 秒断连——这个时间差不会报错,只会静默重连。










