【问题标题】:PyZMQ Dockerized pub sub - sub won't receive messagesPyZMQ Dockerized pub sub - sub 不会收到消息
【发布时间】:2019-05-17 01:55:32
【问题描述】:

我想构建一个模块化系统,其中的模块通过 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

尝试

尝试 1

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

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

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

尝试 2

考虑到我必须将 dockerized 发布者的内部端口映射到我机器的端口,我运行以下 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 的情况相同。

问题

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

代码

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

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

【问题讨论】:

标签: python-3.x docker docker-compose zeromq pyzmq


【解决方案1】:

我在this post 遇到了同样的问题。

你的问题是容器localhost(127.0.0.1)和其他容器或者宿主机localhost有区别。

所以要克服这个问题,请在.bind() 中使用tcp://*:5550,而不是127.0.0.1 或机器IP。

然后,您应该公开 IP 并在容器和主机之间声明分配 IP(我在上面提到的 SO 帖子中使用 docker-compose 来执行此操作)。我认为在您的情况下,您尝试过的情况如下:

EXPOSE 5550

和

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

【讨论】:

    【解决方案2】:

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

    要清楚,在这个例子中,你有一个 服务器 在主机上运行(zmq_sub.py,它调用绑定),你想从一个运行在 docker 内的客户端连接到它容器 (zmq_pub.py)。

    由于 docker 容器是连接的,因此您无需进行任何 docker 端口暴露或转发。 EXPOSE 和转发端口仅用于使连接到容器成为可能(即在容器中调用bind),而不是从容器进行出站连接,这就是这里发生的事情。

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

    所以第一个变化是你的绑定 url 应该是:

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

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

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

    【讨论】:

    • 谢谢,网络接口的解释很有帮助。 tcp://0.0.0.0:5550 和 tcp://*:5550 都有效,并且 pub 不需要公开 5550。但是,您的 server 和 client 描述令人困惑,因为它表示请求-回复模式。这与我的示例中使用的 pubsub 模式 (zguide.zeromq.org/php:chapter5) 不同。如果您说只有 1 个模块可以bind 到ip:port,那么您是正确的,但是任何数量的模块都可以connect 到它。
    猜你喜欢
    • 2018-03-24
    • 2021-05-04
    • 2020-12-04
    • 1970-01-01
    • 2018-08-23
    • 2021-11-24
    • 1970-01-01
    • 1970-01-01
    • 2023-04-01
    相关资源
    最近更新 更多