繁体   English   中英

PyZMQ Dockerized pub sub-sub不会收到消息

[英]PyZMQ Dockerized pub sub - sub won't receive messages

我想建立一个模块化系统,模块通过ZeroMQ进行通信。 为了提高可用性,我想要Dockerize(一些)这些模块,以便用户不必设置环境。 但是,我无法让一个dockerized发布者让非dockerized订阅者收到它的消息。

系统

  • 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 docker sudo docker build . -t foo/bar sudo docker build . -t foo/bar

尝试

尝试1

现在我有我的docker容器和发布者,我试图让我的非dockerized订阅者接收数据。 我运行以下2个命令:

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

我在容器发布数据中看到我的发布者,但我的订阅者什么也没收到。

尝试2

有了这个想法,我必须将我的dockerized发布者的内部端口映射到我的机器端口,我运行以下两个命令:

  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的情况相同。

如何让我的dockerized发布者将数据发布到我的非dockerized订阅者?

编辑1:更新代码以提供有关如何使用docker-compose进行自动IP推理的示例。

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

这主要是一个docker网络问题,并不是特定于pyzmq或zeromq。 尝试从容器连接到主机的任何事情都会遇到同样的问题。

需要说明的是,在此示例中,您在主机上运行了一个服务器 (zmq_sub.py,它调用了bind),并且您希望从在docker容器(zmq_pub.py)内运行的客户端连接到该服务器。

由于docker容器是连接器,因此您无需进行任何Docker端口的公开或转发。 EXPOSE和转发端口仅用于连接容器(即在容器中调用bind ),而不是容器进行出站连接,这就是这里发生的事情。

这里的主要内容是,当涉及到与docker联网时,您应该将每个容器视为本地网络上的单独机器。 为了可以从其他容器或主机连接,服务应该绑定到所有接口(或至少是可访问的接口)。 绑定到容器中的localhost意味着只有该容器中的其他进程才能与之通信。 同样,在主机上绑定localhost意味着docker容器不能连接。

所以第一个改变是你的绑定网址应该是:

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

或者选择与docker虚拟网络对应的IP地址。

然后,您的connect URL需要是从容器中看到的主机的IP。 这可以通过ifconfig找到。 通常任何IP地址都可以,但如果你有一个docker0网络,这将是合乎逻辑的选择。

我在这篇文章中遇到了同样的问题。

您的问题是容器localhost( 127.0.0.1 )与其他容器或主机localhost有所不同。

因此,要解决此问题,请在.bind()使用tcp://*:5550而不是127.0.0.1或机器IP。

然后,你应该创建一个公开IP并声明在容器和主机之间分配IP(我使用docker-compose在上面提到的SO帖子上做这个)。 我认为在您的情况下,您将尝试以下内容:

EXPOSE 5550

sudo docker run -p 5550:5550 -it foo/bar

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM