简体   繁体   English

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

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

I want to build a modularized system with modules communicating over ZeroMQ. 我想建立一个模块化系统,模块通过ZeroMQ进行通信。 To improve usability, I want to Dockerize (some) of these modules, so that users don't have to setup an environment. 为了提高可用性,我想要Dockerize(一些)这些模块,以便用户不必设置环境。 However, I cannot get a dockerized publisher to have its messaged received by a non-dockerized subscriber. 但是,我无法让一个dockerized发布者让非dockerized订阅者收到它的消息。

System 系统

  • Ubuntu 18.04 Ubuntu 18.04
  • Python 3.7 Python 3.7
  • libzmq version 4.2.5 libzmq版本4.2.5
  • pyzmq version is 17.1.2 pyzmq版本是17.1.2
  • Docker version 18.09.0, build 4d60db4 Docker版本18.09.0,构建4d60db4

Minimal test case 最小的测试用例

zmq_sub.py 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 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()

When I open 2 terminals and run: 当我打开2个终端并运行时:

  • python zmq_sub.py python zmq_sub.py
  • python zmq_pub.py python zmq_pub.py

The subscriber receives without error the data ( On topic b'foo', received data: b'test 1' ) 订户无错误地接收数据( On topic b'foo', received data: b'test 1'

Dockerfile Dockerfile

I've created the following 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"]

and then I successfully build a Dockerized publisher with the command: sudo docker build . -t foo/bar 然后我用命令成功构建了一个Dockerized发布者: sudo docker build . -t foo/bar docker sudo docker build . -t foo/bar sudo docker build . -t foo/bar

Attempts 尝试

Attempt 1 尝试1

Now I have my docker container with publisher, I'm trying to have my non-dockerized subscriber receive the data. 现在我有我的docker容器和发布者,我试图让我的非dockerized订阅者接收数据。 I run the following 2 commands: 我运行以下2个命令:

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

I see my publisher inside the container publishing data, but my subscriber receives nothing. 我在容器发布数据中看到我的发布者,但我的订阅者什么也没收到。

Attempt 2 尝试2

With the idea I have to map the internal port of my dockerized publisher to my machine's port, I run the following 2 commands: 有了这个想法,我必须将我的dockerized发布者的内部端口映射到我的机器端口,我运行以下两个命令:

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

However, then I receive the following error: 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. 但是,我收到以下错误: 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.

It seems to me that my subscriber has already bound to 127.0.0.1:5550 and therefore Docker cannot do this anymore when I try to map it. 在我看来,我的订户已经绑定到127.0.0.1:5550 ,因此当我尝试映射时,Docker不能再这样做了。 If I change it to -p 5549:5550 , Docker doesn't give an error, but then it's the same situation as with Attempt 1. 如果我将其更改为-p 5549:5550 ,Docker不会给出错误,但是它与尝试1的情况相同。

Question

How do I get my dockerized publisher to publish data to my non-dockerized subscriber? 如何让我的dockerized发布者将数据发布到我的非dockerized订阅者?

Code

Edit 1: Code updated to also give an example on how to use docker-compose for automatic IP inference. 编辑1:更新代码以提供有关如何使用docker-compose进行自动IP推理的示例。

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

This is mostly a docker networking question, and isn't specific to pyzmq or zeromq. 这主要是一个docker网络问题,并不是特定于pyzmq或zeromq。 You would have the same issues with anything trying to connect from a container to the host. 尝试从容器连接到主机的任何事情都会遇到同样的问题。

To be clear, in this example, you have a server running on the host (zmq_sub.py, which calls bind), and you want to connect to it from a client running inside a docker container (zmq_pub.py). 需要说明的是,在此示例中,您在主机上运行了一个服务器 (zmq_sub.py,它调用了bind),并且您希望从在docker容器(zmq_pub.py)内运行的客户端连接到该服务器。

Since the docker container is the one connecting, you don't need to do any docker port exposing or forwarding. 由于docker容器是连接器,因此您无需进行任何Docker端口的公开或转发。 EXPOSE and forwarding ports are only for making it possible to connect to a container (ie bind is called in the container), not to make outbound connections from a container, which is what's going on here. EXPOSE和转发端口仅用于连接容器(即在容器中调用bind ),而不是容器进行出站连接,这就是这里发生的事情。

The main thing here is that when it comes to networking with docker, you should think of each container as a separate machine on a local network. 这里的主要内容是,当涉及到与docker联网时,您应该将每个容器视为本地网络上的单独机器。 In order to be connectable from other containers or the host, a service should bind onto all interfaces (or at least an accessible interface). 为了可以从其他容器或主机连接,服务应该绑定到所有接口(或至少是可访问的接口)。 Binding on localhost in a container means that only other processes in that container should be able to talk to it. 绑定到容器中的localhost意味着只有该容器中的其他进程才能与之通信。 Similarly, binding localhost on the host means that docker containers shouldn't be able to connect. 同样,在主机上绑定localhost意味着docker容器不能连接。

So the first change is that your bind url should be: 所以第一个改变是你的绑定网址应该是:

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

or pick the ip address that corresponds to your docker virtual network. 或者选择与docker虚拟网络对应的IP地址。

Then, your connect url needs to be the ip of your host as seen from the container. 然后,您的connect URL需要是从容器中看到的主机的IP。 This can be found via ifconfig . 这可以通过ifconfig找到。 Typically any ip address will do, but if you have a docker0 network, that would be the logical choice. 通常任何IP地址都可以,但如果你有一个docker0网络,这将是合乎逻辑的选择。

I had the same problem in this post . 我在这篇文章中遇到了同样的问题。

Your problem is that the container localhost ( 127.0.0.1 ) have a difference with other container or host machine localhost. 您的问题是容器localhost( 127.0.0.1 )与其他容器或主机localhost有所不同。

So to overcome that, use the tcp://*:5550 in the .bind() instead of 127.0.0.1 or machine IP. 因此,要解决此问题,请在.bind()使用tcp://*:5550而不是127.0.0.1或机器IP。

Then, you should make an expose IP and declare assign IP between the container and the host machine (I used from docker-compose to do that on the mentioned SO post at above). 然后,你应该创建一个公开IP并声明在容器和主机之间分配IP(我使用docker-compose在上面提到的SO帖子上做这个)。 I think in your case will be as the following as you tried it: 我认为在您的情况下,您将尝试以下内容:

EXPOSE 5550

and

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

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

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