You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

本地Nginx+npm run dev下WebSocket正常,Docker Compose部署后连接失败

问题描述

我使用React前端、Node.js后端,通过Nginx实现服务部署与负载均衡。本地运行Nginx并执行npm run dev时整个架构运行正常,但通过Docker Compose部署为容器后,WebSocket无法建立连接。

控制台报错

websocket.js:43 WebSocket connection to 'ws://localhost/socket.io/?userId=6500d7f273ec607346da303c&EIO=4&transport=websocket' failed:

配置信息

docker-compose.yml

services:
  ws1:
    image: bark-mate-be
    environment:
      - APPID=1111
      - APPPORT=8081
    networks:
      - loadbalancing
    ports:
      - "8081:8080"
  ws2:
    image: bark-mate-be
    environment:
      - APPID=2222
      - APPPORT=8082
    ports:
      - "8082:8080"
    networks:
      - loadbalancing
  ws3:
    image: bark-mate-be
    environment:
      - APPID=3333
      - APPPORT=8083
    ports:
      - "8083:8080"
    networks:
      - loadbalancing
  barkmatefrontend:
    image: bark-mate-fe
    ports:
      - "3000:80"
    networks:
      - loadbalancing
    depends_on:
      - db
      - ws1
      - ws2
      - ws3
  db:
    volumes:
      - barkMate_db:/data/db
    image: mongo:latest
    ports:
      - "27017:27017"
volumes:
  barkMate_db:
networks:
  loadbalancing:

nginx.conf

map $http_upgrade $connection_upgrade {
        default upgrade;
        '' close;
    }


upstream loadbalancer {
    # ip_hash;
    hash $remote_addr consistent;
    server ws1:8081;
    server ws2:8082;
}


server {
        listen 80;
        listen [::]:80;
        server_name localhost;

        location /socket.io/ {
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header Host $host;
            proxy_pass http://loadbalancer;
            proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection "upgrade";
        }

        location /api/ {
            proxy_pass http://loadbalancer;
            proxy_buffering on;
        }

         location / {
            root   /usr/share/nginx/html;
            index  index.html index.htm;
            try_files $uri $uri/ /index.html;
        }

}

socketio.js

import { io } from "socket.io-client";
const user = JSON.parse(localStorage.getItem("user"));

export let socket = io("http://localhost:80", {
  path: "/socket.io/",
  auth: { token: "token" },
  query: { userId: user ? JSON.parse(localStorage.getItem("user"))._id : "" },
  autoConnect: false,
  transports: ["websocket"],
  withCredentials: true,
});

socket.onAnyOutgoing((e, m) => {
   console.log("any out going", e, m);
});

socket.on("connect", () => {
   console.log("socket has connected");
  socket.emit("message", {
    type: "test",
    content: {
      msg: "test message",
      userId: JSON.parse(localStorage.getItem("user"))._id,
    },
  });
});

socket.on("message", (msg) => {
   console.log("client recieved the message", msg);
});

socket.io.on("reconnect", (attempt) => {
   console.log("socket reconnection success");
});

Dockerfile(前端)

FROM node:current-alpine as build
WORKDIR /app
COPY package*.json /app/
RUN npm install
RUN npm ci --production
COPY . .
RUN npm run build

FROM nginx:stable
COPY --from=build /app/build/ /usr/share/nginx/html
COPY nginx/nginx.conf /etc/nginx/conf.d/default.conf

解决方案

核心问题分析

  1. Nginx未纳入容器网络:当前Docker Compose未包含Nginx服务,前端容器的Nginx仅负责静态文件,WebSocket与API转发逻辑未在容器环境生效;本地单独运行Nginx时能正常转发,但容器环境下前端代码请求localhost:80无对应服务监听。
  2. 后端端口映射混淆:Nginx配置的upstream指向后端容器的外部映射端口(8081/8082),但容器内部服务实际运行在8080端口,导致转发失败。
  3. WebSocket连接地址硬编码:前端代码固定连接http://localhost:80,容器部署后用户访问前端3000端口时,WebSocket请求会直接打向本地80端口,而非正确的Nginx转发入口。

具体修复步骤

1. 调整Docker Compose,加入Nginx服务

将Nginx作为单独容器纳入网络,统一处理静态文件、API与WebSocket转发:

services:
  ws1:
    image: bark-mate-be
    environment:
      - APPID=1111
    networks:
      - loadbalancing
  ws2:
    image: bark-mate-be
    environment:
      - APPID=2222
    networks:
      - loadbalancing
  ws3:
    image: bark-mate-be
    environment:
      - APPID=3333
    networks:
      - loadbalancing
  barkmatefrontend:
    image: bark-mate-fe
    networks:
      - loadbalancing
    depends_on:
      - db
      - ws1
      - ws2
      - ws3
  db:
    volumes:
      - barkMate_db:/data/db
    image: mongo:latest
    networks:
      - loadbalancing
  nginx:
    image: nginx:stable
    ports:
      - "80:80"
    volumes:
      - ./nginx.conf:/etc/nginx/conf.d/default.conf
    networks:
      - loadbalancing
    depends_on:
      - barkmatefrontend
      - ws1
      - ws2
      - ws3
volumes:
  barkMate_db:
networks:
  loadbalancing:

2. 修正Nginx配置

  • 调整upstream指向后端容器内部端口(8080),并补充ws3节点
  • 修改静态文件转发指向前端容器:
upstream loadbalancer {
    hash $remote_addr consistent;
    server ws1:8080;
    server ws2:8080;
    server ws3:8080;
}

server {
        listen 80;
        listen [::]:80;
        server_name localhost;

        location /socket.io/ {
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header Host $host;
            proxy_pass http://loadbalancer;
            proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection "upgrade";
        }

        location /api/ {
            proxy_pass http://loadbalancer;
            proxy_buffering on;
        }

         location / {
            proxy_pass http://barkmatefrontend;
            try_files $uri $uri/ /index.html;
        }
}

3. 修正前端Socket连接地址

使用相对路径替代硬编码地址,自动适配当前访问域名:

import { io } from "socket.io-client";
const user = JSON.parse(localStorage.getItem("user"));

export let socket = io("", {
  path: "/socket.io/",
  auth: { token: "token" },
  query: { userId: user ? user._id : "" },
  autoConnect: false,
  transports: ["websocket"],
  withCredentials: true,
});

验证步骤

  1. 重新构建前端与后端镜像(若代码有修改)
  2. 执行docker-compose up -d启动所有服务
  3. 访问http://localhost,检查WebSocket连接状态

内容的提问来源于stack exchange,提问作者Anthony T

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.05 11:35:20