PAYATHON 2026

Traefik Swarm 后 SignalR WebSocket 间歇性失败如何排查?

数据阿明

结论

gRPC 和 WebSocket 可以共用同一个 Kestrel 端口。两者通过 HTTP 请求协商协议,不会因为端口相同就发生冲突。Traefik 也会转发 WebSocket 的 Upgrade 请求,通常无需额外启用 WebSocket 开关。

当前 WebUI 服务配置将 Traefik 后端端口设为 8080,Kestrel 的 ASPNETCORE_URLS 则是 https://+;http://+:8080。按此配置,Traefik 连接 8080 时应使用 HTTP;对外的 TLS 由 websecure 入口处理。配置中没有明确写出 Kestrel 的 HTTPS 端口,因此需要先确认 Kestrel 实际监听的端口和协议。现有信息还不足以确定间歇性错误的唯一根因。

多个 stack 共用代理网络

多个 stack 可以共用外部 overlay 网络 traefik-reverse-proxy。Traefik 和需要由它访问的服务都必须连接到这张网络,服务标签也应指向该网络。你已在 Traefik 上配置 --providers.swarm.network=traefik-reverse-proxy,WebUI 则通过 network2 加入了该网络。

Traefik v3 使用 Swarm provider 时,WebUI 的网络标签可以写为:

- "traefik.swarm.network=traefik-reverse-proxy"

目前配置使用的是 traefik.docker.network。由于这里使用 Swarm provider,建议改为对应的 traefik.swarm.network 标签,并确认实际部署的 service labels 中包含该标签。全局 provider 网络设置已经存在,因此服务级网络标签并非唯一的网络选择方式;明确指定仍有助于避免选错网络。

先核对 Kestrel 和 Traefik 的端口

ASPNETCORE_URLS=https://+;http://+:8080 没有为 HTTPS URL 明确指定端口。不要根据“443”或“5001”这样的假设来配置后端;应查看应用启动日志或容器监听状态,确认 Kestrel 实际监听的地址。

如果 Traefik 继续连接 8080,后端就应提供 HTTP。当前的 loadbalancer.server.port=8080 与这一配置意图一致。如果改为连接 Kestrel 的 HTTPS 监听端口,Traefik 服务还需要配置相应的后端 scheme,并确认后端证书校验设置符合预期。外部 TLS 证书配置和 Traefik 到容器之间使用 HTTP 还是 HTTPS,属于两段独立的连接。

同时检查 gRpcApiUri=https://svt-webui 实际使用的端口和协议,并确认其证书名称匹配。这个 gRPC 地址通过服务间网络连接,与浏览器经 Traefik 建立 WebSocket 连接的路径不同。即使 gRPC 请求使用同一个 Kestrel 监听端口,也不代表它会与 WebSocket 冲突。

用失败请求定位断点

对照 Traefik access log、Traefik DEBUG 日志和 WebUI 应用日志,记录一次失败请求的时间、路由、后端地址和状态码。重点查看 GET /_blazor?id=... 的 WebSocket 握手是否返回 101 Switching Protocols,以及请求是否到达预期的 WebUI 实例。

  • 如果请求没有进入 Traefik,检查 DNS、外部负载均衡器,以及客户端到 manager 节点的网络路径。
  • 如果 Traefik 收到请求,却没有将它转发到 WebUI,检查 router、service、middleware 和 Swarm 网络选择。
  • 如果请求到达 WebUI,但服务器日志显示 connection ID 不存在,检查请求是否由不同实例处理、应用是否重启,或连接是否在握手前关闭。

Traefik 的端口通过 mode: host 发布,服务约束也设为只运行在 manager 上。需要确认外部流量确实到达该 manager 节点。如果前面还有负载均衡器,也要检查它如何将流量转发到各节点。

检查副本数和连接亲和性

当前 WebUI 配置为 replicas: 1,所以 sticky sessions 通常不是首要排查方向。错误提示中关于多服务器需要会话亲和性的内容,是 SignalR 的通用提示,不能据此判断当前部署有多个 WebUI 实例。

如果实际运行的 WebUI 副本超过一个,或同一主机名背后还有其他服务实例,就需要确保 SignalR 的协商请求和后续连接落到兼容的服务器上,并根据部署方式配置会话亲和性。可以先检查 Swarm 中实际运行的 task 数量、Traefik service 的后端列表和容器重启记录。

排查 middleware 和应用配置

为缩小排查范围,可以暂时从该 router 移除 gzip@file,再用同一浏览器重复连接,观察失败率是否变化。如果移除后问题消失,再单独检查压缩 middleware 的定义,以及它对升级请求的处理方式。也可以在受控排查中单独隔离测试 default-security-headers@file,避免一次改动多个条件。

还要确认应用能正确处理反向代理传入的转发头,尤其是 HTTPS 场景下的原始协议信息,并检查 WebSocket Upgrade 请求是否带有 Connection: Upgrade 和 Upgrade: websocket。不要为了测试直接关闭证书校验,也不要长期放宽入口访问控制。

排查时按顺序记录失败时间和请求状态,确认 Traefik 实际选择的后端及其监听协议,再检查应用日志、实例数量、重启情况和 middleware。这样能帮助判断问题出在网络路径、后端端口、路由还是 SignalR 连接状态。

备注:内容仅供参考。