Ошибка подключения WebSocket в React и Docker: WebSocket connection to 'ws://backend:7000/api/getData' failed

При запуске моего приложения React в Docker с использованием Nginx в качестве обратного прокси-сервера возникает ошибка при подключении к WebSocket. Получаю сообщение об ошибке:

WebSocket connection to 'ws://backend:7000/api/getData' failed: WebSocket error

Однако локально все работает идеально. Проблема возникает только в Docker.

Backend (Go) настроен для обработки WebSocket запросов, а также работает с Redis для получения данных.

При попытке подключения клиентского кода React к WebSocket серверу, который находится в контейнере Docker, возникает ошибка.

Как исправить эту проблему?

вот мой docker-compose:

version: '3'

services:
  frontend:
    build:
      context: ./front/app
      dockerfile: Dockerfile
    ports:
      - "3000:3000"
    environment:
      NODE_ENV: production
    restart: always

  backend:
    build:
      context: ./backend
      dockerfile: Dockerfile
    ports:
      - "7000:7000"
    restart: always

  bot:
    build:
      context: ./bot_crypto/crypto
      dockerfile: Dockerfile
    restart: always

  redis:
    image: redis
    ports:
      - "6379:6379"
    restart: always

  nginx:
    image: nginx
    ports:
      - "80:80"
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf:ro
    depends_on:
      - frontend
      - backend
      - bot
    restart: always

nginx:

worker_processes 1;

events {
    worker_connections 1024;
}

http {
    server {
        listen 80;
        server_name localhost;

        location / {
            proxy_pass http://frontend:3000;
            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_set_header X-Forwarded-Proto $scheme;
        }

        location /api {
            proxy_pass http://backend:7000;
            proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection "upgrade";
            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_set_header X-Forwarded-Proto $scheme;
        }

        
    }
}

frontend:

import { name_table } from "data/data";
import { useEffect, useState } from "react";
import { DataGetProps } from "interfaces/data.props";
import Filter from "components/filter/filter";
import NProgress from "nprogress";
const Content = () => {
    const [data, setData] = useState<DataGetProps[]>([]);
    const [socket, setSocket] = useState<WebSocket | null>(null);
    const [time, setTime] = useState<string>("");
    const [filterData, setFilterData] = useState<DataGetProps[]>([])
    const [stateFilter, setStateFilter] = useState<boolean>(false)
    const [stateFilterNum, setFilterNum]=useState<number>(0)



    useEffect(() => {

        const newSocket = new WebSocket("ws://backend:7000/api/getData");
        setSocket(newSocket);


        return () => {
            newSocket.close();
        };
    }, []);

    useEffect(() => {
        if (!socket) return;



        socket.onmessage = (event) => {
            NProgress.start();

            const message = JSON.parse(event.data);
            setData(message.data);
            setTime(new Date().toLocaleString());
            setStateFilter(true)
            NProgress.done();

            console.log(message);
        };


        socket.onerror = (error) => {
            console.error("WebSocket error:", error);
        };


        return () => {
            socket.close();
        };
    }, [socket]);

    return (
        <div className="min-h-screen flex items-center justify-center">
            <div className="max-w-max  grid grid-cols-1 gap-4 px-4">
                <div className={"text-center"} > Обновление: {time}</div>
                <Filter data={data}   setFilterData={setFilterData} stateFilter={stateFilter} setStateFilter={setStateFilter} stateFilterNum={stateFilterNum} setFilterNum={setFilterNum}/>

                <div className={"grid grid-cols-6 gap-4"}>
                    {name_table && name_table.map((item) => (
                        <div key={item.id} className="p-4 bg-gray-200 text-center rounded">
                            <div className={"text-black font-bold"}>{item.name}</div>
                        </div>
                    ))}
                </div>
                {filterData && filterData.map((item) => (
                    <div key={item.id} className={"grid grid-cols-6 gap-4"}>
                        <div className="p-4 bg-gray-200 text-center rounded">{item.symbol}</div>
                        <div className="p-4 bg-gray-200 text-center rounded">{item.exchange1}</div>
                        <div className="p-4 bg-gray-200 text-center rounded">{item.price1}</div>
                        <div className="p-4 bg-gray-200 text-center rounded">{item.exchange2}</div>
                        <div className="p-4 bg-gray-200 text-center rounded">{item.price2}</div>
                        <div className="p-4 bg-gray-200 text-center rounded">{item.spread}</div>
                    </div>
                ))}
            </div>
        </div>
    );
};

export default Content;

Backend:

package main

import (
    "encoding/json"
    "fmt"
    "github.com/codeza-org/backend_crypto/connect"
    "github.com/go-redis/redis/v8"
    "github.com/gofiber/fiber/v2"
    "github.com/gofiber/fiber/v2/middleware/cors"
    "github.com/gofiber/websocket/v2"
    "time"
)

type Comparison struct {
    ID        string  `json:"id"`
    Symbol    string  `json:"symbol"`
    Exchange1 string  `json:"exchange1"`
    Price1    float64 `json:"price1"`
    Exchange2 string  `json:"exchange2"`
    Price2    float64 `json:"price2"`
    Spread    float64 `json:"spread"`
}

var client *redis.Client

func main() {
    
    var err error
    client, err = connect.Connect()
    if err != nil {
        panic(err)
    }
    defer client.Close()

    app := fiber.New()
    app.Use(cors.New(cors.Config{
        AllowOrigins: "http://frontend:3000",
    }))

    app.Use("api/getData", func(c *fiber.Ctx) error {
        if websocket.IsWebSocketUpgrade(c) {
            return c.Next()
        }
        return fiber.ErrUpgradeRequired
    }, websocket.New(getData))

    app.Post("api/data", data)

    app.Listen(":7000")
}

func data(c *fiber.Ctx) error {
    var comparisons []Comparison

    if err := c.BodyParser(&comparisons); err != nil {
        return err
    }

    
    jsonData, err := json.Marshal(comparisons)
    if err != nil {
        return err
    }

    
    err = client.Set(client.Context(), "data", jsonData, 0).Err()
    if err != nil {
        return err
    }

    fmt.Println("Данные успешно записаны в Redis")

    return c.JSON(fiber.Map{
        "message": "Данные успешно получены и обработаны",
    })
}

func getData(c *websocket.Conn) {
    for {
        fmt.Println(1)
        
        if c == nil {
            fmt.Println("Соединение WebSocket закрыто")
            return
        }

    
        val, err := client.Get(client.Context(), "data").Result()
        if err != nil {
            fmt.Println("Ошибка при получении данных из Redis:", err)
            return
        }

        
        var comparisons []Comparison
        err = json.Unmarshal([]byte(val), &comparisons)
        if err != nil {
            fmt.Println("Ошибка при разборе данных JSON:", err)
            return
        }

        
        data, err := json.Marshal(fiber.Map{"data": comparisons})
        if err != nil {
            fmt.Println("Ошибка при преобразовании данных в JSON:", err)
            return
        }

        
        err = c.WriteMessage(websocket.TextMessage, data)
        if err != nil {
            fmt.Println("Ошибка при отправке данных через WebSocket:", err)
            return
        }

        из Redis
        time.Sleep(5 * time.Second)
    }
}

Ответы (0 шт):