Проблемы с сокетированием контейнера Flask Docker

#docker #flask #docker-compose #socket.io #gunicorn

#docker #flask #docker-compose #socket.io #gunicorn

Вопрос:

У меня есть приложение Flask, которое использует SocketIO для извлечения данных из Postgres live.

Приложение работает нормально, когда я запускаю его локально.

Проблема возникает, когда я использую docker-compose для размещения своего приложения Flask. Мой JS-клиент и сервер flask размещены в одном приложении и в одном контейнере.

Мой socketio в JS выглядит так:

 var socket = io().connect(window.location.protocol   '//'   document.domain   ':'   location.port);
 

Dockerfile:

 # Using python 3.7 in Alpine
FROM python:3.6.5-stretch

# Set the working directory to /app
WORKDIR /app

# Copy the current directory contents into the container at /app
ADD . /app

RUN apt-get update -y amp;amp; apt-get upgrade -y

# Install the dependencies from requirements
RUN pip install -r requirements.txt

# Tell the port number the container should expose
EXPOSE 8083

# Run the command
ENTRYPOINT ["./entry.sh"]
 

entry.sh:

 #!/bin/sh
gunicorn -k geventwebsocket.gunicorn.workers.GeventWebSocketWorker -b :8083 -w 5 run:app
 

Мой docker-compose как таковой:

 version: "3.8"

services:
  fortweet:
    container_name: fortweet
    build: ./
    env_file:
      - secret.env
    networks:
      - plutusnet
    ports:
      - 8083:8083
    restart: always

networks:
  plutusnet:
    name: plutus_network
    driver: bridge
 

Я также пытался использовать var socket = io.connect('http://public_ip_of_website:8083') , но мое сокет-соединение по-прежнему не работает.

Обычно это должно работать так: когда я запускаю это локально и нажимаю определенную кнопку, она выполняет эту функцию в моем JS:

 $("#tweets-live-start").click(function(){
    if (is_streaming == true){
      alert("A stream is already running")
    }else{
      $.ajax({
        type: "POST",
        url : "/admin/startstream",
        data: {url : "print "hello""},
        contentType: 'application/json;charset=UTF-8'
      });
    }
  });
 

Когда мой сервер получает привет, он запускает потоковую передачу твитов и отправляет их через сокет. Затем мой сокет фиксирует их как таковые:

 // Listens for tweets
socket.on('stream-results', function(results){

  // Insert tweets in divs
  $('#live-tweet-container').prepend(`
  <div class="row justify-content-md-center mt-3">
    <div class="col-md-2">
        <img width="56px" height="56px"  src="${results.profile_pic !== "" ? results.profile_pic : "/static/icons/profile-pic.png"}" class="mx-auto d-block rounded"  alt="">
    </div>
    <div class="col-md-8 my-auto">
      <div><b>${results.author}</b></div>
      <div>${results.message}</div>
    </div>
  </div>
  `);
});
 

Но когда я запускаю его в docker, ничего не происходит.

Когда я проверяю консоль JS моего браузера, кажется, что она опрашивает с неправильным запросом, и я не знаю почему:

 index.js:83 GET http://th3pl4gu3.com:8083/socket.io/?EIO=3amp;transport=pollingamp;t=NPYYrxr 400 (BAD REQUEST)
 

Вот мой докер ps для получения дополнительной информации:

 46446efeb472   mervin16/fortweet:dev   "/bin/bash entry.sh"  About a minute ago   Up About a minute   0.0.0.0:8083->8083/tcp   fortweet

12b2bff36af0   postgres    "docker-entrypoint.s…"   2 hours ago          Up 2 hours          0.0.0.0:5432->5432/tcp   plutus
 

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

Я проверил журналы контейнера docker, и это дает следующее:

 fortweet    | [2020-12-26 15:18:55  0000] [8] [INFO] Starting gunicorn 20.0.4
fortweet    | [2020-12-26 15:18:55  0000] [8] [INFO] Listening at: http://0.0.0.0:8083 (8)
fortweet    | [2020-12-26 15:18:55  0000] [8] [INFO] Using worker: geventwebsocket.gunicorn.workers.GeventWebSocketWorker
fortweet    | [2020-12-26 15:18:55  0000] [11] [INFO] Booting worker with pid: 11
fortweet    | [2020-12-26 15:18:55  0000] [12] [INFO] Booting worker with pid: 12
fortweet    | [2020-12-26 15:18:55  0000] [13] [INFO] Booting worker with pid: 13
fortweet    | [2020-12-26 15:18:55  0000] [14] [INFO] Booting worker with pid: 14
fortweet    | [2020-12-26 15:18:55  0000] [15] [INFO] Booting worker with pid: 15
fortweet    | The client is using an unsupported version of the Socket.IO or Engine.IO protocols (further occurrences of this error will be logged with level INFO)
fortweet    | The client is using an unsupported version of the Socket.IO or Engine.IO protocols (further occurrences of this error will be logged with level INFO)
fortweet    | 172.16.0.1 - - [2020-12-26 15:19:57] "POST /admin/startstream HTTP/1.1" 204 170 0.023672
fortweet    | The client is using an unsupported version of the Socket.IO or Engine.IO protocols (further occurrences of this error will be logged with level INFO)
fortweet    | The client is using an unsupported version of the Socket.IO or Engine.IO protocols (further occurrences of this error will be logged with level INFO)
fortweet    | 172.16.0.1 - - [2020-12-26 15:20:20] "GET /socket.io/?EIO=3amp;transport=pollingamp;t=1608996021267-7 HTTP/1.1" 400 195 0.001418
fortweet    | 172.16.0.1 - - [2020-12-26 15:20:20] "GET /socket.io/?EIO=3amp;transport=pollingamp;t=1608996021267-7 HTTP/1.1" 400 195 0.001418
fortweet    | 172.16.0.1 - - [2020-12-26 15:20:21] "GET /socket.io/?EIO=3amp;transport=pollingamp;t=1608996021395-8 HTTP/1.1" 400 195 0.001625
fortweet    | 172.16.0.1 - - [2020-12-26 15:20:21] "GET /socket.io/?EIO=3amp;transport=pollingamp;t=1608996021395-8 HTTP/1.1" 400 195 0.001625
fortweet    | 172.16.0.1 - - [2020-12-26 15:20:26] "GET /socket.io/?EIO=3amp;transport=pollingamp;t=1608996026417-9 HTTP/1.1" 400 195 0.001367
fortweet    | 172.16.0.1 - - [2020-12-26 15:20:26] "GET /socket.io/?EIO=3amp;transport=pollingamp;t=1608996026417-9 HTTP/1.1" 400 195 0.001367
fortweet    | 172.16.0.1 - - [2020-12-26 15:20:26] "GET /socket.io/?EIO=3amp;transport=pollingamp;t=1608996027270-8 HTTP/1.1" 400 195 0.003811
fortweet    | 172.16.0.1 - - [2020-12-26 15:20:26] "GET /socket.io/?EIO=3amp;transport=pollingamp;t=1608996027270-8 HTTP/1.1" 400 195 0.003811
fortweet    | 172.16.0.1 - - [2020-12-26 15:20:34] "POST /admin/startstream HTTP/1.1" 204 170 0.015831
fortweet    | 172.16.0.1 - - [2020-12-26 15:20:36] "GET /socket.io/?EIO=3amp;transport=pollingamp;t=1608996036486-11 HTTP/1.1" 400 195 0.001096
 

К вашему сведению, контейнер plutus — это просто моя база данных Postgres, к которой подключается мое веб-приложение.

Кто-нибудь может мне помочь?

Комментарии:

1. Кто -нибудь , пожалуйста, может мне помочь?

2. Какую ошибку вы получаете при попытке подключения? http://fortweet:8083 должно работать для взаимодействия контейнера с контейнером и http://public_ip_of_website:8083 для взаимодействия снаружи с контейнером. Можете ли вы предоставить нам обратную трассировку или сообщение об ошибке?

3. Когда я запускаю это локально и нажимаю определенную кнопку, оно отправляет данные на мой сервер, а затем мой сервер отвечает в ответ. Но в контейнере docker ничего не происходит. Я обновил свой вопрос относительно того, как должен работать сокет. Пожалуйста, помогите мне

4. Можете ли вы открыть свою консоль devtool, найти запрос на отказ и добавить его в свой вопрос? Или журнал на стороне сервера, если он у вас есть. (хром: developers.google.com/web/tools/chrome-devtools/open , firefox: developer.mozilla.org/en-US/docs/Tools/Network_Monitor )

5. тоже просто придираюсь, но почему вы используете #!/bin/sh shebang для скрипта, который вы явно вызываете для выполнения в bash?

Ответ №1:

TL; DR — вы используете несовместимые версии SocketIO между клиентом и сервером. Проверьте приведенную ниже таблицу и убедитесь, что вы используете соответствующие версии python и javascript.

Как говорится в журналах контейнеров, вы используете несовместимые версии SocketIO между клиентом и сервером. Протоколы SocketIO и Engineerio претерпели несколько изменений, и не все они обратно совместимы, поэтому вы должны убедиться, что используете соответствующие реализации протокола на стороне клиента и сервера, которые могут взаимодействовать друг с другом.

Я подозреваю, что причина, по которой он работает, когда вы запускаете свое приложение локально, но не в вашем контейнере Docker, заключается в том, что зависимости в вашем контейнере requirements.txt ссылаются на более старые, несовместимые версии реализации python. Исходя из того, что вы описали, похоже, что локально установленная реализация SocketIO на python является более новой версией и, следовательно, совместима с более новой версией JS на стороне клиента и не имеет проблем с подключением (или это может быть наоборот …. более старый клиент, более новый сервер).

Если вы используете собственный сокет javascript.Для ввода-вывода версии 3.0 на стороне клиента (которая реализует протокол SocketIO v5 и протокол Engineerio v4) вам необходимо убедиться, что вы используете соответствующие версии реализаций Python на стороне сервера. Вы не указали, но я предполагаю, что вы используете Flask-SocketIO , который сам по себе является оболочкой python-socketio , фактическую реализацию протокола SocketIO на Python.

Проверьте, какую версию клиента SocketIO вы используете в своем Javascript. Затем проверьте requirements.txt и убедитесь, что версия python-socketio совместима с приведенной ниже таблицей (источник):

Версия JS SocketIO Протокол SocketIO Протокол EngineIO python-socketio
0.9.x 1, 2 1, 2 Не поддерживается
1.x и 2.x 3, 4 3 4.x
3.x 5 4 5.x

Скорее всего, вы используете версию JS 3.x с версией 4.x на стороне python (которые несовместимы). Убедитесь, что вы используете Flask-SocketIO версии 5.x, python-socketio python-engineio 5.x и 4.x, и что ваш JS-клиент 3.x. Это должно решить вашу проблему.

Если это работает правильно в вашей локальной среде, вы можете просто запустить pip freeze > requirements.txt и использовать это для своей сборки docker. Этот requirements.txt файл будет иметь правильные зависимости, поскольку очевидно, что он работает при локальном запуске.

В качестве альтернативы, если вы хотите убедиться, что у вас есть последняя версия всего, вы можете запустить pip install --upgrade flask-socketio . При этом будет установлена последняя версия Flask-SocketIO и самые последние зависимости (включая python-socketio и python-engineerio). Затем просто восстановите свой requirements.txt файл и используйте его в своей сборке docker.

Комментарии:

1. Вы были правы! Это именно то, что вы сказали! Сокеты работают, но неэффективно. Некоторые сообщения перехватываются, некоторые нет. В любом случае я могу устранить это лучше теперь, когда основная проблема решена. Спасибо, приятель

Ответ №2:

Это 400 (BAD REQUEST) указывает на то, что существует связь между вашим браузером JS и приложением Flask.

Я подозреваю проблему между приложением Flask и Postgres.

Ваш Postgres должен находиться в той же сети, что и ваш сервер приложений (служба, которую вы называете «fortweet» в вашем docker-compose. Кроме того, вы должны указать ему имя хоста, чтобы сервер приложений мог разрешить его внутри.

 version: "3.8"

services:
  postgres:                               #    <=== same name here 
    image: postgres/postgres:11
    networks:
      - plutusnet                      #  <== same network

  fortweet:
    container_name: fortweet
    build: ./
    env_file:
      - secret.env
    networks:
      - plutusnet                        #  <=== same network
    links:
      - db:postgres                      #    <== than here
    ports:
      - 8083:8083
    restart: always

networks:
  plutusnet:
    name: plutus_network
    driver: bridge`

 

Затем приложение должно быть настроено на использование «postgres: 5432» для подключения к базе данных.

Попробуйте и расскажите нам.

Комментарии:

1. Подключение к базе данных работает, потому что при запуске моего приложения оно создает гостевого пользователя по умолчанию для базы данных. И мои твиты передаются через соединение в прямом эфире, не проходя через базу данных. Я думаю, вы были правы, когда сказали, что связь между браузером и JS не работает, но я не знаю почему. Это нормально работает локально, но не в Docker.