一、macvlan介绍
macvlan 本身是 linxu kernel 模块,其功能是允许在同一个物理网卡上配置多个 MAC 地址而实现虚拟多块网卡,即多个 interface,每个 interface 可以配置自己的IP。macvlan 本质上是一种网卡虚拟化技术。
macvlan 的最大优点是性能极好,相比其他实现,macvlan不需要创建Linux bridge,而是直接通过以太interface连接到物理网络。
二、准备实验环境
使用hosts1 和 hosts2上单独的网卡ens33 创建macvlan。
1、为保证多个 MAC 地址的网络包都可以从 ens33 通过,我们需要打开物理网卡的混杂模式:
ip link set ens33 promisc on
或者
ens33 开启混杂模式ifconfig ens33 promisc
ens33 关闭混杂模式ifconfig ens33 -promisc
2、macvlan 是 Linux 内核提供的一种网络驱动类型。如果内核没有加载 macvlan,可以通过命令加载:
modprobe macvlan # 加载macvlan;可用于判断系统是否支持macvlan;若要卸载 macvlan:modprobe -r macvlan
lsmod | grep macvlan # 确认是否已加载
如果第一个命令报错,或者第二个命令没有返回,说明当前系统不支持 macvlan,需要升级内核。
三、相同 macvlan 网络之间的通信
1、首先使用 docker network create 分别在两台主机上创建两个 macvlan 网络:
sudo docker network create --driver macvlan --subnet 172.16.10.0/24 --gateway 172.16.10.1 -o parent=ens33 macvlan_10 # 为了简单设置的网络与宿主机的网络为同一网段
-
--driver:指定 Docker 网络 driver;
-
--subnet:macvlan网络是local网络,为了保证跨主机能够通信,用户需要自己管理 IP subnet;
-
--gateway 与其他网络不同,docker不会为macvlan 创建网关,这里的网关应该是真实存在的,否则容器无法路由。
-
-o parent 指定用来分配 macvlan 网络的物理网卡
docker network ls查看创建的macvlan网络:
2、在两台宿主机上分别增加一个容器
由于node1中的macvlan_10与node2中的macvlan_10本质上是独立的,为了避免自动分配造成 IP 冲突,最好通过 --ip 指定地址。
node1上运行容器,指定IP为172.16.10.2:
node2上运行容器,指定IP为172.16.10.3:
1、macvlan是不依赖Linux bridge的,创建好macvlan网络之后,通过brctl show可以确认没有创建新的网桥。
2、docker 没有为 macvlan 提供 DNS 服务,这点与 overlay 网络是不同的。
3、容器内的eth0是由macvlan所在物理接口ens33创建的一个逻辑网口,解释如下:
容器内,除了lo,容器只有一个eth0,eth0后面有个if2,这说明该接口有个对应的interface,全局编号为2,而在主机上执行ip link可以看到,ens33的编号也是2:
4、容器直接使用宿主机的网卡,性能较好,这种方案使得容器无需通过NAT和端口映射就能与外网相通(只要有网关),在网络上与其他独立主机没有区别。
5、需要注意的是,通过MacVLAN创建的网络,docker容器内的IP是不能与parent的网卡互通的。因为 macvlan 网络会独占物理网卡,即一张物理网卡只能创建一个 macvlan 网络,如果想创建多个 macvlan 网络就得用多张网卡,但主机的物理网卡是有限的,这个时候需要用到VLAN子接口的功能实现。
四、不同 macvlan 网络之间的通信
由于 macvlan 网络会独占物理网卡,也就是说
一张物理网卡只能创建一个 macvlan 网络,如果我们想创建多个 macvlan 网络就得用多张网卡,但主机的物理网卡是有限的,怎么办呢?
可以
通过 VLAN 技术将一个网口划分出多个子网口,这样就可以基于子网口来创建 macvlan 网络了。
下面是具体的创建过程
1、首先分别在两台主机上将物理网口 ens33创建出两个 VLAN 子接口。
# 使用 vconfig 命令在 ens33配置两个 VLAN
sudo vconfig add ens33 100 # 使用vconfig需要执行sudo apt install vlan
sudo vconfig add ens33 200
# 设置 VLAN 的 REORDER_HDR 参数,默认就行了
sudo vconfig set_flag ens33.100 1 1
sudo vconfig set_flag ens33.200 1 1
# 启用接口
sudo ifconfig ens33.100 up
sudo ifconfig ens33.200 up
ifconfig查看:
2、分别在 node1 和 node2 上基于两个 VLAN 子接口创建 2 个 macvlan 网络,mac100 和 mac200。
docker network create -d macvlan --subnet=172.16.100.0/24 --gateway=172.16.100.1 -o parent=ens33.100 mac100
docker network create -d macvlan --subnet=172.16.200.0/24 --gateway=172.16.200.1 -o parent=ens33.200 mac200
3、分别在 node1 和 node2 上运行容器,并指定不同的 macvlan 网络
# node1
root@node1:~$ docker run -itd --name d1 --ip=172.16.100.10 --network mac100 busybox
root@node1:~$ docker run -itd --name d2 --ip=172.16.200.10 --network mac200 busybox
# node2
zhang@node2:~$ docker run -itd --name d3 --ip=172.16.100.11 --network mac100 busybox
zhang@node2:~$ docker run -itd --name d4 --ip=172.16.200.11 --network mac200 busybox
通过验证,d1 和 d3,d2 和 d4 在同一 macvlan 网络下,互相可以 ping 通,d1 和 d2,d1 和 d4 在不同的 macvlan 网络下,互相 ping 不通。
初始测试,d1与d3不通,d2和d4不通。
后将测试机器改为桥接模式:
d1可以ping通d3,但ping不通d2:
d2可以ping通d4:
这个原因也很明确,不同 macvlan 网络处于不同的网络,而且通过 VLAN 隔离,自然 ping 不了。
但这也只是在二层上通不了,通过三层的路由是可以通的,我们这就来验证下。
重新找一台主机node3,通过打开 ip_forward 把它改造成一台虚拟路由器,用来打通两个 macvlan 网络,大概的图示如下所示:
1 首先对 node3 执行 vi /etc/sysctl.d/99-sysctl.conf net.ipv4.ip_forward=1 sysctl -p开路由开关。
2 然后创建两个 VLAN 子接口,一个作为 macvlan 网络 mac10 的网关,一个作为 mac20 的网关。
# 使用 vconfig 命令在 ens33配置两个 VLAN
[root@node3~]~$ vconfig add ens33 100
[root@node3 ~]~$ vconfig add ens33 200
# 设置 VLAN 的 REORDER_HDR 参数,默认就行了
[root@node3 ~]~$ vconfig set_flag ens33.100 1 1
[root@node3 ~]~$ vconfig set_flag ens33.200 1 1
# 对 vlan 子接口配置网关 IP 并启用
[root@node3 ~]~$ ifconfig ens33.100 172.16.100.1 netmask 255.255.255.0 up
[root@node3 ~]~$ ifconfig ens33.200 172.16.200.1 netmask 255.255.255.0 up
3、此时,d1还是ping不通d4
4、node3上添加iptable规则,转发不通VLAN的数据。
iptables-save查看规则:
添加规则:
iptables -t nat -A POSTROUTING -o ens33.100 -j MASQUERADE
iptables -t nat -A POSTROUTING -o ens33.200 -j MASQUERADE
iptables -A FORWARD -i ens33.100 -o ens33.200 -m state --state RELATED,ESTABLISHED -j ACCEPT
iptables -A FORWARD -i ens33.200 -o ens33.100 -m state --state RELATED,ESTABLISHED -j ACCEPT
iptables -A FORWARD -i ens33.100 -o ens33.200 -j ACCEPT
iptables -A FORWARD -i ens33.200 -o ens33.100 -j ACCEPT
再查看ipatbles规则:
5、此时,d1可以ping通d4,也可以ping通d2。
6、分析数据包是如何从d1(172.16.100.10)到达d4(172.16.200.11)
1、因为d1与d4在不同的IP网段,根据d1的路由表:
数据包将发往网关172.16.100.1.
2、路由器从ens33.100收到数据包,发现目的地址是172.16.200.11,查看自己的路由表:
于是将数据包从ens33.200转发出去。
3、通过ARP记录的信息,路由器能够得知172.16.200.11在host2上,于是将数据包发往hosts2.
4、hosts3根据目的地址和VLAN信息将数据包发往d4。
五、总结
macvlan 是一种网卡虚拟化技术,能够将一张网卡虚拟出多张网卡。
macvlan网络的连通与隔离完依赖VLAN、IP subnet和路由,docker本身不做任何限制,用户可以像管理传统VLAN网络那样管理macvlan。
macvlan 的四种通信模式,常用模式是 bridge。
在 Docker 中,macvlan 只支持 bridge 模式。
相同 macvlan 可以通信,不同 macvlan 二层无法通信,可以借助三层路由完成通信。
六、参考
macvlan 网络结构分析 - 每天5分钟玩转 Docker 容器技术(56) (qq.com)
Docker学习笔记(五)Docker跨主机网络–macvlan方案 | 码农家园 (codenong.com)