我有一台 Ubuntu 服务器,运行一些 Docker 中的应用程序(包括 Nginx 代理管理器)和一些指向我服务器的公共 IP 的子域。我的所有容器都在同一个桥接网络上。其中一个容器需要向其他容器发出请求,这些请求应该使用指向主机公共 IP 的 URL(例如:app1.mydomain.net)发出,但请求永远不会到达目的地。
尝试解决该问题时,我发现当我在服务器上运行ping app1.mydomain.net
或curl -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.net
并curl -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。