Linux 虚拟网卡技术:Macvlan

七月 03, 2024 更新: 七月 12, 2024 [网络与安全] #Linux #obsidian #合集 #网络

Docker的bridge和macvlan两种网络模式

Docker有好几种网络模式,这里介绍下bridge和macvlan两种模式

Bridge模式

    Bridge模式是Docker默认的网络模式,当Docker进程启动时,会在主机上创建一个名为docker0的虚拟网桥,用来连接宿主机和容器,此主机上的Docker容器都会连接到这个虚拟网桥上,虚拟网桥的工作方式和物理交换机类似,这样所有容器就通过交换机连在了一个二层网络中。

看一下实际创建容器后的情况

上面两个图是宿主机上看到的,创建了一个docker0的网桥,ip是172.17.0.1/16,并且创建了一个路由,ip在172.17.0.0/16段的都会都会转到docker0网桥上。

上图是从容器内部看到的,容器有eth0这个网卡对外通信,ip是172.17.0.2/16,跟docker0在同一网段,路由里default via 172.17.0.1指定了容器的网关是docker0

并且容器的eth0还是eth0@if45,就是45网卡的一个接口

上面是宿主机的网卡信息,可以看到有一个编号45的"veth" 开头的网卡,这是一个 veth 设备,而 veth 设备总是成对出现的,另一半就是上面容器内的那个eth0,并且查看网桥信息也能看到这个veth的设备跟docker0网桥是绑定关系。

通过上面的基本上就明确网络的模式了,Docker利用 veth pair技术,在宿主机上创建了两个虚拟网络接口 veth0 和 veth1(veth pair技术的特性可以保证无论哪一个veth 接收到网络报文,都会无条件地传输给另一方),Docker将veth pair设备的一端放在新创建的容器中,并命名为eth0(容器的网卡,从docker0子网中分配一个IP给容器使用,并设置docker0的IP地址为容器的默认网关),另一端放在主机中,以vethxxx这样类似的名字命名,并将这个网络设备加入到docker0网桥中。可以通过brctl show命令查看,这样容器和网桥就可以相互通信了。网络结构如下图:

容器与宿主机通信 : 在桥接模式下,Docker Daemon将veth0附加到docker0网桥上,保证宿主机的报文有能力发往veth0。再将veth1 添加到Docker 容器所属的网络命名空间,保证宿主机的网络报文若发往 veth0 可以立即被 veth1 收到。

容器与外界通信 : 容器如果需要联网,则需要采用 NAT(是一种在 ip 数据包通过路由器或防火墙时,重写来源 ip 地址或目的 ip 地址的技术) 方式。准确的说,是 NATP (网络地址端口转换) 方式。NATP 包含两种转换方式:SNAT 和 DNAT 。

由于容器的 IP 与端口对外都是不可见的,所以数据包的目的地址为宿主机的ip 和端口,为 192.168.1.10:24 。

数据包经过路由器发给宿主机 eth0,再经 eth0 转发给 docker0 网桥。由于存在 DNAT 规则,会将数据包的目的地址转换为容器的ip 和端口,为 172.17.0.n:24 。

宿主机上的 docker0 网桥识别到容器 ip 和端口,于是将数据包发送附加到 docker0 网桥上的 veth0 接口,veth0 接口再将数据包发送给容器内部的 veth1 接口,容器接收数据包并作出响应。

整个过程如下图:

此时数据包的源地址为容器的ip 和端口,为 172.17.0.n:24,容器内部的 veth1 接口将数据包发往 veth0 接口,到达 docker0 网桥。

宿主机上的 docker0 网桥发现数据包的目的地址为外界的 IP 和端口,便会将数据包转发给 eth0 ,并从 eth0 发出去。由于存在 SNAT 规则,会将数据包的源地址转换为宿主机的ip 和端口,为 192.168.1.10:24 。

由于路由器可以识别到宿主机的 ip 地址,所以再将数据包转发给外界,外界接受数据包并作出响应。这时候,在外界看来,这个数据包就是从 192.168.1.10:24 上发出来的,Docker 容器对外是不可见的。

整个过程如下图:

macvlan模式

macvlan本身是linxu kernel的模块,本质上是一种网卡虚拟化技术。其功能是允许在同一个物理网卡上虚拟出多个网卡,通过不同的MAC地址在数据链路层进行网络数据的转发,一块网卡上配置多个 MAC 地址(即多个 interface),每个interface可以配置自己的IP,Docker的macvlan网络实际上就是使用了Linux提供的macvlan驱动

因为多个MAC地址的网络数据包都是从同一块网卡上传输,所以需要打开网卡的混杂模式ip link set eth0 promisc on;

创建macvlan网络不同于桥接模式,需要指定网段和网关,并且都得是真实存在的,例如docker network create -d macvlan --subnet=10.9.8.0/24 --gateway=10.9.8.254 -o parent=eth0 macvlan-test

macvlan模式不依赖网桥,所以brctl show查看并没有创建新的bridge,但是查看容器的网络,会看到虚拟网卡对应了一个interface是2

查看宿主机的网络,2正是虚机的网卡

可见,容器的 eth0 就是宿主机的eth0通过macvlan虚拟出来的interface。容器的interface直接与主机的网卡连接,这种方案使得容器无需通过NAT和端口映射就能与外网直接通信(只要有网关),在网络上看起来与其他独立主机没有区别。当前网络结构如图所示:

macvlan会独占主机的网卡,也就是说一个网卡只能创建一个macvlan 网络,否则会报错,为了能支持更多的macvlan网络,macvlan不仅可以连接到interface(如eth0),也可以连接到网卡的子接口sub-interface(如eth0.xxx)。

VLAN 是现代网络常用的网络虚拟化技术,它可以将物理的二层网络划分成多达 4094 个逻辑网络,这些逻辑网络在二层上是隔离的,每个逻辑网络(即 VLAN)由 VLAN ID 区分,VLAN ID 的取值为 1-4094。Linux 的网卡也能支持 VLAN(apt-get install vlan),同一个 interface可以收发多个VLAN 的数据包,不过前提是要创建 VLAN的 sub-interface。比如希望 eth0同时支持 VLAN10 和 VLAN20,则需创建 sub-interface eth0.10 和 eth0.20。创建的子接口是可以看到的,如下如

在交换机上,如果某个端口只能收发单个VLAN的数据,该port为Access模式,如果支持多个VLAN,则为Trunk模式,要实现多个macvlan,网卡要接在交换机的 trunk口上。

例如给网卡创建了10、20两个子接口后,分别创建容器后的网络结构如下图:

Docker跨主机通信之macvlan

macvlan 本身是 linxu kernel 模块,其功能是允许在同一个物理网卡上配置多个虚拟网卡,即多个 interface,每个 interface 可以配置自己的 IP。macvlan 本质上是一种网卡虚拟化技术。

macvlan 的最大优点是性能极好,相比其他方式,macvlan 不需要创建 Linux bridge,而是直接通过interface 连接到物理网络。

0、准备实验环境

我们会使用 node1和 node2 上单独的网卡创建 macvlan。为保证多个 MAC 地址的网络包都可以从网卡通过,我们需要打开网卡的混杂模式。

ip link set enp0s3 promisc on

确保 enp0s3 状态 UP 并且 promisc 模式已经生效。

因为 node2 和 node3 是 VirtualBox 虚拟机,还需要在网卡配置选项页中设置混杂模式。

当前实验环境拓扑图如下:

1、相同 macvlan 网络之间的通信

分别在 node2 和 node3 中创建 macvlan 网络 macvlan1:

docker network create -d macvlan --subnet=172.16.5.0/24 --gateway=172.16.5.1 -o parent=enp0s3 macvlan1

这条命令中,

之后可以看到当前主机的网络环境,其中出现了 macvlan 网络:

docker network ls
NETWORK ID          NAME                DRIVER              SCOPE
2c49b5f1a0f2        bridge              bridge              local
1651b429a674        host                host                local
1d34c56c6448        macvlan1            macvlan             local
4f2664e6f883        none                null                local

在 node1 中运行容器 busybox1 并连接到 macvlan1。

docker run -itd --name busybox1 --ip=172.16.5.2 --network macvlan1 busybox

这条命令中,

同样在 node2 中运行容器 busybox2:

docker run -itd --name busybox2 --ip=172.16.5.3 --network macvlan1 busybox

验证 busybox1 和 busybox2 的连通性。

busybox2 能够 ping 通 busybox1 的 IP 172.16.10.2

docker exec -it busybox2 /bin/sh
/ # ping 172.16.5.2
PING 172.16.5.2 (172.16.5.2): 56 data bytes
64 bytes from 172.16.5.2: seq=0 ttl=64 time=0.615 ms

2、不同 macvlan 网络之间的通信

macvlan 会独占主机的物理网卡,也就是说一个物理网卡只能创建一个 macvlan 网络,否则会报错:

docker network create -d macvlan --subnet=172.16.20.0/24 --gateway=172.16.20.1 -o parent=enp0s3 macvlan2
Error response from daemon: network dm-1d34c56c6448 is already using parent interface enp0s3

如果我们想创建多个 macvlan 网络就得用多张网卡,但主机的物理网卡数量是有限的。

如何支持更多的 macvlan 网络呢?

好在 macvlan 不仅可以连接到 interface(如 enp0s3),也可以连接到 sub-interface(如 enp0s33.100)。

注意

在交换机上,如果某个 port 只能收发单个 VLAN 的数据,该 port 为 Access 模式. 如果支持多 VLAN,则为 Trunk 模式.enp0s3 要接在交换机的 trunk 口上

接下来,我们来看看不同 macvlan 网络之间的连通性,拓扑环境如下:

2.1 创建子接口

首先分别在两台主机上将物理网口 创建出两个 VLAN 子接口

配置vlan需要vconfig命令,由于centos7上没有自带vconfig命令,所以需要安装vconfig

#确认内核是够载入了802.1q模组
lsmod|grep 8021q
#如果没载入使用这个命令载入模组
modprobe -a 8021q
#配置epel源
yum install epel-release -y
#安装vconfig
yum install vconfig -y
#通过ip -a或者ifconfig -a查看是否生效
## 使用 vconfig 命令在 enp0s3 配置两个 VLAN
#node2 和 node3
vconfig add enp0s3 100
vconfig add enp0s3 200

## 设置 VLAN 的 REORDER_HDR 参数,默认就行了
#node2 和 node3
vconfig set_flag enp0s3.100 1 1
vconfig set_flag enp0s3.200 1 1

## 启用接口
## node2 和 node3
ifconfig enp0s3.100 up
ifconfig enp0s3.200 up

2.2 创建macvlan

分别在 node2 和 node3 上基于两个 VLAN 子接口创建 2 个 macvlan 网络,mac10 和 mac20

#node2 和 node3
docker network create -d macvlan --subnet=172.16.10.0/24 --gateway=172.16.10.1 -o parent=enp0s3.100 mac10
docker network create -d macvlan --subnet=172.16.20.0/24 --gateway=172.16.20.1 -o parent=enp0s3.200 mac20

2.3 创建容器

分别在 node2 和 node3 上运行容器,并指定不同的 macvlan 网络

## node2
docker run -itd --name box1 --ip=172.16.10.10 --network mac10 busybox
docker run -itd --name box2 --ip=172.16.20.10 --network mac20 busybox

## node3
docker run -itd --name box3 --ip=172.16.10.11 --network mac10 busybox
docker run -itd --name box4 --ip=172.16.20.11 --network mac20 busybox

通过验证,box1 和 box3,box2 和 box4 在同一 macvlan 网络下,互相可以 ping 通,box1 和 box2,box3 和 box4 在不同的 macvlan 网络下,互相 ping 不通。

[root@tuling2 ~]# docker exec -it box1 ifconfig
eth0      Link encap:Ethernet  HWaddr 02:42:AC:10:0A:0A
          inet addr:172.16.10.10  Bcast:172.16.10.255  Mask:255.255.255.0
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:0 errors:0 dropped:0 overruns:0 frame:0
          TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0
          RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)

lo        Link encap:Local Loopback
          inet addr:127.0.0.1  Mask:255.0.0.0
          UP LOOPBACK RUNNING  MTU:65536  Metric:1
          RX packets:0 errors:0 dropped:0 overruns:0 frame:0
          TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000
          RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)

[root@tuling2 ~]# docker exec -it box1 ping 172.16.10.11
PING 172.16.10.11 (172.16.10.11): 56 data bytes
64 bytes from 172.16.10.11: seq=0 ttl=64 time=2.781 ms
64 bytes from 172.16.10.11: seq=1 ttl=64 time=1.106 ms

这个原因也很明确,不同 macvlan 网络处于不同的网络,而且通过 VLAN 隔离,自然 ping 不了。

但这也只是在二层上通不了,通过三层的路由是可以通的,我们这就来验证下。

重新找一台主机 node1,通过打开 ip_forward 把它改造成一台路由器,用来打通两个 macvlan 网络,大概的图示如下所示:

2.4 开启路由转发

首先对 node1执行 sysctl -w net.ipv4.ip_forward=1 打开路由开关。

2.5 设置网关

然后创建两个 VLAN 子接口,一个作为 macvlan 网络 mac10 的网关,一个作为 mac20 的网关。

vconfig add enp0s3 100
vconfig add enp0s3 200
vconfig set_flag enp0s3.100 1 1
vconfig set_flag enp0s3.200 1 1

## 对 vlan 子接口配置网关 IP 并启用
ifconfig enp0s3.100 172.16.10.1 netmask 255.255.255.0 up
ifconfig enp0s3.200 172.16.20.1 netmask 255.255.255.0 up

2.6 设置IPtables

添加 iptables 规则,转发不同 VLAN 的数据包

 iptables -t nat -A POSTROUTING -o enp0s3.100 -j MASQUERADE
 iptables -t nat -A POSTROUTING -o enp0s3.200 -j MASQUERADE

 iptables -A FORWARD -i enp0s3.100 -o enp0s3.200 -m state --state RELATED,ESTABLISHED -j ACCEPT
 iptables -A FORWARD -i enp0s3.200 -o enp0s3.100 -m state --state RELATED,ESTABLISHED -j ACCEPT

 iptables -A FORWARD -i enp0s3.100 -o enp0s3.200 -j ACCEPT
 iptables -A FORWARD -i enp0s3.200 -o enp0s3.100 -j ACCEPT

这样之后再从 box1 ping box2 和 box4,就可以 ping 通了。

[root@tuling2 ~]# docker exec -it box1 ping 172.16.10.11
PING 172.16.10.11 (172.16.10.11): 56 data bytes
64 bytes from 172.16.10.11: seq=0 ttl=64 time=2.603 ms
^C
--- 172.16.10.11 ping statistics ---
1 packets transmitted, 1 packets received, 0% packet loss
round-trip min/avg/max = 2.603/2.603/2.603 ms
[root@tuling2 ~]# docker exec -it box1 ping 172.16.20.11
PING 172.16.20.11 (172.16.20.11): 56 data bytes
64 bytes from 172.16.20.11: seq=0 ttl=63 time=2.771 ms
64 bytes from 172.16.20.11: seq=1 ttl=63 time=0.833 ms
64 bytes from 172.16.20.11: seq=2 ttl=63 time=1.834 ms

下面我们分析数据包是如何从 box1(172.16.10.10)到达 box4(172.16.20.11)的。

我们在创建容器的时候指定了网关 172.16.10.1,所以数据包自然会被路由到 node1 的接口。看下 node1 的路由

[root@tuling1 ~]# route -n
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
0.0.0.0         192.168.1.1     0.0.0.0         UG    0      0        0 enp0s3
169.254.0.0     0.0.0.0         255.255.0.0     U     1002   0        0 enp0s3
172.16.10.0     0.0.0.0         255.255.255.0   U     0      0        0 enp0s3.100
172.16.20.0     0.0.0.0         255.255.255.0   U     0      0        0 enp0s3.200
172.17.0.0      0.0.0.0         255.255.0.0     U     0      0        0 docker0
192.168.1.0     0.0.0.0         255.255.255.0   U     0      0        0 enp0s3

可以看到,去往 172.16.10.0/24 网段的数据包会从 enp0s3.100 出去,同理 172.16.20.0/24 网段也是,再加上 node1 的 ip_forward 打开,这就打通了两个 macvlan 网络之间的通路。

总结

macvlan 是一种网卡虚拟化技术,能够将一张网卡虚拟出多张网卡。

在 Docker 中,macvlan 只支持 bridge 模式。

相同 macvlan 可以通信,不同 macvlan 二层无法通信,可以借助三层路由完成通信。

介绍

   macvlan 本身是 linxu kernel 模块,其功能是允许在同一个物理网卡上配置多个 MAC 地址,即多个 interface,每个 interface 可以配置自己的 IP。**macvlan 本质上是一种网卡虚拟化技术(最大优点是性能极好)**

  可以在linux命令行执行`lsmod | grep macvlan` 查看当前内核是否加载了该driver;如果没有查看到,可以通过`modprobe macvlan`来载入,然后重新查看。内核代码路径/drivers/net/macvlan.c

   Macvlan 允许你在主机的一个网络接口上配置多个虚拟的网络接口,这些网络 `interface` 有自己独立的 MAC 地址,也可以配置上 IP 地址进行通信。Macvlan 下的虚拟机或者容器网络和主机在同一个网段中,共享同一个广播域。Macvlan 和 `Bridge` 比较相似,但因为它省去了 Bridge 的存在,所以配置和调试起来比较简单,而且效率也相对高。除此之外,Macvlan 自身也完美支持 `VLAN`。

工作模式(Bridge VS MACVlan)

Bridge Mode

提示:

    **Bridge 有可能会遇到二层环路,如有需要,可以开启 STP 来防止出现环路。**

MACVlan Mode

MACVlan模式

前三种模式示意图:

image

实验

Bridge Mode:创建了两个macvlan接口,分别放到两个netns中;然后验证这两个macvlan口之间客户互通。

设置网卡混杂模式(PROMISC网卡混杂标志): [root@localhost ~]#ifconfig ens224 promisc[root@localhost ~]# ifconfig ens224 ens224: flags=4419<UP,BROADCAST,RUNNING,PROMISC,MULTICAST> mtu 1500 ………………

创建两个macvlan接口,其parent接口都是ens224

[root@localhost ~]# ip link add link ens224 name macv1 type macvlan mode bridge [root@localhost ~]# ip link add link ens224 name macv2 type macvlan mode bridge

查看接口状态:

image

创建namespace [root@localhost ~]# ip netns add net1 [root@localhost ~]# ip netns add net2

将macvlan接口插入到namespace [root@localhost ~]# ip link set macv1 netns net1 [root@localhost ~]# ip link set macv2 netns net2

设置网卡IP,设置网卡UP状态 ip netns exec net1 ip addr add 52.1.1.151/24 dev macv1 ip netns exec net2 ip addr add 52.1.1.152/24 dev macv2

设置网卡IP,设置网卡UP状态 [root@localhost ~]#ip netns exec net1 ip addr add 52.1.1.151/24 dev macv1[root@localhost ~]#ip netns exec net2 ip addr add 52.1.1.152/24 dev macv2 [root@localhost ~]#ip netns exec net1 ip link set macv1 up[root@localhost ~]#ip netns exec net2 ip link set macv2 up

查看网卡状态 [root@localhost ~]# ip netns exec net1 ip a 1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN group default qlen 1000 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 4: macv1@if3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UNKNOWN group default qlen 1000 link/ether 4e:a1:51:a9:3f:ef brd ff:ff:ff:ff:ff:ff link-netnsid 0 inet 52.1.1.151/24 scope global eth0 valid_lft forever preferred_lft forever [root@localhost ~]# ip netns exec net2 ip a 1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN group default qlen 1000 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 5: macv2@if3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UNKNOWN group default qlen 1000 link/ether fe:b5:85:c1:77:c1 brd ff:ff:ff:ff:ff:ff link-netnsid 0 inet 52.1.1.152/24 scope global macv2 valid_lft forever preferred_lft forever

ping测试结果: net1和net2无法ping通宿主机,ping自己也不通.2个容器互相ping没有问题

更改容器内网卡名字

ip netns exec net1 ip link set mac1 name eth0

ipvlan

IPVlan 和 macvlan 类似,都是从一个主机接口虚拟出多个虚拟网络接口。一个重要的区别就是所有的虚拟接口都有相同的 macv 地址,而拥有不同的 ip 地址。因为所有的虚拟接口要共享 mac 地址,所有有些需要注意的地方:

Ipvlan 是 linux kernel 比较新的特性,linux kernel 3.19 开始支持 ipvlan,但是比较稳定推荐的版本是 >=4.2(因为 docker 对之前版本的支持有 bug),具体代码见内核目录:/drivers/net/ipvlan/

ipvlan模式

L2:

 ipvlan L2 模式和 macvlan bridge 模式工作原理很相似,父接口作为交换机来转发子接口的数据。同一个网络的子接口可以通过父接口来转发数据,而如果想发送到其他网络,报文则会通过父接口的路由转发出去。

L3:

 ipvlan 有点像路由器的功能,它在各个虚拟网络和主机网络之间进行不同网络报文的路由转发工作。只要父接口相同,即使虚拟机/容器不在同一个网络,也可以互相 ping 通对方,因为 ipvlan 会在中间做报文的转发工作。

模式架构图:

image

实验

创建IPVlan L3模式 [root@localhost ~]#ip link add link ens224 ipvlan1 type ipvlan mode l3[root@localhost ~]#ip link add link ens224 ipvlan2 type ipvlan mode l3

注意看ipvlan1 和ipvlan2 的MAC地址跟ens224的一样 [root@localhost ~]# ip link 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 2: ens192: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP mode DEFAULT group default qlen 1000 link/ether 00:0c:29:05:18:ac brd ff:ff:ff:ff:ff:ff 3: ens224: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP mode DEFAULT group default qlen 1000 link/ether 00:0c:29:05:18:b6 brd ff:ff:ff:ff:ff:ff 4: ipvlan1@enp0s3: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN group default qlen 1000 link/ether 00:0c:29:05:18:b6 brd ff:ff:ff:ff:ff:ff 5: ipvlan2@enp0s3: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN group default qlen 1000 link/ether 00:0c:29:05:18:b6 brd ff:ff:ff:ff:ff:ff

创建ns绑定接口 [root@localhost ~]#ip net add net-1[root@localhost ~]#ip net add net-2 [root@localhost ~]#ip link set ipvlan1 netns net-1[root@localhost ~]#ip link set ipvlan2 netns net-2

配置IP [root@localhost ~]#ip net exec net-1 ip addr add 10.0.2.18/24 dev ipvlan1[root@localhost ~]#ip net exec net-2 ip addr add 10.0.3.19/24 dev ipvlan2 [root@localhost ~]#ip net exec net-1 ip link set ipvlan1 up[root@localhost ~]#ip net exec net-2 ip link set ipvlan2 up

增加路由 [root@localhost ~]#ip net exec net-1 route add default dev ipvlan1[root@localhost ~]#ip net exec net-2 route add default dev ipvlan2

ping测试 2个ns可以正常互相ping通,无法ping通宿主机IP

抓取ARP报文,结果无法在L3模式中抓到ARP,说明二层广播和组播都不处理,工作在L3.(这就是和L2模式的区别) [root@localhost ~]#ip net exec net-1 tcpdump -ni ipvlan1 -p arp

创建L2模式,其余操作跟L3一样 ip link add link enp0s3 ipvlan1 type ipvlan mode l2 ip link add link enp0s3 ipvlan2 type ipvlan mode l2

区别在于L2可以在2个ns中抓取到ARP报文

总结: ipvlan L3模式中外部网络默认情况下是不知道 ipvlan 虚拟出来的网络的,如果不在外部路由器上配置好对应的路由规则,ipvlan 的网络是不能被外部直接访问的。

问题介绍

最近在做将项目放入docker容器中启动,运行。项目中需要开启DHCP服务,不运行不知道,一运行就发现问题了,由于docker容器和其宿主机不在一个广播域内,所以在容器中启动的DHCP服务收不到宿主机局域网内的广播报文,就无法正常进行DHCP服务的工作

方案

通过查阅资料和官方文档,发现macvlan这种网络模式可以是容器直接连接到物理网络,解决我们的问题。但是官网同时说了这种模式只支持Linux系统下的docker,对于windows和Mac系统下不支持。但是我是需要运行在windows系统下的,所以我采用的方式是在windows下安装一个Linux虚拟机,然后在虚拟机中运行docker。 官网链接:https://docs.docker.com/network/network-tutorial-macvlan/

步骤

1.开启虚拟机网卡的混杂模式(对于直接在Linux系统下的不需要此步骤) 在VirtualBox下选择安装的虚拟机,点击设置,选择左侧网络菜单,选择网卡进行如下配置。

连接方式:连接方式需要选择桥接网卡,使虚拟机和windows宿主机在同一局域网内 界面名称:在windows cmd窗口执行ipconfig /all ,查看对应物理网卡的描述,在界面名称选择需要桥接的物理网卡 混杂模式:选择全部允许(如果在启动后修改混杂模式,建议进行重启后尝试;我在虚拟机启动中修改混杂模式为拒绝后,发现ping立马不通,再改回全部允许也ping不通,需要重启)

在这里插入图片描述 在这里插入图片描述

2.设置Linux系统网卡开启混杂模式 在终端输入ip link set enp0s8 promisc on 开启网卡接口enp0s8的混杂模式,enp0s8根据实际配置网卡填写 在终端输入ifconfig查看是否开启混杂模式,显示有PROMISC即为混杂模式已开启 在这里插入图片描述 3.创建docker network,启动容器

docker network create -d macvlan --subnet=192.168.50.0/24 --gateway=192.168.50.1 -o parent=enp0s8 mymacvlan

–subnet 填写enp0s8网卡网段 parent= 填写需要连接到宿主机物理网络的对应网卡 在这里插入图片描述 执行命令 docker run -itd --network=mymacvlan --ip=192.168.50.20 --name=centos-test centos 进行启动容器 –network:指定为上面创建的网络 –ip:指定IP地址,如果不指定的话会自动分配。建议自己指定,可避免IP地址冲突问题 启动完成后,通过docker exec 进入容器中,进行操作,查看IP地址,ping windows,使用tcpdump抓包进行查看能否收到DHCP广播包。发现能ping通windows和与windows相通的机器,能抓包到DHCP包。大功告成。 容器和Linux宿主机是不能ping通的 在这里插入图片描述 至此,就已经完成了需要容器和其宿主机某一网卡在同一局域网内,可以接收宿主机的物理网络啦。在使用此种方式启动时,可以不用映射端口,可以直接使用容器IP+端口进行访问了。