K8S Flannel容器跨主机通信浅析

k8s集群有多台宿主机的情况下,不同宿主机上的容器如何通过 ip地址进行相互访问呢 ,下面就根据k8s的网络模型,分析一下k8s集群容器的跨主机通信是如何实现的。

Flannel

Flannel是CoreOS推出的,是k8s的一种简单的三层网络方案。在k8s中可以借助这个网络插件实现不同宿主机节点之前的跨主机通讯。

Flannel有三种实现,分别是:

  1. UDP
  2. VXLAN
  3. host-gw

UDP是Flannel最早采用的方法之一,但性能较差,因为其从用户态到内核态的切换次数较为频繁,所以现在主要用的是Linux内核本身就支持的VXLAN网络虚拟化技术,下面会通过k8s的flannel插件来看看是 如何实现跨主机通讯的。

K8S实例

当前分别创建了三个虚拟机master、k8s-node1、k8s-node2当做k8s的三个节点。添加flannel插件:

kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml

可以看到三个节点的ip:

  • master: 192.168.10.200
  • k8s-node1: 192.168.10.201
  • k8s-node2: 192.168.10.202

VXLAN 模块会在现有的三层网络之上覆盖一层由它负责维护一个二层网络,使得在这个二层网络中的设备可以像在同一个局域网中一样通信。VXLAN在宿主机上设置一个特殊的网络设备叫作 VTEP,即:VXLAN Tunnel End Point(虚拟隧道端点)。VTEP设备的作用是负责在Linux内核中对二层数据帧进行封装和解封。

每台宿主机上都会有一个名为flannel.1的设备,这个设备就是VXLAN的VTEP设备,如图所示,k8s-node1上有一个flannel.1设备,它既有ip地址,又有mac地址。

在k8s集群中通过deployment_busybox.yml起了三个busybox的pod,一个在k8s-node1,两个在k8s-node2。

现在,通过k8s-node1上的pod中的容器(下面用container1代替)访问在k8s-node2上的pod中的容器(下面用container2代替),基于VXLAN的跨主机通信流程图如下所示。

container1的ip是10.244.5.2/24,它要去访问container2的ip是10.244.6.4/24。

首先进入container1,通过route -n查看容器内部的路由表

请求的ip是10.244.6.4,匹配第二条路由规则,走etho设备,而etho设备以Veth Pair的方式连接在k8s-node1的cni0网桥上,同个宿主机中docker容器通信是走docker0网桥,在k8s集群中替换为cni0网桥,作用和docker0网桥类似,都是二层交换设备。请求会通过cni0网桥到达宿主机上,而k8s-node1节点的路由表如下所示

10.244.6.0这条记录是在节点加入Flannel网络的时候,flanneld这个进程在路由表中添加的,请求匹配到 10.244.6.0 这条规则,走到flannel.1这个VETP设备,经过flannel.1这个设备转发到地址是10.244.6.0的网关。而10.244.6.0恰恰就是k8s-node2上flannel.1设备的ip地址。

既然是要组成二层网络,所以flannel.1 VETP设备接收到原始ip包后,要加上目的mac地址封装成二层数据帧。获取ip对应的mac地址就需要通过ARP,flanneld进程在节点加入Flannel的网络时已经帮我们在ARP表中插入了一条ARP记录。

可以看到10.244.6.0这个ip对应的mac地址是6a:2f:7d:e5:3f:2e。获取目的mac地址之后,就可以得到一个二层数据帧。

但现在得到的二层数据帧还无法在宿主机的网络中传输,因为现在只有目的VETP设备的mac地址,而源宿主机需要目的宿主机的ip和mac地址。所以Linux内核会把这个二层数据帧前面加一个VXLAN头,封装到一个UDP包中从宿主机中发送出去,VXLAN头的作用是标识这个被包裹的数据帧是供VXLAN使用的。那么如何获取目的宿主机的ip和mac地址呢。宿主机上会有一个FDB表,它和ARP表的区别是ARP表是三层转发,而FDB表用于二层转发,flannel.1在这里相当于起到一个二层网桥设备的作用。

我们在k8s-node1上通过之前获取到的目的VETP设备的mac地址可以查询到目的宿主机的ip是192.168.10.202,而目的宿主机的mac地址可以通过k8s-node1的正常ARP学习得到,并不需要特意维护一份,所以最终得到在宿主机网络中传输的二层数据帧格式如下所示

k8s-node2节点的ens33网卡接收到这个请求之后对数据帧进行解包,Linux内核会根据该数据帧携带的VXLAN Header中的信息将内层包裹的VETP设备的二层数据帧转到k8s-node2上的flannel.1设备,然后flannel.1会再次对数据帧进行解包,拿到k8s-node1节点容器要发送请求的内容,根据路由表把请求转到cni0网桥,最终到达container2容器。

以上就是个人对k8s容器跨主机通信实现的理解,如果有误,可以在评论区指出,谢谢~!

参考链接

转发于网络

原文地址:https://www.cnblogs.com/cheyunhua/p/15064651.html