在远程服务器上使用docker、nestjs gateway、react、vite时为socket.io配置NGINX/traefik

问题描述 投票:0回答:1

我正在尝试弄清楚如何获取 nginx 配置设置,以允许 socket.io 从我的 React 客户端连接到使用 NestJS 网关的节点后端。 客户端和后端都在临时/生产服务器上自己的 docker 容器内运行。 当在我的本地计算机上测试东西时,使用 vite 运行客户端和节点运行后端(不在 docker 中运行)我可以毫无问题地连接两者。所以我知道我已经配置了后端和前端并且工作正常。 当我部署到临时服务器时,我无法使 socket.io 连接正常工作。我 99% 确定这与我的 nginx 配置有关。

这是我的设置:

  1. 在我的后端
    docker
    文件中,我暴露了端口8889(这是我的套接字网关端口)
EXPOSE 8889
  1. 在我的后端中,使用NestJS,我创建了一个
    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 模块系统。

  1. 在我的客户端应用程序中,这是一个使用 vite 在本地提供服务的 React 应用程序。我知道 vite 在临时服务器上运行时不会对客户端产生任何影响,因为 nginx 正在为客户端提供服务并记下 vite,但我在这里指出以防万一。我有以下文件和代码:

vite.config.mjs

export default defineConfig({
   ...
   server: {
    host: "127.0.0.1",
    port: 3000,
    proxy: {
      "/api": {
        target: "http://127.0.0.1:8888/",   // <-- this is for the backend api's.
        changeOrigin: true,
        secure: false,
        ws: true,
        rewrite: path => path.replace(/^\/api/, ""),
      },
      "/socket": {
        target: "http://127.0.0.1:8889/",   // <-- This is for the socket connections.
        changeOrigin: true,
        secure: false,
        ws: true,
        // rewrite: path => path.replace(/^\/socket/, ""),
      },
    },
  },
  ...
})

这是我的

socket.tsx
文件的一部分,该文件已添加到我的 app.tsx 文件中,我尝试在其中进行测试套接字连接:

const WSClient: React.FC<WSClientProps> = props => {
   const uri = `${import.meta.env.VITE_APP_WS}/api/frontend`; // <-- this translates to 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.");
        }
      });
   }, []);

   ...
}
  1. 这是我的 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;
    }

}

客户端的 React 部分以及与后端 api 的通信与以前一样完美。为了访问任何 api,我需要对 https://stage.mycompany.io/api/https://stage.mycompany.io/api/graphql/ 执行 axios 或 fetch 调用我的疑问。一切都按其应有的方式进行。

在我的控制台中,我看到套接字正在尝试连接到网关命名空间中定义的https://stage.mycompany.io/api/frontend。老实说,我不确定故障在哪里,我在 nginx 配置文件中尝试了很多不同的东西,但我似乎无法弄清楚。

认为这可能是proxy_pass,因为后端位于另一个docker容器中,因此使用本地主机或家庭IP是行不通的。我尝试过使用服务器 url 和其他一些东西但无济于事。

这几天我一直在用头撞键盘,试图解决这个问题。任何帮助将不胜感激。

更新: 这是我根据要求提供的 docker 文件

后端docker文件:

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/


#RUN --name some-redis -d redis

EXPOSE 8889

ENTRYPOINT ["npm"]

CMD ["start"]

前端 Docker 文件:

FROM node:18.19-alpine3.18 as builder
#WORKDIR '/builddir'
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/

base.yml是我的docker compose文件,当我部署到阶段时运行。

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",
      "[email protected]",
      "--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

我开始认为问题可能出在 base.yml 的 traefik 配置部分。我看到它定义了后端端口 8888,但没有为套接字连接定义 8889。我不确定如何配置它。

docker nginx socket.io nestjs traefik
1个回答
0
投票

假设容器之间相互链接并连接,并且您可以访问两者。

根据您的配置:

proxy_pass http://127.0.0.1:8889/;
您正在尝试连接到Nginx容器的
localhost
,这里您应该使用另一个容器的IP或其别名(哈希)。

© www.soinside.com 2019 - 2024. All rights reserved.