当容器侦听本地主机接口时,Docker 端口转发不起作用



我有一个无法解决的端口转发问题。我在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

最新更新