什么是VXLAN?

在三层可达的网络中部署VXLAN,在每个VXLAN网络端点中都有一个VTEP设备,负责将VXLAN协议的数据包进行UDP数据包的封包和解包,可以将其理解为隧道,将VXLAN数据包从逻辑网络转发到物理网络

VXLAN使用24位的VXLAN网络标识符(VNI)来标识不同的虚拟网络

理解VXLAN网络_IP

点对点的VXLAN

在已经知道对端VTEP所在节点,是如何进行跨节点通信的。

理解VXLAN网络_网络_02

实操

在Node上新增VTEP设备vxlan0,创建命令如下:

ip link add vxlan0 type vxlan id 42 dstport 4789 remote 10.65.132.188 local 10.65.132.187 dev ens18
  • remote是对端节点的IP
  • local是本节点IP
  • id是VNI的值
  • dstport 是VTEP设备的端口

创建好后,需要给它配置IP并启用

ip a add 172.17.1.2/24 dev vxlan0
ip link set vxlan0 up

vxlan0详情如下:

# ip -d link show dev vxlan0
114: vxlan0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
    link/ether 86:45:5c:56:49:42 brd ff:ff:ff:ff:ff:ff promiscuity 0 
    vxlan id 42 remote 10.65.132.187 local 10.61.74.37 dev ens18 srcport 0 0 dstport 4789 ageing 300 noudpcsum noudp6zerocsumtx noudp6zerocsumrx addrgenmode eui64 numtxqueues 1 numrxqueues 1 gso_max_size 65536 gso_max_segs 65535

并且可以看到路由中新增一条记录,所有目的地址是172.17.1.0/24网段的包都要走vxlan0转发。

# ip r
...
172.17.1.0/24 dev vxlan0 proto kernel scope link src 172.17.1.2
...

同时fdb表中新增以下记录,代表着,所有进过vxlan0包的外部UDP目的地址都会设置为10.65.132.187,并从ens18网卡出去。

]# bridge fdb | grep vxlan0
00:00:00:00:00:00 dev vxlan0 dst 10.65.132.187 via ens18 self permanent

到这里,Node1已经配置好了,我们重复上面操作设置Node2,只需要将部分IP修改即可。

# 将Node1中remote和local反过来即可.
ip link add vxlan0 type vxlan id 42 dstport 4789 remote 10.65.132.187 dev ens18 local 10.65.132.188 dev ens18

# vxlan0 地址设置为172.17.1.3/24
ip a add 172.17.1.3/24 dev vxlan0
ip link set vxlan0 up

# ip -d link show vxlan0
20: vxlan0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
    link/ether 12:09:3d:c0:97:32 brd ff:ff:ff:ff:ff:ff promiscuity 0 minmtu 68 maxmtu 65535 
    vxlan id 42 remote 10.65.132.187 local 10.65.132.188 dev ens18 srcport 0 0 dstport 4789 ttl auto ageing 300 udpcsum noudp6zerocsumtx noudp6zerocsumrx addrgenmode eui64 numtxqueues 1 numrxqueues 1 gso_max_size 65536 gso_max_segs 65535

开测

在Node2上监听Node1的udp数据包

tcpdump -i ens18 udp and host 10.65.132.187 -n

然后在Node1中ping Node2的VTEP设备地址172.17.1.3

[root@k8s-master-07rf9 ~]# ping 172.17.1.3 -c 3
PING 172.17.1.3 (172.17.1.3) 56(84) bytes of data.
64 bytes from 172.17.1.3: icmp_seq=1 ttl=64 time=0.841 ms
64 bytes from 172.17.1.3: icmp_seq=2 ttl=64 time=0.382 ms
64 bytes from 172.17.1.3: icmp_seq=3 ttl=64 time=0.377 ms

可以看到tcpdump抓到的数据包如下,先收到了Node1的发过来封装了ARP数据包的数据包,想要知道Node2中VTEP设备的MAC地址,然后回给了Node1,Node1收到后,将MAC地址填充,再将封装了ICMP的VXLAN数据包发送到Node2,Node2中VTEP收到包并解包,最后回包给Node1

[root@k8s-work01-1zapn ~]# tcpdump -i ens18 udp and host 10.65.132.187 -n
dropped privs to tcpdump
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on ens18, link-type EN10MB (Ethernet), capture size 262144 bytes
19:23:36.705418 IP 10.65.132.187.34062 > 10.65.132.188.vxlan: VXLAN, flags [I] (0x08), vni 42
ARP, Request who-has 172.17.1.3 tell 172.17.1.2, length 28
19:23:36.705574 IP 10.65.132.188.20835 > 10.65.132.187.vxlan: VXLAN, flags [I] (0x08), vni 42
ARP, Reply 172.17.1.3 is-at 3a:84:39:cc:05:93, length 28
19:23:36.705880 IP 10.65.132.187.jboss-iiop > 10.65.132.188.vxlan: VXLAN, flags [I] (0x08), vni 42
IP 172.17.1.2 > 172.17.1.3: ICMP echo request, id 25707, seq 1, length 64
19:23:36.705986 IP 10.65.132.188.62793 > 10.65.132.187.vxlan: VXLAN, flags [I] (0x08), vni 42
IP 172.17.1.3 > 172.17.1.2: ICMP echo reply, id 25707, seq 1, length 64
19:23:37.708701 IP 10.65.132.187.jboss-iiop > 10.65.132.188.vxlan: VXLAN, flags [I] (0x08), vni 42
IP 172.17.1.2 > 172.17.1.3: ICMP echo request, id 25707, seq 2, length 64
19:23:37.708846 IP 10.65.132.188.62793 > 10.65.132.187.vxlan: VXLAN, flags [I] (0x08), vni 42
IP 172.17.1.3 > 172.17.1.2: ICMP echo reply, id 25707, seq 2, length 64
19:23:38.732737 IP 10.65.132.187.jboss-iiop > 10.65.132.188.vxlan: VXLAN, flags [I] (0x08), vni 42
IP 172.17.1.2 > 172.17.1.3: ICMP echo request, id 25707, seq 3, length 64
19:23:38.732846 IP 10.65.132.188.62793 > 10.65.132.187.vxlan: VXLAN, flags [I] (0x08), vni 42
IP 172.17.1.3 > 172.17.1.2: ICMP echo reply, id 25707, seq 3, length 64
19:23:41.804637 IP 10.65.132.188.20835 > 10.65.132.187.vxlan: VXLAN, flags [I] (0x08), vni 42
ARP, Request who-has 172.17.1.2 tell 172.17.1.3, length 28
19:23:41.804917 IP 10.65.132.187.34062 > 10.65.132.188.vxlan: VXLAN, flags [I] (0x08), vni 42
ARP, Reply 172.17.1.2 is-at c2:a1:4e:56:7d:eb, length 28

VXLAN的多播模式

在我们不知道VTEP所在节点的情况下,并且需要使用多个VTEP组建逻辑网络。

通信过程

理解VXLAN网络_kubernetes_03

  1. 在Node1上通过ping Node2上vxlan0设备ip,数据包通过路由到达vxlan0,vxlan0发现目的IP和源IP属于同一网段,需要知道对方的MAC地址,因此需要发送ARP查询报文。
  2. ARP报文中,源MAC地址为Node1上vxlan0的mac地址,目的mac地址为255.255.255.255也就是广播地址,并且添加VXLAN头部VNI=42
  3. 因为不知道对端的VTEP设备在哪台节点上,所以vxlan0会向多播地址224.1.1.1发送多播报文。
  4. 多播组中所有的主机都会收到报文,并且内核会判断该数据包为VXLAN报文,根据VNI发送给VTEP设备。
  5. Node2中vxlan0收到报文后,解包拿到ARP报文,然后通过ARP报文学习到了将Node1的vxlan0的mac地址与Node1 IP的映射关系记录到FDB表中,并且生成ARP应答报文。
  6. ARP应答报文中,目的主机Node1的MAC地址和目的主机Node1中vxlan0的MAC地址都通过发过来的ARP报文中学习到,所以可以直接通过单播进行回复。
  7. Node1收到ARP回复的报文后,通过报文内容,将Node2的主机地址和vxlan0的MAC地址的映射关系缓存到FDB表中。
  8. 现在双方都已经通过ARP报文获得了双方的建立ICMP通信的所有信息,可以直接通过通过单播进行通信。

环境准备

首先在Node1中新增VTEP设备vxlan0,VNI为42,通信端口4789,主机地址为10.65.132.187,设置多播地址为224.1.1.1,数据包通过ens18真实网卡出去。

ip link add vxlan0 type vxlan id 42 dstport 4789 local 10.65.132.187 group 224.1.1.1 dev ens18

给vxlan0添加地址172.17.1.2/24,并把它拉起

ip addr add 172.17.1.2/24 dev vxlan0
ip link set vxlan0 up

查看vxlan0的详细信息

# ip -d link  show vxlan0
22: vxlan0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
    link/ether 4e:92:72:79:59:ed brd ff:ff:ff:ff:ff:ff promiscuity 0 minmtu 68 maxmtu 65535 
    vxlan id 42 group 224.1.1.1 local 10.65.132.187 dev ens18 srcport 0 0 dstport 4789 ttl auto ageing 300 udpcsum noudp6zerocsumtx noudp6zerocsumrx addrgenmode eui64 numtxqueues 1 numrxqueues 1 gso_max_size 65536 gso_max_segs 65535

再看看fdb表,意思是,vxlan0封包的时候,默认会使用224.1.1.1作为VXLAN包也就是外部UDP的目的IP。

# bridge fdb | grep vxlan0
00:00:00:00:00:00 dev vxlan0 dst 224.1.1.1 via ens18 self permanent

在Node2上重复上述操作

ip link add vxlan0 type vxlan id 42 dstport 4789 local 10.65.132.188 group 224.1.1.1 dev ens18

ip addr add 172.17.1.3/24 dev vxlan0
ip link set vxlan0 up

# ip -d link show vxlan0
11: vxlan0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
    link/ether ba:d8:43:67:8d:fb brd ff:ff:ff:ff:ff:ff promiscuity 0 minmtu 68 maxmtu 65535 
    vxlan id 42 group 224.1.1.1 local 10.65.132.188 dev ens18 srcport 0 0 dstport 4789 ttl auto ageing 300 udpcsum noudp6zerocsumtx noudp6zerocsumrx addrgenmode eui64 numtxqueues 1 numrxqueues 1 gso_max_size 65536 gso_max_segs 65535

开始分析

先使用tcpdump来监听Node2中ens18网卡中来自Node1的UDP数据包,然后在Node1上ping Node2中vxlan0设备。

可以看到是可以ping通的。

# ping 172.17.1.3 -c 1
PING 172.17.1.3 (172.17.1.3) 56(84) bytes of data.
64 bytes from 172.17.1.3: icmp_seq=1 ttl=64 time=0.807 ms

--- 172.17.1.3 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.807/0.807/0.807/0.000 ms

再来看看抓到的包:

  1. 第一个数据包,可以看到Node2收到了来自Node1的 VXLAN数据包,vni为42,目的地址是多播IP224.1.1.1,因为我们创建的VTEP设备设置了该多播IP,所以会接收该数据包。且里面封装了ARP数据包,需要将172.17.1.3的MAC地址告诉172.17.1.2。
  2. 第二个数据包可以看到回复了一个VXLAN数据包,里面包含了ARP应答包,将172.17.1.3的mac地址也就是vxlan0的地址回复过去。
  3. 第三个数据包是Node1 vxlan0根据对方的MAC地址和Node2的IP地址,直接单播发送包含了ICMP数据包的VXLAN数据包到Node2
  4. 第四个数据包时Node2 vxlan0进行回复ICMP,也是包裹在一个VXLAN数据包中,发送到Node1中。
# tcpdump -i ens18 udp and host 10.65.132.187 -n
dropped privs to tcpdump
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on ens18, link-type EN10MB (Ethernet), capture size 262144 bytes

10:31:34.560469 IP 10.65.132.187.34062 > 224.1.1.1.vxlan: VXLAN, flags [I] (0x08), vni 42
ARP, Request who-has 172.17.1.3 tell 172.17.1.2, length 28
10:31:34.560616 IP 10.65.132.188.20835 > 10.65.132.187.vxlan: VXLAN, flags [I] (0x08), vni 42
ARP, Reply 172.17.1.3 is-at ba:d8:43:67:8d:fb, length 28
10:31:34.560819 IP 10.65.132.187.jboss-iiop > 10.65.132.188.vxlan: VXLAN, flags [I] (0x08), vni 42
IP 172.17.1.2 > 172.17.1.3: ICMP echo request, id 46810, seq 1, length 64
10:31:34.560935 IP 10.65.132.188.62793 > 10.65.132.187.vxlan: VXLAN, flags [I] (0x08), vni 42
IP 172.17.1.3 > 172.17.1.2: ICMP echo reply, id 46810, seq 1, length 64
10:31:39.661990 IP 10.65.132.188.20835 > 10.65.132.187.vxlan: VXLAN, flags [I] (0x08), vni 42
ARP, Request who-has 172.17.1.2 tell 172.17.1.3, length 28
10:31:39.662329 IP 10.65.132.187.34062 > 10.65.132.188.vxlan: VXLAN, flags [I] (0x08), vni 42
ARP, Reply 172.17.1.2 is-at 4e:92:72:79:59:ed, length 28

再次ping Node2的vxlan0设备,通过抓包可以看到,不需要发送ARP报文了,是因为Node1已经缓存了ARP。

第一个数据包目的IP不再是224.1.1.1多播地址了,而是Node2的地址。是因为vxlan0也已经缓存到fdb表中。

10:49:36.712777 IP 10.65.132.187.jboss-iiop > 10.65.132.188.vxlan: VXLAN, flags [I] (0x08), vni 42
IP 172.17.1.2 > 172.17.1.3: ICMP echo request, id 41695, seq 1, length 64
10:49:36.712973 IP 10.65.132.188.62793 > 10.65.132.187.vxlan: VXLAN, flags [I] (0x08), vni 42
IP 172.17.1.3 > 172.17.1.2: ICMP echo reply, id 41695, seq 1, length 64

查看arp缓存

# arp | grep vxlan0
172.17.1.3               ether   ba:d8:43:67:8d:fb   C                     vxlan0

查看fdb表,添加了Node2中vxlan0的MAC地址和Node2地址的映射表项。

# bridge fdb | grep vxlan0
00:00:00:00:00:00 dev vxlan0 dst 224.1.1.1 via ens18 self permanent
ba:d8:43:67:8d:fb dev vxlan0 dst 10.65.132.188 self

VXLAN多播模式+桥接

在VXLAN的多播的基础上再加上桥接网络。

通信过程

桥接网络模拟及通信到宿主机过程可以参考手动实现docker容器bridge网络模型

数据包达到网桥后,然后再转发到vxlan0中,接下来的流程也与上面将的多播是一致的。

理解VXLAN网络_kubernetes_04

环境准备

在Node1上运行如下命令准备环境

# 添加VTEP设备
ip link add vxlan0 type vxlan id 42 dstport 4789 local 10.65.132.187 group 224.1.1.1 dev ens18

# 添加网桥
ip link add bridge0 type bridge
ip link set vxlan0 master bridge0
ip link set vxlan0 up
ip link set bridge0 up

# 添加net1模拟容器
ip netns add net1
ip link add veth0 type veth peer name veth1
ip link set dev veth0 up

# 将另一端绑定在网桥上
ip link set dev veth1 up
ip link set dev veth1 master bridge0


# 设置net1中网络配置
ip link set dev veth0 netns net1
ip netns exec net1 ip addr add 172.19.1.2/24 dev veth0
ip netns exec net1 ip link set veth0 up

在Node2中重复上面操作,将172.19.1.3/24绑定到另一个Network Namespace net3中

# 添加VTEP设备
ip link add vxlan0 type vxlan id 42 dstport 4789 local 10.65.132.188 group 224.1.1.1 dev ens18

# 添加网桥
ip link add bridge0 type bridge
ip link set vxlan0 master bridge0
ip link set vxlan0 up
ip link set bridge0 up

# 添加net1模拟容器
ip netns add net3
ip link add veth0 type veth peer name veth1

# 将另一端绑定在网桥上
ip link set dev veth1 up
ip link set dev veth1 master bridge0

# 设置net1中网络配置
ip link set dev veth0 netns net3
ip netns exec net3 ip addr add 172.19.1.3/24 dev veth0
ip netns exec net3 ip link set veth0 up

开始分析

在Node1的net1中ping Node2中的net3,可以看到有回包,说明网络是通的。

# ip netns exec net1 ping 172.19.1.3 -c 2
PING 172.19.1.3 (172.19.1.3) 56(84) bytes of data.
64 bytes from 172.19.1.3: icmp_seq=1 ttl=64 time=0.992 ms
64 bytes from 172.19.1.3: icmp_seq=2 ttl=64 time=0.605 ms

通过监听Node2的网卡ens18,抓到以下的包:

  1. 首先是Node1发送包含ARP的UDP包到多播地址224.1.1.1,因为Node2是属于该多播地址,所以会接收该包,并通过vxlan0进行解包,最终拿到ARP包,然后发送vxlan0的mac地址进行arp回包,也是通过封装到UDP包中发送Node1中,然后再通过vxlan0->bridge进入到net1中,net1中收到ARP后,将Node2的容器IP地址和mac地址缓存到ARP中。
  2. 并且在fdb表中添加一项,net3的的mac地址与Node2的IP地址映射关系
  3. 接下来是通过ARP缓存找到MAC地址并添加到ICMP的IP包上,然后再将其封装到UDP包中,UDP的源IP是通过查询fdb表得到的,可以直接通过单播发送。
7:15:47.595683 IP 10.65.132.187.34062 > 224.1.1.1.vxlan: VXLAN, flags [I] (0x08), vni 42
ARP, Request who-has 172.19.1.3 tell 172.19.1.2, length 28
17:15:47.595863 IP 10.65.132.188.20835 > 10.65.132.187.vxlan: VXLAN, flags [I] (0x08), vni 42
ARP, Reply 172.19.1.3 is-at 02:fd:f9:7f:61:a7, length 28
17:15:47.596172 IP 10.65.132.187.42208 > 10.65.132.188.vxlan: VXLAN, flags [I] (0x08), vni 42
IP 172.19.1.2 > 172.19.1.3: ICMP echo request, id 51397, seq 1, length 64
17:15:47.596295 IP 10.65.132.188.22080 > 10.65.132.187.vxlan: VXLAN, flags [I] (0x08), vni 42
IP 172.19.1.3 > 172.19.1.2: ICMP echo reply, id 51397, seq 1, length 64
17:15:48.596543 IP 10.65.132.187.42208 > 10.65.132.188.vxlan: VXLAN, flags [I] (0x08), vni 42
IP 172.19.1.2 > 172.19.1.3: ICMP echo request, id 51397, seq 2, length 64
17:15:48.596724 IP 10.65.132.188.22080 > 10.65.132.187.vxlan: VXLAN, flags [I] (0x08), vni 42
IP 172.19.1.3 > 172.19.1.2: ICMP echo reply, id 51397, seq 2, length 64

可以看到net1中已经缓存了net3的ip地址与mac地址

# Node1
# ip netns exec net1 arp 
Address                  HWtype  HWaddress           Flags Mask            Iface
172.19.1.3               ether   02:fd:f9:7f:61:a7   C                     veth0

net3中的mac地址和net1中的arp缓存是对应上的。

# ip netns exec net3 ip -d link show veth0
34: veth0@if33: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT group default qlen 1000
    link/ether 02:fd:f9:7f:61:a7 brd ff:ff:ff:ff:ff:ff link-netnsid 0 promiscuity 0 minmtu 68 maxmtu 65535 
    veth addrgenmode eui64 numtxqueues 8 numrxqueues 8 gso_max_size 65536 gso_max_segs 65535

查看fdb表,新增了net3的mac地址和Node2的IP地址映射关系

# bridge fdb | grep vxlan0
02:fd:f9:7f:61:a7 dev vxlan0 dst 10.65.132.188 self

巨人的肩膀

  • 《kubernetes网络权威指南》