如何在 Docker 容器中访问主机的 eno1 网络?



我有一台带有多个网络接口的主机。eth0是连接到互联网的主要网络接口,eno1是基本上是使用USB以太网适配器连接到主机的交换机的次要专用网络。

在这种配置中,有没有一种方法可以让我从Docker容器中访问连接到交换机(172.16.0.0/22)的设备,但不需要用--net=host启动容器?我希望容器被隔离,所以使用--net=host会破坏这一点。

但是,我也希望能够在Docker容器中与连接到eno1的设备进行通信。

我可能实现这一点的一种方法是,让一个服务在Docker主机上运行,绑定到主机的docker0IP地址,并让所有容器通过Docker的默认网桥网络连接,使用主机上运行的服务作为代理访问连接到eno1的设备

还有别的办法吗?类似于--net=host,但不是完全共享主机的网络,我可以选择只共享172.16.0.0/22子网?


主机上ip -4 a的输出:

1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
inet 10.91.44.20/22 brd 10.91.47.255 scope global noprefixroute eth0
valid_lft forever preferred_lft forever
3: eno1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
inet 172.16.0.1/22 brd 172.16.3.255 scope global noprefixroute eno1
valid_lft forever preferred_lft forever
4: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default
inet 240.10.0.1/24 brd 240.10.0.255 scope global docker0
valid_lft forever preferred_lft forever

ip -4 a在集装箱上的输出:

1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN qlen 1000
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
243: eth0@if244: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue state UP
inet 240.10.0.2/24 brd 240.10.0.255 scope global eth0
valid_lft forever preferred_lft forever

nc -zv 172.16.3.1 5555在主机上的输出:

Ncat: Version 7.50 ( https://nmap.org/ncat )
Ncat: Connected to 172.16.3.1:5555.
Ncat: 0 bytes sent, 0 bytes received in 0.02 seconds.

nc -zv 172.16.3.1 5555在使用以下命令启动时无法在容器中连接:

docker run -d --rm --init --privileged --cap-add=NET_ADMIN --name=test -i my-image:latest

nc -zv 172.16.3.1 5555在使用以下命令启动时在容器中成功连接:

docker run -d --rm --init --privileged --net=host --cap-add=NET_ADMIN --name=test -i my-image:latest
docker exec -it test sh -c 'nc -zv 172.16.3.1 5555'
172.16.3.1 (172.16.3.1:5555) open

在主机上:

$ sudo iptables -S FORWARD
-P FORWARD DROP
-A FORWARD -j DOCKER-USER
-A FORWARD -j DOCKER-ISOLATION-STAGE-1
-A FORWARD -o docker0 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
-A FORWARD -o docker0 -j DOCKER
-A FORWARD -i docker0 ! -o docker0 -j ACCEPT
-A FORWARD -i docker0 -o docker0 -j ACCEPT
$ ip route
default via 10.91.44.1 dev eth0 proto static metric 20
default via 10.91.44.1 dev eno1 proto static metric 40
10.91.44.0/22 dev eth0 proto kernel scope link src 10.91.44.20 metric 20
10.91.44.1 dev eno1 proto static scope link metric 40
172.16.0.0/22 dev eno1 proto kernel scope link src 172.16.0.1 metric 40
240.10.0.0/24 dev docker0 proto kernel scope link src 240.10.0.1

集装箱上:

$ ip route
default via 240.10.0.1 dev eth0
240.10.0.0/24 dev eth0 scope link  src 240.10.0.2

我想做的是从容器内连接172.16.3.1:55555,而不必用--net=host启动容器。

实现目标的一种可能方法是为Docker使用macvlan网络驱动程序。此驱动程序允许您为每个直接连接到主机物理网络接口的容器创建一个虚拟网络接口。通过这种方式,容器可以与eno1网络上的设备通信,就好像它们直接连接到交换机一样,而无需将主机用作代理或共享主机的网络命名空间。

解释

macvlan网络驱动程序创建主机物理网络接口的子接口,并为其分配一个唯一的MAC地址和一个来自与主机相同子网的IP地址。然后,子接口连接到容器的网络名称空间,允许容器在物理网络上发送和接收数据包。主机充当子接口和物理网络接口之间的桥梁,但它不为容器执行任何路由或NAT。

要使用macvlan网络驱动程序,您需要使用Docker创建macvlan网络,并指定要用于容器的父网络接口和子网。例如,如果主机的eno1接口的IP地址为172.16.0.1/22,并且您希望容器的范围为172.16.0.128/25,则可以使用以下命令创建macvlan网络:

docker network create -d macvlan 
--subnet=172.16.0.0/22 
--gateway=172.16.0.1 
--ip-range=172.16.0.128/25 
-o parent=eno1 
eno1_net

此命令使用指定的参数创建一个名为eno1_net的macvlan网络。-o父选项指定macvlan网络使用的物理网络接口。--subnet、--gateway和--ip range选项指定容器的网络配置。您可以使用来自与主机相同子网的任何有效IP地址,但请确保这些地址与主机的IP地址或网络上的任何其他设备不冲突。

创建macvlan网络后,可以使用带有网络名称的--network选项在网络上运行容器。例如,要在macvlan网络上运行图像为ubuntu:18.04的容器,可以使用以下命令:

docker run -it --network eno1_net ubuntu:18.04

该命令运行一个带有交互式shell的容器,并将其连接到eno1_net网络。容器的IP地址范围为172.16.0.128/25,能够通过主机的eth0接口与eno1网络上的设备以及互联网进行通信。

示例

为了演示macvlan网络驱动程序的使用,让我们假设您有一个具有两个网络接口的主机:eth0和eno1。eth0接口连接到互联网,IP地址为192.168.1.10/24。eno1接口连接到交换机,IP地址为172.16.0.1/22。交换机还连接了另外两个设备:一台IP地址为172.16.0.2的打印机和一台IP号码为172.16.0.3的笔记本电脑。

您希望运行一个容器,该容器可以访问eno1网络上的打印机和笔记本电脑,也可以通过eth0接口访问互联网。您还希望将容器与主机的网络命名空间隔离开来。要实现这一点,您可以使用以下步骤:

  1. 创建一个macvlan网络,父级为eno1,IP范围为172.16.0.128/25:
docker network create -d macvlan 
--subnet=172.16.0.0/22 
--gateway=172.16.0.1 
--ip-range=172.16.0.128/25 
-o parent=eno1 
eno1_net
  1. macvlan网络上运行带有图像ubuntu:18.04的容器:
docker run -it --network eno1_net ubuntu:18.04
  1. 在容器内,使用ip命令验证网络配置:
root@b0f0a8f0a0f0:/# ip a
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
9: eth0@if10: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default 
link/ether 02:42:ac:10:00:80 brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet 172.16.0.128/22 brd 172.16.3.255 scope global eth0
valid_lft forever preferred_lft forever

您可以看到容器有一个eth0接口,IP地址为172.16.0.128/22,来自macvlan网络。

  1. Ping eno1网络上的打印机和笔记本电脑:
root@b0f0a8f0a0f0:/# ping 172.16.0.2
PING 172.16.0.2 (172.16.0.2) 56(84) bytes of data.
64 bytes from 172.16.0.2: icmp_seq=1 ttl=64 time=0.386 ms
64 bytes from 172.16.0.2: icmp_seq=2 ttl=64 time=0.325 ms
^C
--- 172.16.0.2 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1001ms
rtt min/avg/max/mdev = 0.325/0.355/0.386/0.034 ms
root@b0f0a8f0a0f0:/# ping 172.16.0.3
PING 172.16.0.3 (172.16.0.3) 56(84) bytes of data.
64 bytes from 172.16.0.3: icmp_seq=1 ttl=64 time=0.421 ms
64 bytes from 172.16.0.3: icmp_seq=2 ttl=64 time=0.358 ms
^C
--- 172.16.0.3 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1001ms
rtt min/avg/max/mdev = 0.358/0.389/0.421/0.036 ms

您可以看到容器可以在eno1网络上成功ping打印机和笔记本电脑。

  1. 通过主机的eth0接口在互联网上Ping网站:
root@b0f0a8f0a0f0:/# ping google.com
PING google.com (142.250.74.238) 56(84) bytes of data.
64 bytes from 142.250.74.238: icmp_seq=1 ttl=117 time=14.9 ms
64 bytes from 142.250.74.238: icmp_seq=2 ttl=117 time=14.9 ms
^C
--- google.com ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1001ms
rtt min/avg/max/mdev = 14.900/14.900/14.900/0.000 ms

您可以看到,容器可以通过主机的eth0接口成功地在互联网上ping网站。

  1. 退出容器并验证主机的网络配置是否保持不变:
root@b0f0a8f0a0f0:/# exit
exit
$ ip a
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
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
link/ether 52:54:00:12:34:56 brd ff:ff:ff:ff:ff:ff
inet 192.168.1.10/24 brd 192.168.1.255 scope global dynamic eth0
valid_lft 86397sec preferred_lft 86397sec
3: eno1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
link/ether 00:e0:4c:68:00:01 brd ff:ff:ff:ff:ff:ff
inet 172.16.0.1/22 brd 172.16.3.255 scope global eno1
valid_lft forever preferred_lft forever

您可以看到主机的网络配置没有变化,主机在eth0和eno1接口上的IP地址分别为192.168.1.10/24和172.16.0.1/22。主机不知道容器的IP地址或macvlan网络。

摘要

在这个答案中,我们解释了如何使用Docker的macvlan网络驱动程序来实现从容器内访问eno1网络上的设备的目标,而无需将主机用作代理或共享主机的网络命名空间。我们还提供了一个示例,说明如何使用Docker创建和使用macvlan网络,以及如何验证容器和主机的网络配置。我们希望这个答案是有用的和有信息的。

最新更新