我在端口转发方面遇到了无法解决的问题。我在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上不起作用。
答案 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