linux macvlan 网卡虚拟化技术

Macvlan

Macvlan 本身是 linxu kernel 模块

Macvlan 接口是物理以太网接口的虚拟子接口,Macvlan 允许用户在一个物理网络接口上面配置多个虚拟的网络接口,每个 Macvlan 接口都有自己的区别与父接口的 MAC 地址,并且可以像普通的物理网络接口一样分配 IP 地址。使用 Macvlan 技术实现的效果是一块物理网卡可以绑定多个 IP 地址,并且每个 IP 地址有自己独立的 MAC 地址。

Macvlan 虚拟出来的虚拟网卡,在逻辑上和物理网卡是对等的。使用 Macvlan 的虚拟网卡要和父接口在同一个网段。

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

为保证父接口能接收多个不同 MAC 地址的网络包,需要开启网卡的 混杂模式

docker 中使用 Macvlan 虚拟网卡

本示例演示 docker 环境中使用 macvlan。首先创建使用 macvlan 驱动的 network,Docker 中 macvlan 只支持 bridge 模式 [1]

$ docker network create -d macvlan --subnet=192.168.142.0/24 \
--gateway=192.168.142.2 \
-o parent=ens33 \
macvlan1

$ docker network ls
NETWORK ID NAME DRIVER SCOPE
69324d203c35 bridge bridge local
f8943f720d73 host host local
0aa95ac8c0f4 macvlan1 macvlan local
d400c40efdc5 none null local

在 docker 环境中,创建网络时,会自动将宿主机网卡设置为混杂模式,此时查看网卡信息,未显示混杂模式,但是查看 dmesg 日志,会看到网卡进入了混杂模式

$ dmesg
[93203.228311] device ens33 entered promiscuous mode

运行容器并连接到新建的 macvlan 网络 macvlan1

docker run -itd --name test01 \
--ip=192.168.142.12 \
--network macvlan1 centos:centos7.9.2009

使用命令 docker exec -it test01 bash 进入容器查看容器的 IP 地址信息,可以看到容器中的网卡类型为 macvlan,模式为 bridge,网关为 docker 网络 macvlan1 中配置的网关。

$ ip -d add
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 promiscuity 0 numtxqueues 1 numrxqueues 1 gso_max_size 65536 gso_max_segs 65535
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
4: eth0@if2: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether 02:42:c0:a8:8e:0c brd ff:ff:ff:ff:ff:ff link-netnsid 0 promiscuity 0
macvlan mode bridge numtxqueues 1 numrxqueues 1 gso_max_size 65536 gso_max_segs 65535
inet 192.168.142.12/24 brd 192.168.142.255 scope global eth0
valid_lft forever preferred_lft forever

$ route -n
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
0.0.0.0 192.168.142.2 0.0.0.0 UG 0 0 0 eth0
192.168.142.0 0.0.0.0 255.255.255.0 U 0 0 0 eth0

测试容器可以和宿主机网络一样访问外网。

此时检查宿主机网卡信息,系统上只有 loens33docker0,未出现其他网卡。

$ ip -d 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 promiscuity 0 addrgenmode eui64 numtxqueues 1 numrxqueues 1 gso_max_size 65536 gso_max_segs 65535
2: ens33: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP mode DEFAULT group default qlen 1000
link/ether 00:0c:29:90:51:eb brd ff:ff:ff:ff:ff:ff promiscuity 1 addrgenmode eui64 numtxqueues 1 numrxqueues 1 gso_max_size 65536 gso_max_segs 65535
3: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN mode DEFAULT group default
link/ether 02:42:e9:a6:76:56 brd ff:ff:ff:ff:ff:ff promiscuity 0
bridge forward_delay 1500 hello_time 200 max_age 2000 ageing_time 30000 stp_state 0 priority 32768 vlan_filtering 0 vlan_protocol 802.1Q bridge_id 8000.2:42:e9:a6:76:56 designated_root 8000.2:42:e9:a6:76:56 root_port 0 root_path_cost 0 topology_change 0 topology_change_detected 0 hello_timer 0.00 tcn_timer 0.00 topology_change_timer 0.00 gc_timer 265.03 vlan_default_pvid 1 vlan_stats_enabled 0 group_fwd_mask 0 group_address 01:80:c2:00:00:00 mcast_snooping 1 mcast_router 1 mcast_query_use_ifaddr 0 mcast_querier 0 mcast_hash_elasticity 16 mcast_hash_max 4096 mcast_last_member_count 2 mcast_startup_query_count 2 mcast_last_member_interval 100 mcast_membership_interval 26000 mcast_querier_interval 25500 mcast_query_interval 12500 mcast_query_response_interval 1000 mcast_startup_query_interval 3125 mcast_stats_enabled 0 mcast_igmp_version 2 mcast_mld_version 1 nf_call_iptables 0 nf_call_ip6tables 0 nf_call_arptables 0 addrgenmode eui64 numtxqueues 1 numrxqueues 1 gso_max_size 65536 gso_max_segs 65535

在另一个 docker 节点上同样配置 docker 网络和容器

$ docker network create -d macvlan --subnet=192.168.142.0/24 \
--gateway=192.168.142.2 \
-o parent=ens33 \
macvlan1

$ docker run -itd --name test01 \
--ip=192.168.142.13 \
--network macvlan1 centos:centos7.9.2009

进入容器 test01 ,访问到另一个节点上的容器的连通性

$ ping 192.168.142.12
PING 192.168.142.12 (192.168.142.12) 56(84) bytes of data.
64 bytes from 192.168.142.12: icmp_seq=1 ttl=64 time=0.871 ms
64 bytes from 192.168.142.12: icmp_seq=2 ttl=64 time=0.403 ms
64 bytes from 192.168.142.12: icmp_seq=3 ttl=64 time=0.568 ms
^C
--- 192.168.142.12 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2047ms
rtt min/avg/max/mdev = 0.403/0.614/0.871/0.193 ms

进入容器 test01 ,测试和宿主机 ip 的连通性,结果发现不通,原因为:在 macvlan 虚拟网络中,父接口(物理网卡)相当于一个交换机,对于其子 macvlan 网卡的数据包,只进行转发而不处理,于是造成了使用本机 macvlan 网卡的虚拟 IP 无法和本机物理网卡的 IP 通信。

$ ping 192.168.142.10
PING 192.168.142.10 (192.168.142.10) 56(84) bytes of data.
From 192.168.142.13 icmp_seq=1 Destination Host Unreachable
From 192.168.142.13 icmp_seq=2 Destination Host Unreachable
From 192.168.142.13 icmp_seq=3 Destination Host Unreachable
^C
--- 192.168.142.10 ping statistics ---
5 packets transmitted, 0 received, +3 errors, 100% packet loss, time 4073ms
pipe 4

macvlan 网络存的缺点

  • macvlan 会独占主机的物理网卡,也就是说一个物理网卡只能创建一个 macvlan 网络
  • 使用本机 macvlan 网卡的虚拟 IP 无法和本机物理网卡的 IP 通信
  • 父接口异常,其他所有的虚拟子接口的网络都会异常

脚注