关于 vxlan 的详细介绍,可以参考 VXLAN 解读
最近在研究 docker k8s 相关的一些东西,flannel 需要了解到 vxlan 相关知识,下面通过两个简单的实验来了解一下 vxlan 在 linux 中是如何工作的。
实验环境
- OS: Ubuntu 18.04
- Docker 19.08
- Kernel: 4.15
创建两台 VM,物理网卡使用 ens3 进行通信,IP 为:
- 172.16.3.219
- 172.16.3.203
而要创建的 overlay 网络网段为 10.100.1.0/24,实验目的就是 vxlan 能够通过 overlay IP 互相连通。
快速搭建和理解VXLAN的方法之一就是利用Linux。从内核3.7版本开始,Linux就开始支持VXLAN。到了内核3.12版本,Linux对VXLAN的支持已经完备,支持单播和组播,IPv4和IPv6。利用man查看ip的link子命令,可以查看是否有vxlan type,如下:
man ip-link
可以看到,在支持的协议中,有 vxlan。
TYPE := { vlan | veth | vcan | vxcan | dummy | ifb | macvlan | macvtap |
bridge | bond | team | ipoib | ip6tnl | ipip | sit | vxlan |
gre | gretap | erspan | ip6gre | ip6gretap | ip6erspan |
vti | nlmon | team_slave | bond_slave | ipvlan | geneve |
bridge_slave | vrf | macsec }
实验
点对点的 vxlan
我们先来搭建一个最简单的 vxlan 网络,两台机器构成一个 vxlan 网络,每台机器上有一个 vtep,vtep 通过它们的 IP 互相通信。这次实验完成后的网络结构如下图所示:
先用 ip 命令创建一个 vxlan 接口 vxlan0
ip link add vxlan0 type vxlan \
id 1 \
dstport 4789 \
remote 172.16.3.203 \
local 172.16.3.219 \
dev ens3
上面这条命令创建一个名字为 vxlan0,类型为 vxlan 的网络 interface,后面是 vxlan interface 需要的参数:
id 1:指定 VNI 的值,这个值可以在 1 到 2^24 之间dstport:vtep 通信的端口,linux 默认使用 8472(为了保持兼容,默认值一直没有更改),而 IANA 分配的端口是 4789,所以我们这里显式指定了它的值remote 172.16.3.203:对方 vtep 的地址,类似于点对点协议local 172.16.3.219:当前节点 vtep 要使用的 IP 地址dev ens3:当节点用于 vtep 通信的网卡设备,用来读取 IP 地址。注意这个参数和local参数含义是相同的,在这里写出来是为了告诉大家有两个参数存在
执行完之后,系统就会创建一个名字为 vxlan0 的 interface,可以用 ip -d link 查看它的详细信息:
root@henryxzx:~# ip -d link show dev vxlan0
11: vxlan0: <BROADCAST,MULTICAST> mtu 1450 qdisc noop state DOWN mode DEFAULT group default qlen 1000
link/ether 16:bb:ae:5d:cc:08 brd ff:ff:ff:ff:ff:ff promiscuity 0
vxlan id 1 remote 172.16.3.203 local 172.16.3.219 dev ens3 srcport 0 0 dstport 4789 ttl inherit ageing 300 udpcsum noudp6zerocsumtx noudp6zerocsumrx addrgenmode eui64 numtxqueues 1 numrxqueues 1 gso_max_size 65536 gso_max_segs 65535
接下来,为刚创建的 interface 配置 IP 地址并启用它:
ip addr add 10.100.1.2/24 dev vxlan0
ip link set vxlan0 up
执行结束之后,会发现路由表项多了下面的内容,所有 10.100.1.0/24 网段的 IP 地址要通过 vxlan0 来转发:
root@henryxzx:~# ip route get 10.100.1.0
broadcast 10.100.1.0 dev vxlan0 src 10.100.1.2 uid 0
cache <local,brd>
同时,vxlan0 fdb 表项中的内容如下:
root@henryxzx:~# bridge fdb | grep vxlan0
00:00:00:00:00:00 dev vxlan0 dst 172.16.3.203 via ens3 self permanent
这个表项的意思是说,默认的而 vtep 对端地址为 172.16.3.203 ,如果接收到的报文添加上 vxlan 头部之后,都会发到 172.16.3.203 。
在另外一台虚拟机(172.16.3.203)上也进行相同的配置,要保证 VNI 相同,dstport 也是 4789,并修改 vtep 的地址和 remote IP 地址到相应的值。测试两台 vtep 的连通性:
root@henryxzx:~# ping 10.100.1.3
PING 10.100.1.3 (10.100.1.3) 56(84) bytes of data.
64 bytes from 10.100.1.3: icmp_seq=1 ttl=64 time=0.286 ms
64 bytes from 10.100.1.3: icmp_seq=2 ttl=64 time=0.179 ms
64 bytes from 10.100.1.3: icmp_seq=3 ttl=64 time=0.180 ms
^C
--- 10.100.1.3 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2034ms
rtt min/avg/max/mdev = 0.179/0.215/0.286/0.050 ms
这种点对点的 vxlan 网络只能两两通信,实际用处不大。下面我们介绍多节点怎么组成 vxlan 网络进行通信。
下面是从 ens3 接口抓取到的报文:
多播模式的 vxlan
如果 vxlan 要使用多播模式,那么底层的网络结构需要支持多播的功能,在本实验中,因为只有两台 VM,又处于同一子网内,所以不需要其他配置。
要组成同一个 vxlan 网络,vtep 必须能感知到彼此的存在。多播组本来的功能就是把网络中的某些节点组成一个虚拟的组,所以 vxlan 最初想到用多播来实现是很自然的事情。
这个实验和前面一个非常相似,只不过主机之间不是点对点的连接,而是通过多播组成一个虚拟的整体。最终的网络架构也很相似(为了简单图中只有两个主机,但这个模型可以容纳多个主机组成 vxlan 网络):
在两台 VM 执行以下命令:
ip link add vxlan0 type vxlan \
id 1 \
dstport 4789 \
group 239.1.1.1 \
dev ens3
ip addr add 10.100.1.2/24 dev vxlan0
ip link set vxlan0 up
查看 fdb 表项
root@henryxzx:~# bridge fdb | grep vxlan
00:00:00:00:00:00 dev vxlan0 dst 239.1.1.1 via ens3 self permanent
在配置完成之后,vtep 通过 IGMP 加入同一个多播网络 239.1.1.1。
- 发送 ping 报文到 10.100.1.3,查看路由表,报文会从 vxlan0 发出去
- 内核发现 vxlan0 的 IP 是 10.100.1.2/24,和目的 IP 在同一个网段,所以在同一个局域网,需要知道对方的 MAC 地址,因此会发送 ARP 报文查询
- ARP 报文源 MAC 地址为 vxlan0 的 MAC 地址,目的 MAC 地址为全 1 的广播地址
- vxlan 根据配置(VNI 1)添加上头部
- 因为不知道对方 vtep 在哪台主机上,根据配置,vtep 会往多播地址 239.1.1.1 发送多播报文
- 多播组中所有的主机都会受到这个报文,内核发现是 vxlan 报文,会根据 VNI 发送给对应的 vtep
- vtep 去掉 vxlan 头部,取出真正的 ARP 请求报文。同时 vtep 会记录
<源 MAC 地址 - vtep 所在主机 IP 地址>信息到 fdb 表中 - 如果发现 ARP 不是发送给自己的,直接丢弃;如果是发送给自己的,则生成 ARP 应答报文
- 应答报文目的 MAC 地址是发送方 vtep 的 MAC 地址,而且 vtep 已经通过源报文学习到了 vtep 所在的主机,因此会直接单播发送给目的 vtep。因此 vtep 不需要多播,就能填充所有的头部信息
- 应答报文通过 underlay 网络直接返回给发送方主机,发送方主机根据 VNI 把报文转发给 vtep,vtep 解包取出 ARP 应答报文,添加 arp 缓存到内核。并根据报文学习到目的 vtep 所在的主机地址,添加到 fdb 表中
- vtep 已经知道了通信需要的所有信息,后续 ICMP 的 ping 报文都是单播进行的
在这个过程中,在主机上抓包更容易看到通信的具体情况,下面是 ARP 请求报文的详情:
从上面的通信过程,可以看出不少信息:
- 通信报文没有任何区别,除了因为 MTU 导致有些报文比较小
- 然后是 vxlan 头部,我们最关心的字段 VNI 确实是 1
- 最外层(图中最上面)是 vtep 所在主机的通信报文头部。可以看到这里 IP 地址为多播
239.1.1.1,目的 MAC 地址也是多播对应的地址
通信结束之后,可以在主机上看到保存的 ARP 缓存:
root@henryxzx:~# ip neigh
10.100.1.3 dev vxlan0 lladdr d2:96:a5:fb:78:47 STALE
以及 vtep 需要的 fdb 缓存:
root@henryxzx:~# bridge fdb
d2:96:a5:fb:78:47 ......
00:00:00:00:00:00 dev vxlan0 dst 239.1.1.1 via ens3 self permanent
参考
- https://cizixs.com/2017/09/28/linux-vxlan/
- https://www.cnblogs.com/wipan/p/9220615.html
- Vincent Bernat -VXLAN & Linux
- Linux Kernel Documentation: vxlan
- Software Defined network using VXLAN
- Docker Multi Host Networking: Overlay to the Rescue
- Deep Dive into Docker Overlay Network Part 3
画图工具
