我有一个无法解决的端口转发问题。我在VM中运行Linux,并且正在使用该VM中的docker。例如,当我尝试从 docker 撰写设置端口转发时:
ports:
- "3080:3080"
它仅在容器中运行的应用程序正在侦听 0.0.0.0:3080 的情况下起作用。问题是,我正在dockerization的大多数应用程序都在监听localhost。除 0.0.0.0 以外的任何接口都会导致端口转发不起作用。您知道为什么会发生这种情况或如何解决此问题吗?
我正在运行:
Docker 版本 17.05.0-ce,内部版本 89658be
docker-compose 版本 1.17.1,内部版本未知
谢谢
附言我找到了一个临时解决方法。我为容器指定网络模式"host",强制容器使用主机操作系统网络,但这种方法在 MacOS 上不起作用。
"如何解决这个问题"是将应用程序设置为侦听0.0.0.0。 对于短脚本,您经常会在 main 函数中看到硬编码(甚至由库暗示(,但它对于"真实"服务器来说是一个非常常见的选项,并且您可能通过命令行选项或环境变量公开这种东西。
就"为什么"而言:在 Docker 内部,每个容器都在一个隔离的网络命名空间中运行。 例如,如果您尝试:
$ docker run --rm busybox ifconfig
eth0 Link encap:Ethernet HWaddr 02:42:AC:11:00:02
inet addr:172.17.0.2 Bcast:172.17.255.255 Mask:255.255.0.0
lo Link encap:Local Loopback
inet addr:127.0.0.1 Mask:255.0.0.0
这里重要的是每个容器都有自己的localhost
,不同于其他每个容器的localhost
和主机的localhost
。 所以如果你将一个容器设置为bind(2( 到 127.0.0.1, 它只会接受来自同一容器内127.0.0.1 的连接。
同时,Docker为您运行网络地址转换(NAT(层。 如果您如图所示运行docker run -p 3080:3080
,然后(从主机(运行iptables -vL
,您会发现挖掘的一件事是端口转发规则,该规则通过设备docker0
将入站请求路由到主机上的端口 3080、容器 IP 地址(在我的示例中为 172.17.0.2(到 端口 3080。 在容器的网络地址空间中,它将在人工容器本地eth0
接口上接收入站连接;如果你在套接字上调用getsockname(2(,你会看到 172.17.0.2 地址。您的进程必须接受容器本地eth0
接口或所有接口上的连接,才能从容器外部访问。
所有这些都是实现细节;你几乎不需要真正担心其中任何一个。 例如,由于 172.17.0.0/16 地址是由 Docker 人为管理的,因此您无法从主机外访问它们,并且它们会在不同的docker run
之间切换;对于容器之间的通信(在同一 Docker 内部网络上(,您确实间接使用它们,但通常通过 Docker 提供的 DNS 服务(因此作为主机名连接到other-container-name
,这将恰好解析为 172.17.0.3(。 如果您查看一些特别涉及的服务器的启动序列的详细输出,您将看到它们遍历接口并显式绑定到所有这些接口;但对于大多数应用程序来说,Docker 空间中的正确答案是始终绑定到 0.0.0.0。
理想情况下,您可以将容器内的服务配置为侦听0.0.0.0:3080
而不是本地主机127.0.0.1:3080
但是,如果像我最近一样,您正在使用硬编码的第三方服务来侦听本地主机端口(例如127.0.0.1:3080
(,您可以使用容器内的 socat 实用程序将容器的外部端口转发到内部服务。 例如,要使侦听本地主机端口的服务127.0.0.1:3080
可以从端口3081
上的容器外部访问:
apt-get update && apt-get install -y socat
然后
socat TCP-LISTEN:3081,fork TCP:127.0.0.1:3080
然后,您可以通过 docker 端口转发从容器外部访问服务,即docker run -p 3080:3081
(或 docker-compose.yml 中的ports: ["3080:3081"]
(,用 3080 代替任何方便的端口号。
我建议在 docker 撰写文件中建立网络模式桥接
driver: bridge