Как вы докертизируете сервер WebSocket? - PullRequest
0 голосов
/ 09 января 2019

У меня проблемы с переносом сервера WebSocket в контейнер Docker.

Это код сервера, который записывает новое соединение с «подключен».

// server.go
func RootHandler(w http.ResponseWriter, r *http.Request) {
    upgrader := websocket.Upgrader{ // (Uses gorilla/websocket)
        ReadBufferSize:  4096,
        WriteBufferSize: 4096,
    }

    conn, err := upgrader.Upgrade(w, r, nil)
    if err != nil {
        panic(err)
    }

    if err = conn.WriteMessage(websocket.TextMessage, []byte("connected")); err != nil {
        panic(err)
    }
}

func main() {
    fmt.Println("server is running")

    // For graceful shutdown
    stop := make(chan os.Signal, 1)
    signal.Notify(stop, os.Interrupt)

    server := http.Server{Addr: "localhost:8000"}
    defer server.Close()

    http.HandleFunc("/", RootHandler)

    go func() {
        err := server.ListenAndServe()
        if err != nil && err != http.ErrServerClosed {
            panic(err)
        }
    }()

    <-stop
}

Вот клиент:

// client.go
func main() {
    connection, _, err := websocket.DefaultDialer.Dial("ws://localhost:8000", nil)
    if err != nil {
        panic(err)
    }

    _, b, err := connection.ReadMessage()
    if err != nil {
        panic(err)
    }
    fmt.Println(string(b)) // "connected"
}

Запуск server.go, затем client.go выводит «подключено», показывая, что код работает. Теперь я хочу поместить сервер в Docker-контейнер. Это dockerfile:

FROM golang:1.11.4-alpine3.8
COPY . $GOPATH/src/websocket-test
WORKDIR $GOPATH/src/websocket-test
RUN ["go", "build", "server.go"]
EXPOSE 8000
CMD ["./server"]

Я использую эти команды для сборки и запуска контейнера, выставляя 8000 / tcp.

docker build -t websocket-test .
docker run -p 8000:8000 websocket-test

Я могу подтвердить, что сервер работает, потому что он печатает «сервер работает». Если я запускаю client.go вне контейнера, он паникует:

panic: read tcp [::1]:60328->[::1]:8000: read: connection reset by peer

То, что я ожидаю, это тот же результат, что и раньше - печать «подключен» на стороне клиента. Сообщение об ошибке означает, что сервер разорвал соединение до рукопожатия. Я не понимаю число "60328". Насколько я знаю, WebSocket не меняет порты при обновлении, поэтому я должен быть в порядке, выставив всего 8000.

Я не знаю, что мне нужно изменить, чтобы иметь возможность подключаться через WebSocket к моему серверу.

1 Ответ

0 голосов
/ 09 января 2019

Когда вы указываете имя хоста или IP-адрес для прослушивания (в данном случае localhost, который разрешается в 127.0.0.1), тогда ваш сервер будет прослушивать только этот IP-адрес.

Прослушивание localhost не является проблемой, когда вы находитесь вне контейнера Docker. Если ваш сервер прослушивает только 127.0.0.1:8000, то ваш клиент может легко подключиться к нему, так как соединение также установлено с 127.0.0.1.

Когда вы запускаете свой сервер в контейнере Docker, он будет прослушивать только 127.0.0.1:8000, как и раньше. 127.0.0.1 является локальным адресом обратной связи, и он не доступен вне контейнера.

Когда вы запускаете докер-контейнер с помощью -p 8000:8000, он будет перенаправлять трафик на 127.0.0.1:8000 на IP-адрес контейнера, который в моем случае 172.17.0.2.

Контейнер получает IP-адреса в сетевом интерфейсе docker0 (который можно увидеть с помощью команды ip addr ls)

Итак, когда ваш трафик перенаправляется в контейнер на 172.17.0.2:8000, там ничего не прослушивается, и попытка подключения завершается неудачей.

Исправление:

Проблема с адресом прослушивания:

server := http.Server{Addr: "localhost:8000"}

Чтобы исправить проблему, измените ее на

server := http.Server{Addr: ":8000"}

Это заставит ваш сервер прослушивать все IP-адреса своего контейнера.

Дополнительная информация:

Когда вы открываете порты в контейнере Docker, Docker создает правила iptables для фактической пересылки. Видеть это. Вы можете просмотреть эти правила с помощью:

iptables -n -L 
iptables -t nat -n -L
...