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

PyZMQ Dockerized pub sub - sub won't receive messages

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

系统

最小测试用例

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 个终端并且 运行:

订户无误地接收数据(On topic b'foo', received data: b'test 1')

Docker文件

我创建了以下 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"]

然后我使用以下命令成功构建了一个 Docker 化的发布者:sudo docker build . -t foo/bar

尝试次数

尝试 1

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

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

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

尝试 2

考虑到我必须将 docker 化发布者的内部端口映射到我机器的端口,我 运行 以下 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.

相同

问题

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

代码

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

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

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

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

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

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

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

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 post 上执行此操作) .我认为在您的情况下将如下所示:

EXPOSE 5550

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