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
这条命令中,
-d
指定 Docker 网络 driver--subnet
指定 macvlan 网络所在的网络--gateway
指定网关-o parent
指定用来分配 macvlan 网络的物理网卡
之后可以看到当前主机的网络环境,其中出现了 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
这条命令中,
--ip
指定容器 busybox1使用的 IP,这样做的目的是防止自动分配,造成 IP 冲突--network
指定 macvlan 网络
同样在 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 二层无法通信,可以借助三层路由完成通信。