Docker多个网络,无法连接外界

Dan*_*aub 6 macos networking docker docker-compose

使用多个网络部署 docker-compose 时,只有第一个接口可以访问外界

version: "3.9"
services:
  speedtest:
    build:
      context: .
      dockerfile: speedtest.Dockerfile
    tty: true
    networks:
      - eth0
      - eth1

networks:
  eth0:
  eth1:
Run Code Online (Sandbox Code Playgroud)

例如,在容器内运行 pingping -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
Run Code Online (Sandbox Code Playgroud)

知道如何在两个网络上都有到互联网的出口吗?尝试了多种组合来创建网络,包括外部网络、带有自定义配置的桥接器等......

更新

larsks回答后,使用ip route addeth1 并运行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
Run Code Online (Sandbox Code Playgroud)

但还是100%丢包...

lar*_*sks 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
Run Code Online (Sandbox Code Playgroud)

路由表如下所示:

/ # 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
Run Code Online (Sandbox Code Playgroud)

当您运行ping google.com或 时ping -I eth0 google.com,在这两种情况下,您的 ICMP 请求都会通过 传出eth0,到达相应的默认网关,并最终到达 google.com。

但是当您运行时ping -I eth1 google.com,无法从该地址到达默认网关;网关只能通过 eth0 访问。由于内核无法找到有用的路由,因此它会尝试直接连接。如果我们tcpdump在 with 另一端的主机接口上运行eth1,我们会看到:

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
[...]
Run Code Online (Sandbox Code Playgroud)

内核说,“我被告知使用这个特定接口连接到这个地址,但是没有路由,所以我假设该地址位于同一个网络上,并且只是 ARP”。

当然失败了。

我们可以通过添加适当的路线来完成这项工作。您需要运行一个privileged容器来执行此操作(或至少有 CAP_NET_ADMIN):

ip route add default via 192.168.48.1 metric 101
Run Code Online (Sandbox Code Playgroud)

(网关地址是.1与 关联的网络的地址eth1。)

我们需要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
Run Code Online (Sandbox Code Playgroud)

我们可以通过以下方式成功 ping google.com eth1:

/ # 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
Run Code Online (Sandbox Code Playgroud)

经历了这一切之后,我要补充一点,我没有看到很多有必要的情况:通常,您使用额外的网络来隔离数据库服务器等,同时使用“主”接口(与默认路由关联的路由)用于出站请求。


我使用以下内容测试了所有这些docker-compose.yaml:

version: "3"

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

networks:
  eth0:
  eth1:
Run Code Online (Sandbox Code Playgroud)