远程服务器Docker环境下Socket.IO的Nginx/Traefik配置问题
Socket.IO连接失败排查(React+NestJS+Docker+Traefik)
本地非Docker环境下,React客户端(基于Vite)与NestJS Gateway的Socket.IO连接正常,但部署到预生产服务器后连接失败,确认问题出在Nginx或Traefik配置环节。
1. 后端NestJS配置
- Dockerfile已暴露Socket网关端口8889:
EXPOSE 8889
- WebSocket网关核心配置(
ws.gateway.ts):
@WebSocketGateway(8889, { cors: { origin: "*" }, path: "/socket", namespace: "/api/frontend" }) export class EventsGateway implements OnGatewayInit, OnGatewayConnection, OnGatewayDisconnect { @WebSocketServer() server: Server; afterInit(server: Server) { console.log("Websocket initialized."); } handleDisconnect(client: Socket) { console.log(`Client disconnected: ${client.id}`); } handleConnection(client: Socket, ...args: any[]) { console.log(`Client connected: ${client.id}`); } }
该网关类已接入NestJS模块系统。
2. 前端React/Vite配置
vite.config.mjs代理设置:
export default defineConfig({ ... server: { host: "127.0.0.1", port: 3000, proxy: { "/api": { target: "http://127.0.0.1:8888/", // 后端API代理地址 changeOrigin: true, secure: false, ws: true, rewrite: path => path.replace(/^\/api/, ""), }, "/socket": { target: "http://127.0.0.1:8889/", // Socket连接代理地址 changeOrigin: true, secure: false, ws: true, // rewrite: path => path.replace(/^\/socket/, ""), }, }, }, ... })
- Socket连接代码(
socket.tsx):
const WSClient: React.FC<WSClientProps> = props => { const uri = `${import.meta.env.VITE_APP_WS}/api/frontend`; // 实际连接地址:https://stage.mycompany.io/api/frontend useEffect(() => { console.log("Attempting to open socket to:", uri); const options: Partial<ManagerOptions & SocketOptions> = { reconnection: true, reconnectionDelay: 100, reconnectionAttempts: 3, path: "/socket", }; const newSocket = io(uri, options); setSocket(newSocket); newSocket.on("connect", () => { console.log("Socket connected to backend server. Socket id:", newSocket.id, "on uri:", uri); }); newSocket.on("disconnect", () => { console.log(`Socket id ${newSocket.id} has disconnected.`); }); newSocket.io.on("reconnect_attempt", attempt => { console.log(`Socket reconnect_attempt ${attempt}.`); if (attempt > 2) { console.log("Failed to disconnect the socket."); } }); }, []); ... }
3. Nginx配置(default.conf)
server { listen 80; server_name localhost; root /usr/share/nginx/html; index index.html index.htm; resolver 127.0.0.11 ipv6=off valid=10s; access_log /etc/nginx/conf.d/access.log; error_log /etc/nginx/conf.d/errors.log debug; location @index { root /usr/share/nginx/html; add_header Cache-Control 'private, no-cache, no-store, must-revalidate'; add_header Expires 'Sat, 01 Jan 2000 00:00:00 GMT'; add_header Pragma no-cache; try_files $uri $uri/ /index.html; } ##### SETUP FOR SOCKET.IO ###### location /socket { 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_pass http://127.0.0.1:8889/; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; } #### END SETUP FOR SOCKET.IO ###### location / { try_files $uri @index; } error_page 500 502 503 504 /50x.html; location = /50x.html { root /usr/share/nginx/html; } }
4. Docker相关配置
后端Dockerfile
FROM node:18.19-alpine3.18 as builder ARG NPM_TOKEN ARG PACKAGE_VERSION RUN echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" > ~/.npmrc RUN for i in $(seq 1 3); \ do [ $i -gt 1 ] && sleep 5; \ npm cache clean --force; \ npm add @mycompany/mycompany-backend@$PACKAGE_VERSION --global && s=0 && break || s=$?; \ done; (exit $s) FROM node:18.19-alpine3.18 ARG PACKAGE_VERSION ARG ENVIRONMENT COPY --from=builder /usr/local/lib/node_modules/@mycompany/mycompany-backend /app/ WORKDIR /app/ EXPOSE 8889 ENTRYPOINT ["npm"] CMD ["start"]
前端Dockerfile
FROM node:18.19-alpine3.18 as builder ARG NPM_TOKEN ARG PACKAGE_VERSION RUN echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" > ~/.npmrc RUN for i in $(seq 1 3); \ do [ $i -gt 1 ] && sleep 5; \ npm cache clean --force; \ npm add @mycompany/mycompany-frontend@$PACKAGE_VERSION --global && s=0 && break || s=$?; \ done; (exit $s) FROM nginx:1.25.3-alpine RUN rm -f /etc/nginx/conf.d/default.conf RUN rm -f /etc/nginx/default.conf COPY /conf/default.conf /etc/nginx/conf.d/default.conf COPY --from=builder /usr/local/lib/node_modules/@mycompany/mycompany-frontend/dist/ /usr/share/nginx/html/
Docker Compose(base.yml)
version: '3.4' networks: traefik-net: app-net: volumes: certs: services: proxy: image: traefik:1.7.5 deploy: replicas: 1 placement: constraints: - node.role == manager environment: CF_API_EMAIL: "${CLOUDFLARE_EMAIL}" CF_API_KEY: "${CLOUDFLARE_API_KEY}" command: [ "--loglevel=ERROR", "--web.address=:8080", "--retry", "--docker.swarmmode=true", "--docker.watch=true", "--docker.endpoint=unix:///var/run/docker.sock", "--defaultentrypoints=http,https", "--entryPoints=Name:http Address::80 Redirect.EntryPoint:https", "--entryPoints=Name:https Address::443 TLS", "--acme.dnschallenge.provider=cloudflare", "--acme.dnschallenge.delaybeforecheck=30", "--acme.acmelogging=true", "--acme.email=dave@mycompany.com", "--acme.entrypoint=https", "--acme.onhostrule=true", "--acme.storage=/certs/acme.json" ] volumes: - /var/run/docker.sock:/var/run/docker.sock - certs:/certs/ networks: - traefik-net ports: - "80:80" - "443:443" - "8080:8080" - "9229:9229" - "8889:8889" server: image: mycompany/mycompany-frontend:$MYCOMPANY_FRONTEND deploy: replicas: 1 update_config: delay: 20s failure_action: rollback restart_policy: condition: any labels: traefik.backend: 'server' traefik.docker.network: 'mycompany_traefik-net' traefik.enable: 'true' traefik.port: '80' networks: - traefik-net api: image: mycompany/mycompany-backend:$MYCOMPANY_BACKEND deploy: replicas: 1 update_config: delay: 20s failure_action: rollback restart_policy: condition: any labels: traefik.backend: 'api' traefik.docker.network: 'mycompany_traefik-net' traefik.enable: 'true' traefik.port: '8888' networks: - traefik-net - app-net dns: - 1.1.1.1 - 8.8.8.8 - 8.8.4.4
问题现状及求助
客户端React页面及API通信均正常,但Socket.IO连接失败,控制台显示尝试连接https://stage.mycompany.io/api/frontend。怀疑Nginx的proxy_pass配置错误(后端在独立Docker容器,localhost无法访问),尝试多种配置无效。目前推测Traefik仅配置了后端8888端口,未处理Socket的8889端口,需要正确配置Traefik以支持Socket.IO连接。
内容的提问来源于stack exchange,提问作者Mystify
相关产品推荐
相关产品推荐

