ZeroMQ 无法在两个 Docker 容器之间进行通信
ZeroMQ failing to communicate between two Docker containers
我正在尝试在 macOS 中设置一个 Docker 与 ZeroMQ 联网的玩具示例,其中 serverd.py
向 clientd.py
发送一条消息,客户端只是显示它使用 PUSH/PULL。如果我 运行 它们在容器外,它们工作正常,但是当 运行 在单独的容器中时,我无法让它们进行通信。似乎我的 clientd.py
无法连接到容器名称,尽管它们在同一个桥接网络中。我尝试用为 serverd_dev_1
分配的 IP 地址替换主机名,但这也不起作用。
这是我的设置:
我用 docker network create -d bridge mynet
创建了一个新网络。这是 docker network inpsect mynet
的输出:
{
"Name": "mynet",
"Id": "cec7f8037c0ef173d9a9a66065bb46cb6a631fea1c0636876ccfe5a792f92412",
"Created": "2017-08-19T09:52:44.8034344Z",
"Scope": "local",
"Driver": "bridge",
"EnableIPv6": false,
"IPAM": {
"Driver": "default",
"Options": {},
"Config": [
{
"Subnet": "172.18.0.0/16",
"Gateway": "172.18.0.1"
}
]
},
"Internal": false,
"Attachable": false,
"Ingress": false,
"ConfigFrom": {
"Network": ""
},
"ConfigOnly": false,
"Containers": {
"5fa8dc2f8059d675dfd3dc4f2e50265be99361cd8a8f2730eb273772c0148742": {
"Name": "serverd_dev_1",
"EndpointID": "3a62e82b1b34d5c08f2a9f340ff93aebd65c0f3dfde70e354819befe21422d0b",
"MacAddress": "02:42:ac:12:00:02",
"IPv4Address": "172.18.0.2/16",
"IPv6Address": ""
},
"ec1e5f8c525ca8297611e02bcd3a64198fda3a07ce8ed82c0c4298609ba0357f": {
"Name": "clientd_dev_1",
"EndpointID": "a8ce6f178a225cb2d39ac0009e16c39abdd2dae02a65ba5fd073b7900f059bb8",
"MacAddress": "02:42:ac:12:00:03",
"IPv4Address": "172.18.0.3/16",
"IPv6Address": ""
}
},
"Options": {},
"Labels": {}
}
我像这样创建了 serverd.py
和 clientd.py
并将它们与它们的 Docker 文件和 docker-[=79 一起放在单独的文件夹中=]:
serverd.py:
import zmq
import time
context = zmq.Context()
socket = context.socket(zmq.PUSH)
address = "tcp://127.0.0.1:5557"
socket.bind(address)
print("Sending to {}...".format(address))
while True:
message = socket.send_string("Got it!")
print("Sent message")
time.sleep(1)
clientd.py:
import zmq
context = zmq.Context()
socket = context.socket(zmq.PULL)
address = "tcp://serverd_dev_1:5557"
socket.connect(address)
print("Listening to {}...".format(address))
while True:
message = socket.recv_string()
print("Client got message! {}".format(message))
我有两个 Docker 文件和 docker-compose.yml:
Docker文件 serverd.py:
FROM python:3.6
RUN mkdir src
ADD serverd.py /src/
RUN pip install pyzmq
WORKDIR /src/
EXPOSE 5557
Docker文件 clientd.py:
FROM python:3.6
RUN mkdir src
ADD clientd.py /src/
RUN pip install pyzmq
WORKDIR /src/
EXPOSE 5557
docker-compose.yml 对于 serverd.py:
dev:
build: .
command: ["python", "-u", "./serverd.py"]
net: mynet
docker-撰写 clientd.py:
dev:
build: .
command: ["python", "-u", "./clientd.py"]
net: mynet
serverd.py
按预期启动 docker-compose up
:
Sending to tcp://127.0.0.1:5557...
clientd.py
不会像这样启动,因为它找不到主机名 tcp://serverd_dev_1:5557
.
Attaching to countd_dev_1
dev_1 | Traceback (most recent call last):
dev_1 | File "./countd.py", line 6, in <module>
dev_1 | socket.connect(address)
dev_1 | File "zmq/backend/cython/socket.pyx", line 528, in zmq.backend.cython.socket.Socket.connect (zmq/backend/cython/socket.c:5971)
dev_1 | File "zmq/backend/cython/checkrc.pxd", line 25, in zmq.backend.cython.checkrc._check_rc (zmq/backend/cython/socket.c:10014)
dev_1 | zmq.error.ZMQError: Invalid argument
如果我将 URI tcp://serverd_dev_1:5557
替换为 tcp://172.18.0.2:5557
,它不会再崩溃,但它只是处于空闲状态,没有收到来自服务器的任何消息。显然我做错了什么,但我不完全确定到底是什么。我觉得我一直在尽可能密切地关注 Docker 文档,如果您有任何想法,我将不胜感激。
您的主要问题是您已经使用地址 tcp://127.0.0.1:5557
配置了您的服务器。因为它绑定到本地主机 (127.0.0.1
),所以该套接字对该容器之外的任何事物都不可见。因此,您需要修复的第一件事是服务器绑定地址。考虑:
address = "tcp://0.0.0.0:5557"
第二个问题是您在客户端中使用了名称 serverd_dev_1
,但不清楚这实际上是您的 serverd 容器的名称(这取决于使用的目录名称你 运行 docker-compose up
).
使用单个 docker-compose.yaml 文件更容易管理命名。例如,我这样设置:
version: "2"
services:
serverd:
build: serverd
command: ["python", "-u", "./serverd.py"]
environment:
SERVER_LISTEN_URI: tcp://0.0.0.0:5557
clientd:
build: clientd
command: ["python", "-u", "./clientd.py"]
environment:
SERVER_CONNECT_URI: tcp://serverd:5557
这将在专用网络中启动两个容器(因为这是 docker-compose 默认执行的操作),因此您不需要显式创建或引用 mynet
.
正如您可能从上面推断的那样,我修改了您的代码以从环境变量中获取 ZMQ uri,因为这使得实验更容易。您可以在 docker-compose.yaml 和修改后的代码中找到:
更新
如果您真的 want/need 有两个单独的 docker-compose.yaml
文件,我已经更新了示例以包含每个服务的文件。这些示例使用 alias
选项提供一个名称,客户端可以通过该名称联系服务器,而不管您的本地目录布局如何:
version: "2"
services:
serverd:
build: .
command: ["python", "-u", "./serverd.py"]
environment:
SERVER_LISTEN_URI: tcp://0.0.0.0:5557
networks:
mynet:
aliases:
- serverd
networks:
mynet:
external: True
此配置要求您在启动容器之前创建 mynet
。
我正在尝试在 macOS 中设置一个 Docker 与 ZeroMQ 联网的玩具示例,其中 serverd.py
向 clientd.py
发送一条消息,客户端只是显示它使用 PUSH/PULL。如果我 运行 它们在容器外,它们工作正常,但是当 运行 在单独的容器中时,我无法让它们进行通信。似乎我的 clientd.py
无法连接到容器名称,尽管它们在同一个桥接网络中。我尝试用为 serverd_dev_1
分配的 IP 地址替换主机名,但这也不起作用。
这是我的设置:
我用
docker network create -d bridge mynet
创建了一个新网络。这是docker network inpsect mynet
的输出:{ "Name": "mynet", "Id": "cec7f8037c0ef173d9a9a66065bb46cb6a631fea1c0636876ccfe5a792f92412", "Created": "2017-08-19T09:52:44.8034344Z", "Scope": "local", "Driver": "bridge", "EnableIPv6": false, "IPAM": { "Driver": "default", "Options": {}, "Config": [ { "Subnet": "172.18.0.0/16", "Gateway": "172.18.0.1" } ] }, "Internal": false, "Attachable": false, "Ingress": false, "ConfigFrom": { "Network": "" }, "ConfigOnly": false, "Containers": { "5fa8dc2f8059d675dfd3dc4f2e50265be99361cd8a8f2730eb273772c0148742": { "Name": "serverd_dev_1", "EndpointID": "3a62e82b1b34d5c08f2a9f340ff93aebd65c0f3dfde70e354819befe21422d0b", "MacAddress": "02:42:ac:12:00:02", "IPv4Address": "172.18.0.2/16", "IPv6Address": "" }, "ec1e5f8c525ca8297611e02bcd3a64198fda3a07ce8ed82c0c4298609ba0357f": { "Name": "clientd_dev_1", "EndpointID": "a8ce6f178a225cb2d39ac0009e16c39abdd2dae02a65ba5fd073b7900f059bb8", "MacAddress": "02:42:ac:12:00:03", "IPv4Address": "172.18.0.3/16", "IPv6Address": "" } }, "Options": {}, "Labels": {} }
我像这样创建了
serverd.py
和clientd.py
并将它们与它们的 Docker 文件和 docker-[=79 一起放在单独的文件夹中=]:
serverd.py:
import zmq
import time
context = zmq.Context()
socket = context.socket(zmq.PUSH)
address = "tcp://127.0.0.1:5557"
socket.bind(address)
print("Sending to {}...".format(address))
while True:
message = socket.send_string("Got it!")
print("Sent message")
time.sleep(1)
clientd.py:
import zmq
context = zmq.Context()
socket = context.socket(zmq.PULL)
address = "tcp://serverd_dev_1:5557"
socket.connect(address)
print("Listening to {}...".format(address))
while True:
message = socket.recv_string()
print("Client got message! {}".format(message))
我有两个 Docker 文件和 docker-compose.yml:
Docker文件 serverd.py:
FROM python:3.6
RUN mkdir src
ADD serverd.py /src/
RUN pip install pyzmq
WORKDIR /src/
EXPOSE 5557
Docker文件 clientd.py:
FROM python:3.6
RUN mkdir src
ADD clientd.py /src/
RUN pip install pyzmq
WORKDIR /src/
EXPOSE 5557
docker-compose.yml 对于 serverd.py:
dev:
build: .
command: ["python", "-u", "./serverd.py"]
net: mynet
docker-撰写 clientd.py:
dev:
build: .
command: ["python", "-u", "./clientd.py"]
net: mynet
serverd.py
按预期启动docker-compose up
:
Sending to tcp://127.0.0.1:5557...
clientd.py
不会像这样启动,因为它找不到主机名tcp://serverd_dev_1:5557
.Attaching to countd_dev_1 dev_1 | Traceback (most recent call last): dev_1 | File "./countd.py", line 6, in <module> dev_1 | socket.connect(address) dev_1 | File "zmq/backend/cython/socket.pyx", line 528, in zmq.backend.cython.socket.Socket.connect (zmq/backend/cython/socket.c:5971) dev_1 | File "zmq/backend/cython/checkrc.pxd", line 25, in zmq.backend.cython.checkrc._check_rc (zmq/backend/cython/socket.c:10014) dev_1 | zmq.error.ZMQError: Invalid argument
如果我将 URI
tcp://serverd_dev_1:5557
替换为tcp://172.18.0.2:5557
,它不会再崩溃,但它只是处于空闲状态,没有收到来自服务器的任何消息。显然我做错了什么,但我不完全确定到底是什么。我觉得我一直在尽可能密切地关注 Docker 文档,如果您有任何想法,我将不胜感激。
您的主要问题是您已经使用地址 tcp://127.0.0.1:5557
配置了您的服务器。因为它绑定到本地主机 (127.0.0.1
),所以该套接字对该容器之外的任何事物都不可见。因此,您需要修复的第一件事是服务器绑定地址。考虑:
address = "tcp://0.0.0.0:5557"
第二个问题是您在客户端中使用了名称 serverd_dev_1
,但不清楚这实际上是您的 serverd 容器的名称(这取决于使用的目录名称你 运行 docker-compose up
).
使用单个 docker-compose.yaml 文件更容易管理命名。例如,我这样设置:
version: "2"
services:
serverd:
build: serverd
command: ["python", "-u", "./serverd.py"]
environment:
SERVER_LISTEN_URI: tcp://0.0.0.0:5557
clientd:
build: clientd
command: ["python", "-u", "./clientd.py"]
environment:
SERVER_CONNECT_URI: tcp://serverd:5557
这将在专用网络中启动两个容器(因为这是 docker-compose 默认执行的操作),因此您不需要显式创建或引用 mynet
.
正如您可能从上面推断的那样,我修改了您的代码以从环境变量中获取 ZMQ uri,因为这使得实验更容易。您可以在 docker-compose.yaml 和修改后的代码中找到:
更新
如果您真的 want/need 有两个单独的 docker-compose.yaml
文件,我已经更新了示例以包含每个服务的文件。这些示例使用 alias
选项提供一个名称,客户端可以通过该名称联系服务器,而不管您的本地目录布局如何:
version: "2"
services:
serverd:
build: .
command: ["python", "-u", "./serverd.py"]
environment:
SERVER_LISTEN_URI: tcp://0.0.0.0:5557
networks:
mynet:
aliases:
- serverd
networks:
mynet:
external: True
此配置要求您在启动容器之前创建 mynet
。