#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.