Docker多网络,无法连接外部世界。

6
使用docker-compose部署时,如果有多个网络,只有第一个接口可以访问外部网络。
version: "3.9"
services:
  speedtest:
    build:
      context: .
      dockerfile: speedtest.Dockerfile
    tty: true
    networks:
      - eth0
      - eth1

networks:
  eth0:
  eth1:

在容器内运行例如 ping -I eth0 google.com 的命令可以正常工作。但是运行 ping -I eth1 google.com 命令会得到以下结果。

PING google.com (142.250.200.238) from 172.21.0.2 eth1: 56(84) bytes of data.
From c4d3b238f9a1 (172.21.0.2) icmp_seq=1 Destination Host Unreachable
From c4d3b238f9a1 (172.21.0.2) icmp_seq=2 Destination Host Unreachable

有没有什么办法可以在两个网络上都访问互联网? 尝试过多种创建网络的组合,包括带有自定义配置的外部桥接等...
更新 接受了larsks的答案后,对于eth1使用ip路由添加并运行tcpdump -i any,数据包正确地传入:
11:26:12.098918 eth1  Out IP 8077ec32b69d > dns.google: ICMP echo request, id 3, seq 1, length 64
11:26:12.184195 eth1  In  IP dns.google > 8077ec32b69d: ICMP echo reply, id 3, seq 1, length 64

但仍然出现100%数据包丢失...


1
名称解析之所以有效,是因为 -I 仅影响将发送 ICMP Echo Req 数据包(Ping)的接口。无论如何,也许可以设置 NAT,因为 Docker 的内部 IP 是不可路由的。可能有一种特定于 Docker 的解决方案,但我对 Docker 不是特别熟悉。 - Yarin_007
容器内的网络名称和接口名称并不是同一回事。 - BMitch
1个回答

9
这里的问题在于容器内有两个接口,但只有一个默认路由。假设有一个具备两个接口的容器,如下所示:
/ # ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
70: eth0@if71: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
    link/ether 02:42:c0:a8:10:02 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 192.168.16.2/20 brd 192.168.31.255 scope global eth0
       valid_lft forever preferred_lft forever
72: eth1@if73: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
    link/ether 02:42:c0:a8:30:02 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 192.168.48.2/20 brd 192.168.63.255 scope global eth1
       valid_lft forever preferred_lft forever

路由表如下所示:
/ # ip route
default via 192.168.16.1 dev eth0
192.168.16.0/20 dev eth0 proto kernel scope link src 192.168.16.2
192.168.48.0/20 dev eth1 proto kernel scope link src 192.168.48.2

当你运行ping google.comping -I eth0 google.com时,在这两种情况下,你的ICMP请求通过eth0出口,经过适当的默认网关,并最终到达google.com。
但是当你运行ping -I eth1 google.com时,没有办法从该地址到达默认网关;网关只能通过eth0到达。由于内核找不到有用的路由,因此它尝试直接连接。如果我们在与eth1相连的主机接口上运行tcpdump,我们会看到:
23:47:58.035853 ARP, Request who-has 142.251.35.174 tell 192.168.48.2, length 28
23:47:59.083553 ARP, Request who-has 142.251.35.174 tell 192.168.48.2, length 28
[...]

这就是内核在说,"我被告知要使用特定接口连接到这个地址,但没有路径,所以我假设这个地址在同一网络中并进行ARP查找"。

当然,这种方法会失败。

我们可以通过添加适当的路由来解决这个问题。你需要运行一个特权容器来实现这一点(或至少具有CAP_NET_ADMIN权限):

ip route add default via 192.168.48.1 metric 101

(网关地址是与 eth1 关联的网络的地址,最后一段为 .1。)

我们需要 metric 设置来区分此路由与现有默认路由;否则命令将以错误代码 RTNETLINK answers: File exists 失败。

运行该命令后,我们得到:

/ # ip route
default via 192.168.16.1 dev eth0
default via 192.168.48.1 dev eth1 metric 101
192.168.16.0/20 dev eth0 proto kernel scope link src 192.168.16.2
192.168.48.0/20 dev eth1 proto kernel scope link src 192.168.48.2

我们可以通过eth1成功地ping通google.com:

/ # ping -c2 -I eth1 google.com
PING google.com (142.251.35.174) from 192.168.48.2 eth1: 56(84) bytes of data.
64 bytes from lga25s78-in-f14.1e100.net (142.251.35.174): icmp_seq=1 ttl=116 time=8.87 ms
64 bytes from lga25s78-in-f14.1e100.net (142.251.35.174): icmp_seq=2 ttl=116 time=8.13 ms

--- google.com ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1001ms
rtt min/avg/max/mdev = 8.127/8.497/8.868/0.370 ms

经过了所有这些,我要补充的是我认为并不需要经常使用:通常您使用额外的网络来隔离诸如数据库服务器之类的东西,同时使用“主”接口(与默认路由相关联的接口)进行出站请求。


我使用以下docker-compose.yaml进行了所有测试:

version: "3"

services:
  sleeper:
    image: alpine
    cap_add:
      - NET_ADMIN
    command:
      - sleep
      - inf
    networks:
      - eth0
      - eth1

networks:
  eth0:
  eth1:

感谢您详细而迅速的回复,我尝试重新创建您的解决方案,在主机上使用tcpdump现在可以捕获流量,但是流量没有返回到容器内部,ping命令显示100%的数据包丢失。 - Daniel Taub
1
我刚刚注意到你正在MacOS下运行。这意味着Docker正在Linux虚拟机内运行。虽然这不应该影响到这个问题,但我想要查看来自macos主机和Linux虚拟机内部的流量,以确定问题出在哪里。 - larsks
这些地址是基于分配给 eth1 的地址的(我在回答中明确指出了这一点,“网关地址是与 eth1 关联的网络的 .1 地址。”)。因此,如果你的 eth1172.19.0.2,那么该网络的网关将是 172.19.0.1 - larsks
也许这就是问题所在 https://docs.docker.com/desktop/mac/networking/#i-cannot-ping-my-containers - Daniel Taub
@larsks 在主机和容器上同时运行tcpdump可以显示进出流量,但不确定为什么会在此之后被丢弃,可能与iptables有关。 - Daniel Taub
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接