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

时间:2018-10-13 14:52:42

标签: docker docker-compose docker-networking docker-network

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

ports:
   - "3080:3080"

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

我正在跑步:

  

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

     

docker-compose版本1.17.1,版本未知

谢谢

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

3 个答案:

答案 0 :(得分:2)

“如何解决此问题”是将应用程序设置为侦听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

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

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

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

答案 1 :(得分:1)

理想情况下,您可以将容器内的服务配置为侦听 0.0.0.0:3080 而不是 localhost 127.0.0.1:3080

但是,如果像我最近一样,您正在使用硬编码的第三方服务来侦听本地主机(例如 127.0.0.1:3080),您可以在容器内使用 socat 实用程序将容器的外部端口转发到内部服务。 例如,要使侦听 localhost 端口 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 替换为任何方便的端口号。

答案 2 :(得分:0)

我建议在docker-compose文件中使用网络模式桥接

driver: bridge