Docker 容器无法使用主机公共 IP 进行通信

Docker 容器无法使用主机公共 IP 进行通信

我有一台 Ubuntu 服务器,运行一些 Docker 中的应用程序(包括 Nginx 代理管理器)和一些指向我服务器的公共 IP 的子域。我的所有容器都在同一个桥接网络上。其中一个容器需要向其他容器发出请求,这些请求应该使用指向主机公共 IP 的 URL(例如:app1.mydomain.net)发出,但请求永远不会到达目的地。

尝试解决该问题时,我发现当我在服务器上运行ping app1.mydomain.netcurl -v app1.mydomain.net在终端中时,几分钟后总会超时,但如果我在另一台电脑上运行相同的命令,或者使用不指向主机公共 IP 的不同 URL 运行相同的命令,我会得到成功的响应。

然后我使用了iptables -t nat -A OUTPUT -d [SERVER-PUBLIC-IP]/32 -j DNAT --to-destination [SERVER-PRIVATE-IP],现在我可以使用ping app1.mydomain.netcurl -v app1.mydomain.net从服务器接收成功的响应。但我认为这不是正确的解决方案,因为在容器内部,超时仍然会发生,就好像什么都没有改变一样。

在 iptables 命令之后,我还尝试了以下命令进行测试:

docker run --rm curlimages/curl -L -v https://google.com(成功)

docker run --rm curlimages/curl -L -v https://app1.mydomain.net(超时失败)

docker run --rm --network host curlimages/curl -L -v https://app1.mydomain.net(成功)

我会很感激任何帮助。抱歉语法不对,我不是母语人士。

答案1

我觉得这是主机路由的问题。我能够复制此问题。在全新安装中,我能够从容器内 ping 主机

主机 IP = 208.85.135.195(出于隐私原因而更改,您需要根据您的情况更改下面示例中的 IP 和设备)

$ docker run -it --rm busybox
/ # ping bigboast.net
PING bigboast.net (208.85.135.195): 56 data bytes
64 bytes from 208.85.135.195: seq=0 ttl=64 time=0.077 ms
64 bytes from 208.85.135.195: seq=1 ttl=64 time=0.272 ms

我的主机路线

$ ip route
default via 208.85.135.1 dev enp3s0 onlink 
172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1 linkdown 
208.85.135.0/24 dev enp3s0 proto kernel scope link src 208.85.135.195

现在,当我添加新的桥接网络(通过 docker-compose)时出现了连接问题。

busybox 现在超时

我的 Host 路线

$ ip route show
default via 208.85.135.1 dev enp3s0 onlink 
172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1 linkdown 
172.18.0.0/16 dev br-1723ac40a178 proto kernel scope link src 172.18.0.1 
208.85.135.0/24 dev enp3s0 proto kernel scope link src 208.85.135.195 

解决方案/修复

用包含下一跳路由器“via”的路由替换主机路由

$ sudo ip route replace 208.85.135.0/24 via 208.85.135.1 dev enp3s0

注意:我发现如果你先创建新的docker桥接网络,这并不总是有效。所以最好在机器启动时执行此操作。

编辑 /etc/network/interfaces 并在设备上添加“up ip route replace 208.85.135.0/24 via 208.85.135.1 dev enp3s0”。例如

auto enp3s0
iface enp3s0 inet static
    address 208.85.135.195
    netmask 24
    gateway 208.85.135.1
    up ip route replace 208.85.135.0/24 via 208.85.135.1 dev enp3s0

答案2

我刚刚遇到了这个问题。这是因为当连接进入机器时,有一些 iptables 规则会根据端口号重写数据包,这样它的目标 IP 地址就是 Docker 容器的内部 IP,而不是主机的 IP。然后根据其内部 IP,将该数据包发送到正确的容器。

问题是,当数据包来自本地计算机时,这些规则不会运行,因此您最终会尝试连接到主机上已关闭的给定端口。通常,Docker 容器会拦截到此端口的连接,使其看起来像是端口已打开,但在这种情况下,您是在 Docker 拦截之后直接在主机网络上访问端口。

似乎没有办法让本地数据包遍历将端口映射到容器 IP 的 DNAT iptables 规则,因为这将解决问题。

当我尝试时出现错误:

x_tables: ip_tables: DNAT target: used from hooks PREROUTING/INPUT/OUTPUT,
  but only usable from PREROUTING/OUTPUT

一个快速的解决方法是运行容器,--net=host打开主机上的端口,而不是使用 iptables 规则重写。

给出的正常解决方案是使用docker0网络接口的 IP 地址,但就我而言,这是不可能的,因为我在容器中运行 DNS 服务器,所以我的其他容器从中获取 IP /etc/resolv.conf。我无法将其更改为 Docker 内部 IP,因为它来自 DHCP,并且会破坏网络上其他计算机或主机上运行的其他进程的 DNS。所以我别无选择,只能在 Docker 容器中使用主机 IP。

相关内容