“如何解决”是将应用程序设置为监听 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
。因此,如果您将容器设置为将 (2)绑定到 127.0.0.1,它将只接受来自同一容器内 127.0.0.1 的连接。
同时,Docker 为您运行一个网络地址转换 (NAT) 层。如果您docker run -p 3080:3080
按照显示的方式运行,然后(从主机)运行iptables -vL
,您会发现其中一件事是端口转发规则,它将入站请求路由到主机上的端口 3080,到容器 IP 地址(在我的例如 172.17.0.2),通过 device docker0
,到端口 3080。在容器的网络地址空间中,它将在人工容器本地eth0
接口上接收入站连接;如果您要在套接字上调用getsockname (2),您会看到 172.17.0.2 地址。 您的进程必须接受容器本地eth0
接口或所有接口上的连接,才能从容器外部访问。
所有这些都是实现细节;你几乎永远不需要真正担心它。例如,由于 172.17.0.0/16 地址是 Docker 人为管理的,所以你无法从 off-host 访问它们,它们会在不同docker run
的 s 之间变化;对于容器之间的通信(在同一个 Docker 内部网络上),您确实间接使用它们,但通常通过 Docker 提供的 DNS 服务(因此连接到other-container-name
作为主机名,这将恰好被解析为 172.17.0.3)。如果您查看一些特别涉及的服务器启动序列的详细输出,您会看到它们迭代接口并显式绑定到所有接口;但对于大多数应用程序来说,Docker 空间中的正确答案就是始终绑定到 0.0.0.0。