我想构建一个模块化的系统,其中包含通过ZeroMQ进行通信的模块。为了提高可用性,我想对其中一些模块进行Dockerize,以便用户不必设置环境。 但是,我无法让未授权的订阅服务器接收到未授权的发布者的消息。
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'
)
我创建了以下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容器与发布者一起使用,我正在尝试让非dockerized订阅者接收数据。我运行以下2条命令:
python zmq_sub.py
sudo docker run -it foo/bar
我看到我的发布者在容器中发布数据,但是我的订阅者什么也没收到。
出于这个想法,我必须将dockerized发布者的内部端口映射到机器的端口,我运行以下2条命令:
python zmq_sub.py
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推断的示例。
答案 0 :(得分:2)
这主要是一个docker网络问题,并不特定于pyzmq或zeromq。尝试从容器连接到主机的任何事情都会遇到相同的问题。
为清楚起见,在此示例中,您在主机上运行了一个 server (zmq_sub.py,它调用bind),并且您想从运行在docker内部的客户端连接到该服务器容器(zmq_pub.py)。
由于docker容器是一个连接容器,因此您无需进行任何docker端口公开或转发。 EXPOSE和转发端口仅用于将连接到容器(即在容器中调用bind
),而不能从 建立出站连接容器,这就是这里发生的事情。
这里的主要内容是,在与docker联网时,您应该将每个容器视为本地网络上的一台单独的计算机。为了可以从其他容器或主机进行连接,服务应 bind 绑定到所有接口(或至少一个可访问的接口)上。在容器中的localhost上绑定意味着只有该容器中的其他进程应该能够与之对话。同样,在主机上绑定本地主机意味着docker容器不应该能够连接。
因此,第一个更改是您的绑定网址应为:
url = 'tcp://0.0.0.0:5550'
...
socket.bind(url)
或选择与您的docker虚拟网络相对应的IP地址。
然后,您的connect
url必须是从容器中看到的主机的ip。可以通过ifconfig
找到。通常,任何IP地址都可以,但是如果您有docker0
网络,那将是合理的选择。
答案 1 :(得分:1)
我在this post中遇到了同样的问题。
您的问题是容器localhost(127.0.0.1
)与其他容器或主机localhost有区别。
因此,要解决此问题,请在tcp://*:5550
中使用.bind()
而不是127.0.0.1
或计算机IP。
然后,您应该设置一个公开IP,并在容器和主机之间声明分配IP(我在docker-compose中使用的是在上面提到的SO帖子上执行的操作)。我认为您的情况如下所示:
EXPOSE 5550
和
sudo docker run -p 5550:5550 -it foo/bar