macvlan 本身是 linux kernel 模块,其功能是允许在同一个物理网卡上配置多个 MAC 地址,即多个 interface,每个 interface 可以配置自己的 IP。

macvlan 本质上是一种网卡虚拟化技术,Docker 用 macvlan 实现容器网络。

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

1、准备实验环境

创建一个 macvlan 网络。

docker(7、容器网络3) macvlan 网络

使用 host1 和 host2 上再添加单独的网卡 ens37 创建 macvlan

docker(7、容器网络3) macvlan 网络

为保证多个 MAC 地址的网络包都可以从 ens192 通过,需要打开网卡的混杂模式。

ip link set ens37 promisc on

ip link show ens37

docker(7、容器网络3) macvlan 网络

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

 如果 host1 和 host2 是 vwmare 虚拟机,还需要在网卡配置选项页中设置混杂模式

docker(7、容器网络3) macvlan 网络

2、创建 macvlan 网络

在 host1 和 host2 中创建 macvlan 网络 mac_net1:

docker network create -d macvlan --subnet=172.16.86.0/24 --gateway=172.16.86.1 -o parent=ens37 mac_net1

 docker(7、容器网络3) macvlan 网络

注意:在 host2 中也要执行相同的命令。

1) -d macvlan 指定 driver 为 macvlan。

2) macvlan 网络是 local 网络,为了保证跨主机能够通信,用户需要自己管理 IP subnet。

3) 与其他网络不同,docker 不会为 macvlan 创建网关,这里的网关应该是真实存在的,否则容器无法路由。

4) -o parent 指定使用的网络 interface。

在 host1 中运行容器 bbox1 并连接到 mac_net1。

由于 host1 中的 mac_net1 与 host2 中的 mac_net1 本质上是独立的,为了避免自动分配造成 IP 冲突,我们最好通过 --ip 指定 bbox1 地址为 172.16.86.10。

docker run -itd --name bbox1 --ip=172.16.86.10 --network mac_net1 busybox docker(7、容器网络3) macvlan 网络

在 host2 中运行容器 bbox2,指定 IP 172.16.86.11。

docker run -itd --name  bbox2 --ip=172.16.86.11 --network mac_net1 busybox

docker(7、容器网络3) macvlan 网络

验证 bbox1 和 bbox1 的连通性。

docker(7、容器网络3) macvlan 网络

bbox2 能够 ping 到 bbox1 的 IP 172.16.86.10,但无法解析 “bbox1” 主机名。

docker(7、容器网络3) macvlan 网络

可见 docker 没有为 macvlan 提供 DNS 服务,这点与 overlay 网络是不同的。

3、macvlan 网络结构分析

macvlan 不依赖 Linux bridge,brctl show 可以确认没有创建新的 bridge。

docker(7、容器网络3) macvlan 网络

查看一下容器 bbox1 的网络设备:

docker(7、容器网络3) macvlan 网络

除了 lo,容器只有一个 eth0,请注意 eth0 后面的 @if16,这表明该 interface 有一个对应的 interface,其全局的编号为 16。

根据 macvlan 的原理,我们有理由猜测这个 interface 就是主机的 ens37,确认如下:

docker(7、容器网络3) macvlan 网络

由此可见,容器的 eth0 就是 ens37 通过 macvlan 虚拟出来的 interface。

容器的 interface 直接与主机的网卡连接,这种方案使得容器无需通过 NAT 和端口映射就能与外网直接通信(只要有网关),在网络上与其他独立主机没有区别。

当前网络结构如图所示

docker(7、容器网络3) macvlan 网络

 

4、用 sub-interface 实现多 macvlan 网络(另外的环境的测试网卡是ens192)

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

docker(7、容器网络3) macvlan 网络

但主机的网卡数量是有限的,如何支持更多的 macvlan 网络呢?

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

VLAN 是现代网络常用的网络虚拟化技术,它可以将物理的二层网络划分成多达 4094 个逻辑网络,这些逻辑网络在二层上是隔离的,每个逻辑网络(即 VLAN)由 VLAN ID 区分,VLAN ID 的取值为 1-4094。

Linux 的网卡也能支持 VLAN(apt-get install vlan),同一个 interface 可以收发多个 VLAN 的数据包,不过前提是要创建 VLAN 的 sub-interface。

比如希望 ens192 同时支持 VLAN10 和 VLAN20,则需创建 sub-interface ens192.10 和 ens192.20。

在交换机上,如果某个 port 只能收发单个 VLAN 的数据,该 port 为 Access 模式,如果支持多 VLAN,则为 Trunk 模式,所以接下来实验的前提是:

ens192 要接在交换机的 trunk 口上。如果是虚拟机,则不需要额外配置了。

在 ens192.10 和 ens192.20 上创建 macvlan 网络。

首先编辑 host1 和 host2 的 /etc/network/interfaces,配置 sub-interfaces:

docker(7、容器网络3) macvlan 网络

 

然后启用 sub-interface:

docker(7、容器网络3) macvlan 网络

创建 macvlan 网络:

host1上:

docker network create -d macvlan --subnet=172.16.10.0/24 --gateway=172.16.10.1 -o parent=ens192.10 mac_net10

docker network create -d macvlan --subnet=172.16.20.0/24 --gateway=172.16.20.1 -o parent=ens192.20 mac_net20

docker(7、容器网络3) macvlan 网络

host2上用同样方法创建macvlan网络。

在 host1 中运行容器:

 docker(7、容器网络3) macvlan 网络

在 host2 中运行容器:

 docker(7、容器网络3) macvlan 网络

当前网络结构如图所示:

docker(7、容器网络3) macvlan 网络

5、macvlan 网络隔离和连通

验证 macvlan 之间的连通性。

docker(7、容器网络3) macvlan 网络

docker(7、容器网络3) macvlan 网络

bbox1 能 ping 通 bbox3,bbox2 能 ping 通 bbox4。

即:同一 macvlan 网络能通信。

docker(7、容器网络3) macvlan 网络

docker(7、容器网络3) macvlan 网络

bbox1 无法 ping 通 bbox2 和 bbox4。

即:不同 macvlan 网络之间不能通信。

但更准确的说法应该是:不同 macvlan 网络不能 在二层上 通信。

在三层上可以通过网关将 macvlan 连通,下面我们就启用网关。

将 额外的doceker-machine配置成一个虚拟路由器,设置网关并转发 VLAN10 和 VLAN20 的流量。

当然也可以使用物理路由器达到同样的效果。首先确保操作系统 IP Forwarding 已经启用。

输出为 1 则表示启用,如果为 0 可通过如下命令启用:

sysctl -w net.ipv4.ip_forward=1

docker(7、容器网络3) macvlan 网络

 

在 /etc/network/interfaces 中配置 vlan sub-interface:

docker(7、容器网络3) macvlan 网络

启用 sub-interface:

ifup ens192.10

ifup ens192.20

docker(7、容器网络3) macvlan 网络

将网关 IP 配置到 sub-interface:

[[email protected] ~]$ifconfig ens192.10 172.16.10.1 netmask 255.255.255.0 up

[[email protected] ~]$ifconfig ens192.20 172.16.20.1 netmask 255.255.255.0 up

 

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

iptables -t nat -A POSTROUTING -o ens192.10 -j MASQUERADE

iptables -t nat -A POSTROUTING -o ens192.20 -j MASQUERADE

iptables -A FORWARD -i ens192.10 -o ens192.20 -m state --state RELATED,ESTABLISHED -j ACCEPT

iptables -A FORWARD -i ens192.20 -o ens192.10 -m state --state RELATED,ESTABLISHED -j ACCEPT

iptables -A FORWARD -i ens192.10 -o ens192.20 -j ACCEPT

iptables -A FORWARD -i ens192.20 -o ens192.10 -j ACCEPT

 

当前网络拓扑如下图所示:

docker(7、容器网络3) macvlan 网络

现在 host1 上位于 mac_net10 的 bbox1 已经可以与 host2 上位于 mac_net20 的 bbox4 通信了。

docker(7、容器网络3) macvlan 网络

下面我们分析数据包是如何从 bbox1(172.16.10.10)到达 bbox4(172.16.20.11)的。整个过程如下图所示:

docker(7、容器网络3) macvlan 网络

1) 因为 bbox1 与 bbox4 在不同的 IP 网段,跟据 bbox1 的路由表:

docker(7、容器网络3) macvlan 网络

数据包将发送到网关 172.16.10.1。

2) 路由器从 ens192.10 收到数据包,发现目的地址是 172.16.20.11,查看自己的路由表:

docker(7、容器网络3) macvlan 网络

于是将数据包从 ens192.20 转发出去。

3) 通过 ARP 记录的信息,路由器能够得知 172.16.20.11 在 host2 上,于是将数据包发送给 host2。

4) host2 根据目的地址和 VLAN 信息将数据包发送给 bbox4。

macvlan 网络的连通和隔离完全依赖 VLAN、IP subnet 和路由,docker 本身不做任何限制,用户可以像管理传统 VLAN 网络那样管理 macvlan。

 

 

相关文章: