Подзародник PyZMQ Dockerized pub не будет получать сообщения - PullRequest
0 голосов
/ 16 декабря 2018

Я хочу построить модульную систему с модулями, связывающимися через ZeroMQ.Чтобы повысить удобство использования, я хочу Dockerize (некоторые) из этих модулей, чтобы пользователям не приходилось настраивать среду.Тем не менее, я не могу заставить докерализованного издателя получать сообщения, полученные не докеризированным подписчиком.

Система

  • Ubuntu 18.04
  • Python 3.7
  • libzmq версия 4.2.5
  • версия pyzmq 17.1.2
  • версия Docker 18.09.0, сборка 4d60db4

минимальный тестовый пример

zmq_sub.py

# CC0

import zmq


def main():
    # ZMQ connection
    url = "tcp://127.0.0.1:5550"
    ctx = zmq.Context()
    socket = ctx.socket(zmq.SUB)
    socket.bind(url)  # subscriber creates ZeroMQ socket
    socket.setsockopt(zmq.SUBSCRIBE, ''.encode('ascii'))  # any topic
    print("Sub bound to: {}\nWaiting for data...".format(url))

    while True:
        # wait for publisher data
        topic, msg = socket.recv_multipart()
        print("On topic {}, received data: {}".format(topic, msg))


if __name__ == "__main__":
    main()

zmq_pub.py

# CC0

import zmq
import time


def main():
    # ZMQ connection
    url = "tcp://127.0.0.1:5550"
    ctx = zmq.Context()
    socket = ctx.socket(zmq.PUB)
    socket.connect(url)  # publisher connects to subscriber
    print("Pub connected to: {}\nSending data...".format(url))

    i = 0

    while True:
        topic = 'foo'.encode('ascii')
        msg = 'test {}'.format(i).encode('ascii')
        # publish data
        socket.send_multipart([topic, msg])  # 'test'.format(i)
        print("On topic {}, send data: {}".format(topic, msg))
        time.sleep(.5)

        i += 1


if __name__ == "__main__":
    main()

Когда я открываю 2 терминала и запускаю:

  • python zmq_sub.py
  • python zmq_pub.py

Абонент получает без ошибок данные (On topic b'foo', received data: b'test 1')

Dockerfile

Я создал следующее Dockerfile:

FROM python:3.7.1-slim

MAINTAINER foo bar <foo@spam.eggs>

RUN apt-get update && \
  apt-get install -y --no-install-recommends \
  gcc

WORKDIR /app
COPY requirements.txt /app
RUN pip install -r requirements.txt

COPY zmq_pub.py /app/zmq_pub.py

EXPOSE 5550

CMD ["python", "zmq_pub.py"]

и затем я успешно собираю Dockerized издателя с помощью команды: sudo docker build . -t foo/bar

Попытки

Попытка 1

Теперь у меня есть докерКонтейнер с издателем, я пытаюсь, чтобы мой не докеризованный подписчик получил данные.Я запускаю следующие 2 команды:

  1. python zmq_sub.py
  2. sudo docker run -it foo/bar

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

Попытка 2

С мыслью, что мне нужно сопоставить внутренний порт моего докеризированного издателя с портом моей машины, я запускаю следующие 2 команды:

  1. python zmq_sub.py
  2. sudo docker run -p 5550:5550 -it foo/bar

Однако тогда я получаю следующую ошибку: docker: Error response from daemon: driver failed programming external connectivity on endpoint objective_shaw (09b5226d89a815ce5d29842df775836766471aba90b95f2e593cf5ceae0cf174): Error starting userland proxy: listen tcp 0.0.0.0:5550: bind: address already in use.

Мне кажется, что мой подписчик ужепривязан к 127.0.0.1:5550 и, следовательно, Docker больше не может этого делать, когда я пытаюсь отобразить его.Если я изменю его на -p 5549:5550, Docker не выдаст ошибку, но тогда будет та же самая ситуация, что и при попытке 1.

Вопрос

Как мне заставить моего докеризованного издателя публиковатьданные для моего недокурсированного абонента?

Код

Редактировать 1: Код обновлен, чтобы также дать пример того, как использовать docker-compose для автоматического IP-вывода.

GitHub: https://github.com/NumesSanguis/pyzmq-docker

Ответы [ 2 ]

0 голосов
/ 21 декабря 2018

У меня была такая же проблема в этом сообщении .

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

Таким образом, чтобы преодолеть это, используйте tcp://*:5550 в .bind() вместо 127.0.0.1 или IP-адреса компьютера.

Затем вы должны создать открытый IP-адрес и объявить IP-адрес между контейнером ихост-машина (я использовал из docker-compose, чтобы сделать это в упомянутом выше посте SO).Я думаю, что в вашем случае будет так, как вы пытались это сделать:

EXPOSE 5550

и

sudo docker run -p 5550:5550 -it foo/bar
0 голосов
/ 17 декабря 2018

Это в основном вопрос сетевого взаимодействия с докером, и он не относится только к pyzmq или zeromq.У вас могут возникнуть те же проблемы, что и при попытке подключения из контейнера к хосту.

Для ясности, в этом примере у вас есть сервер , работающий на хосте (zmq_sub.py, который вызывает bind), и вы хотите подключиться к нему с клиента, работающего внутри Docker-контейнера (zmq_pub.py).

Так как Docker-контейнер подключается, вам не нужно ничего делатьОтображение или переадресация порта докера.Порты EXPOSE и forwarding предназначены только для обеспечения возможности подключения к контейнеру (т. Е. В контейнере вызывается bind), а не к исходящим соединениям из контейнера, который являетсячто здесь происходит.

Главное здесь в том, что когда дело доходит до работы в сети с помощью Docker, вы должны думать о каждом контейнере как о отдельной машине в локальной сети.Для возможности подключения из других контейнеров или хоста служба должна связывать со всеми интерфейсами (или, по крайней мере, с доступным интерфейсом).Привязка к локальному узлу в контейнере означает, что только другие процессы в этом контейнере должны иметь возможность общаться с ним.Аналогично, привязка localhost к хосту означает, что контейнеры Docker не должны иметь возможность подключаться.

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

url = 'tcp://0.0.0.0:5550'
...
socket.bind(url)

или выбрать ipадрес, который соответствует вашей виртуальной сети докера.

Тогда ваш connect url должен быть ip вашего хоста, как видно из контейнера.Это можно найти через ifconfig.Обычно подойдет любой IP-адрес, но если у вас сеть docker0, это будет логичным выбором.

...