k8s Flannel vxlan模式原理
1 k8s网络模型
Flannel 在每个节点上运行一个 flanneld 代理,并预先分配一个子网给主机节点 ,例如10.244.3.1给k8sworker1,Pod运行的前再分配这个子网的IP。Flannel 使用etcd来保存网络配置,已分配的子网,节点IP等。同时还要分配flannel1的 arp-地址解析协议永久缓存,cni0-网关, veth设备(veth设备是每一个POD在主机端有一个veth设备)等,具体作用在后面介绍。
例,arp缓存 ip neigh show dev flannel.1,每台主机都有同样一个flannel.1
10.244.4.0 lladdr a2:8d:f0:2e:58:ce PERMANENT
10.244.1.0 lladdr 06:7d:8a:1f:59:c2 PERMANENT
10.244.5.0 lladdr 0a:fb:71:0f:74:c0 PERMANENT
10.244.2.0 lladdr 5a:e4:c3:cc:13:c2 PERMANENT
10.244.0.0 lladdr 12:2a:47:be:1f:33 PERMANENT
2. 网络配置及cni0:
Flannel 网络配置:
[root@k8smaster1 flannel]# cat /var/run/flannel/subnet.env
FLANNEL_NETWORK=10.244.0.0/16
FLANNEL_SUBNET=10.244.0.1/24
FLANNEL_MTU=1450
FLANNEL_IPMASQ=true
在我们这个集群中的实际分配状况如下:
[sdwfwd@k8sworker1 /]$ ifconfig
cni0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1450
inet 10.244.3.1 netmask 255.255.255.0 broadcast 10.244.3.255
inet6 fe80::140b:57ff:fe5c:77ce prefixlen 64 scopeid 0x20<link>
ether 16:0b:57:5c:77:ce txqueuelen 1000 (Ethernet)
RX packets 146292640 bytes 52132921571 (48.5 GiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 143861695 bytes 82973332387 (77.2 GiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
flannel.1: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1450
inet 10.244.3.0 netmask 255.255.255.255 broadcast 0.0.0.0
inet6 fe80::a011:55ff:fe0b:9855 prefixlen 64 scopeid 0x20<link>
ether a2:11:55:0b:98:55 txqueuelen 0 (Ethernet)
RX packets 119234264 bytes 37483182903 (34.9 GiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 104687050 bytes 33146619706 (30.8 GiB)
TX errors 0 dropped 8 overruns 0 carrier 0 collisions 0
veth43438514: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1450
inet6 fe80::60a3:9ff:fe7e:b5f0 prefixlen 64 scopeid 0x20<link>
ether 62:a3:09:7e:b5:f0 txqueuelen 0 (Ethernet)
RX packets 415692 bytes 28769309 (27.4 MiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 722618 bytes 49979675 (47.6 MiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
[sdfsdf@k8sworker1 /]$ ethtool -i cni0
driver: bridge
version: 2.3
firmware-version: N/A
expansion-rom-version:
bus-info: N/A
supports-statistics: no
supports-test: no
supports-eeprom-access: no
supports-register-dump: no
supports-priv-flags: no
这里的cni0,是一种网桥类型的设备,是一个工作在第二层网络的设备以MAC地址作为目的地进行帧转发,可以看成一个交换机,只负责局域网内部转发。这样就能保证同一主机内的POD的通信。
同一台主机的POD之间是如何通信的:
不同网络命名空间的设备是可以通过veth这样的设备去连接的,可以用 ifconfig看到,veth是成对出现的,不同的POD属于不同的命令空间的他们是不能通信的,所以需要建立 veth pair的设备,连接到两端的网络命名空间去通信。
但是只有veth是设备是不够的,因为一旦有很多的POD,要让POD之间都联通就必须得两两的去建立这种veth设备,这样veth设备会随时POD的增多而极速增长,而网桥cni0的出现,就会让个拓扑图变得简单,veth pair的一端连接POD,别一个端连接网桥cni0,所以veth看起来更像网线,网桥就将同一台主机上所有POD连接在一起,所以网桥更像一个交换机,工作在第二层,这样就让同一台主机各POD能够进行通信。
3. Flannel.1 介绍
这里的flannel.1是一种 vxlan类型的设备
Vxlan:VxLAN是一种UDP封装协议,它使属于同一网络的主机能够通过L3路由基础设施进行通信
[sdfssfd@k8sworker1 /]$ ethtool -i flannel.1
driver: vxlan
version: 0.1
firmware-version:
expansion-rom-version:
bus-info:
supports-statistics: no
supports-test: no
supports-eeprom-access: no
supports-register-dump: no
supports-priv-flags: no
准备的说法是,flannel.1是一个vtep-vxlan tunnel endpoint设备,在vxlan模式下,这个vtep设备的功能就是封包解包。为什么要封包解包,因为在vxlan模式下,要把发往目标的IP的数据包会用UDP的格式封装起来,最后通过物理网卡和协议栈去发往对端的,
从POD内部发往本地主机物理网卡时,首先要通过cni0网桥的,网桥再将这个数据包转发到flannel.1的,下面介绍一下这种转发的规则及POD和网桥的通信规则:
同一台主机的转发,在上面已经讲讲了。
但如何让POD与外部或公网通信的? 因为网桥还有一个帧转发的功能,它需要把数据帧转发到第三层网络协议栈上去,而网桥只有在数据帧MAC地址为网桥自身MAC地址时,网桥就认为该数据包是要发往主机的,表明该帧是要访问外部网络,此时它才进行转发。如果实现网络数据帧的MAC地址是网桥自身MAC地址的呢,详细的可以看POD里面:
K8sworker1 pod:
[root@ldsfsd-deployment-85b94bb6c-6wsmv tmp]# route -n
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
0.0.0.0 10.244.3.1 0.0.0.0 UG 0 0 0 eth0
10.244.0.0 10.244.3.1 255.255.0.0 UG 0 0 0 eth0
10.244.3.0 0.0.0.0 255.255.255.0 U 0 0 0 eth0
可以看到只要发往10.244.0.0 这个网段的数据时,就会把网桥 10.244.3.1作为网关(IP通信中转站), 那么k8sworker1 的pod发出去的数据包就指定到达10.244.3.1这个ip上,所以mac地址就是10.244.3.1对应的mac地址,即cni0的mac地址。正是由于这个原因,触发了cni0网桥的转发到主机上的三层网络协议栈的这个机制,而到达k8sworker1主机后,通过ip要看具体转发到哪张网卡是看主机的路由信息的,所以,先看下k8sworker1的路由信息。
[sfdfed@k8sworker1 /]$ route -n
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
0.0.0.0 192.168.3.1 0.0.0.0 UG 100 0 0 enp0s3
10.244.0.0 10.244.0.0 255.255.255.0 UG 0 0 0 flannel.1
10.244.1.0 10.244.1.0 255.255.255.0 UG 0 0 0 flannel.1
10.244.2.0 10.244.2.0 255.255.255.0 UG 0 0 0 flannel.1
10.244.3.0 0.0.0.0 255.255.255.0 U 0 0 0 cni0
10.244.4.0 10.244.4.0 255.255.255.0 UG 0 0 0 flannel.1
10.244.5.0 10.244.5.0 255.255.255.0 UG 0 0 0 flannel.1
172.17.0.0 0.0.0.0 255.255.0.0 U 0 0 0 docker0
192.168.3.0 0.0.0.0 255.255.255.0 U 100 0 0 enp0s3
如果此时要把k8sworker1数据包发往k8s worker3的 POD :10.244.5.103
查看k8sworker1的路由信息,通过路由信息,看到属于10.244.5.0/24这个网段的数据包都会经过 flannel.1发出,网关是10.244.5.0 ,这个ip也就是对端vtep设备的ip地址
[sinbdfl@k8sworker1 /]$ route -n
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
0.0.0.0 192.168.3.1 0.0.0.0 UG 100 0 0 enp0s3
10.244.0.0 10.244.0.0 255.255.255.0 UG 0 0 0 flannel.1
10.244.1.0 10.244.1.0 255.255.255.0 UG 0 0 0 flannel.1
10.244.2.0 10.244.2.0 255.255.255.0 UG 0 0 0 flannel.1
10.244.3.0 0.0.0.0 255.255.255.0 U 0 0 0 cni0
10.244.4.0 10.244.4.0 255.255.255.0 UG 0 0 0 flannel.1
10.244.5.0 10.244.5.0 255.255.255.0 UG 0 0 0 flannel.1
172.17.0.0 0.0.0.0 255.255.0.0 U 0 0 0 docker0
192.168.3.0 0.0.0.0 255.255.255.0 U 100 0 0 enp0s3
可以看到它需要匹配的路由信息。
10.244.5.0 其实就是对端的一个vtep设备的IP地址,vtep就是一个封包解包的设备,封解的都是UDP包,封包完成后发往主机节点,由主机节点发送这个UDP包,达到k8sworker3这个设备的物理网卡上面。但是在发往k8sworker3主机节点网卡前,flanne.1要进行一加工处理,只有这样才能让内核认识这是一个vxlan模式的数据包,以便内核能够对它进行特殊的处理,处理的时候会在包的前面加上对端vtep设备的MAC地址 ,那么怎么得到的MAC地址呢,这就要看flanneld这个进程,因为flanneld这个进程会在节点上运行,并在arp缓存中写入了mac地址,且永不过期。
在k8sworker1.
[sisdfe@k8sworker1 /]$ ip neigh show dev flannel.1
10.244.4.0 lladdr a2:8d:f0:2e:58:ce PERMANENT
10.244.1.0 lladdr 06:7d:8a:1f:59:c2 PERMANENT
10.244.5.0 lladdr 0a:fb:71:0f:74:c0 PERMANENT
10.244.2.0 lladdr 5a:e4:c3:cc:13:c2 PERMANENT
10.244.0.0 lladdr 12:2a:47:be:1f:33 PERMANENT
除了加上mac地址,还会加上 vxlan头部信息,来表明这是一个vxlan帧,这样方便对端知道这是一个vxlan帧并进行相应的解包操作。同时头部信息中还包含一个vni的信息,只有vni相同的网络设备才有解包的资格。Flannel.1在对这个包封装完后,还要用UDP格式把这个数据包再包裹一次,这时需要知道要发往的对端的IP,这时flannel.1会查询本机的一个fdb的转发数据库获取目的节点主机的IP。
K8sworker1:
[sidnsdfw@k8sworker1 /]$ bridge fdb show flannel.1 |grep 0a:fb:71:0f:74:c0
0a:fb:71:0f:74:c0 dev flannel.1 dst 192.168.3.225 self permanent
这个IP就是k8sworker3的主机IP,
fdb配置表和前面提到的ARP缓存,都是由 flanneld进程完成的。
k8sworker3 的接收过程:
收到数据包后通过vxlan头部信息,VNI标记,把包给flannel.1进行解包,发现要发送的IP为10.244.5.103,然后查询本机路由信息
[root@k8sworker3 ~]# route -n
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
0.0.0.0 192.168.3.1 0.0.0.0 UG 100 0 0 enp0s3
10.244.0.0 10.244.0.0 255.255.255.0 UG 0 0 0 flannel.1
10.244.1.0 10.244.1.0 255.255.255.0 UG 0 0 0 flannel.1
10.244.2.0 10.244.2.0 255.255.255.0 UG 0 0 0 flannel.1
10.244.3.0 10.244.3.0 255.255.255.0 UG 0 0 0 flannel.1
10.244.4.0 10.244.4.0 255.255.255.0 UG 0 0 0 flannel.1
10.244.5.0 0.0.0.0 255.255.255.0 U 0 0 0 cni0
172.17.0.0 0.0.0.0 255.255.0.0 U 0 0 0 docker0
192.168.3.0 0.0.0.0 255.255.255.0 U 100 0 0 enp0s3
发现的匹配规则为本地cni0网桥的,然后把数据发送到网桥上,网桥是连接所有的子网,网桥发现10.244.5.103是子网的IP,则把数据包发往这个IP.
UDP是可以丢包的,那个上面的UDP封包并传输是可靠的吗?
不用考虑可靠性。这里的UDP封装的IP包实际上是充当了第二层链路层的工作,传输的可靠性是靠传输层实现的,传输层靠的是对应POD所在网络命令空间中的TCP的重传来保证的。