当容器在localhost接口上侦听时Docker端口转发不起作用

Dav*_*ita 2 docker docker-compose docker-networking docker-network

我有无法解决的端口转发问题。我在VM中运行Linux,并且正在使用该VM中的docker。例如,当我尝试从docker compose设置端口转发时:

ports:
   - "3080:3080"
Run Code Online (Sandbox Code Playgroud)

它仅在容器中运行的应用程序正在侦听0.0.0.0:3080的情况下起作用。问题是,我正在码头化的大多数应用程序都在监听localhost。0.0.0.0以外的任何接口都会导致端口转发不起作用。您是否知道为什么会这样或如何解决?

我在跑:

Docker版本17.05.0-CE,内部版本89658be

docker-compose版本1.17.1,构建未知

谢谢

PS我发现了一个临时的解决方法。我为容器指定了网络模式“主机”,强制容器使用主机OS网络,但是这种方法在MacOS上不起作用。

Dav*_*aze 6

“如何解决此问题”是将应用程序设置为侦听0.0.0.0。对于简短的脚本,您经常会在主函数中(甚至是由库隐含)看到此代码的硬编码,但这对于“真实”服务器是极为常见的选项,并且您可能会通过命令行选项公开这种内容或环境变量。

用“为什么”来表示:在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
Run Code Online (Sandbox Code Playgroud)

这里重要的是每个容器都有自己的容器localhost,不同于其他每个容器localhost和主机的容器localhost。因此,如果将容器设置为(2)绑定到127.0.0.1,则该容器将只接受来自同一容器内 127.0.0.1的连接。

同时,Docker为您运行一个网络地址转换(NAT)层。如果您docker run -p 3080:3080按照显示的方式运行,然后(从主机)运行iptables -vL,您将发现的其中一项工作是端口转发规则,该规则将入站请求路由到主机上的端口3080,并路由到容器IP地址(在我的例如172.17.0.2),通过设备docker0到达端口3080。在容器的网络地址空间中,它将在人工容器本地eth0接口上接收入站连接;如果您要在套接字上调用getsockname(2),则会看到172.17.0.2地址。 您的过程必须接受容器本地eth0接口或所有接口上的连接,才能从容器外部进行访问。

所有这些都是实现细节;您几乎不需要真正担心任何一个。例如,由于172.17.0.0/16地址是由Docker人为管理的,因此您无法从脱离主机访问它们,它们将在不同的docker runs 之间更改;为了在容器之间(在同一Docker内部网络上)进行通信,您确实间接使用了它们,但通常是通过Docker提供的DNS服务(因此以other-container-name主机名身份连接,这将解析为172.17.0.3)。如果查看一些特别涉及的服务器启动序列的详细输出,您将看到它们通过接口进行迭代并显式绑定到所有服务器。但是对于大多数应用程序,Docker空间中的正确答案是始终绑定到0.0.0.0。