三、Kubernetes网络CNI实现--Flannel
3.1 Flannel 简介
Flannel 是网络控制平面软件,是CoreOS 下的一个子项目,类似的还有CoreDNS, 为Kubernetes 提供了基于CNI的网络实现方式。
K8S给每个Pod都有一个唯一的路由可达的IP地址,如果Pod没有唯一的IP地址话,Pod就要复用WorkNode的IP和端口,这样就需要在WorkNode进行复杂的端口映射管理,把WorkNode上的一个端口映射给Pod,管理繁琐,网络资源受限。
Kubernetes 默认网络实现为 kubernet,它是通过配置和连接了WorkNode外面的物理或虚拟路由器,在这些路由器上配置Pod的子网路由,实现跨WorkNode的容器网络连接。但是为路由器添加路由是有很大的局限性(路由表条目数限制,IP地址冲突等),解决这种局限性就推出了各种CNI的实现,Flannel就是其中一种,通过配置主机的路由,或者是Overlay网络,避免配置物理路由器。
Flannel 支持三种网络模式:
Vxlan
UDP
host-gw
Flannel 的核心是提供一个跨WorkNoded的容器网络连接。它并不是为K8s创立的,可以为Docker工作。是独立于K8s的项目。
在每个WorkNode Flannel都会运行一个Flannel-d的进程。运行在kubeflannelds-pod里。
Flannel 架构:
(1)独立于K8s的项目,是K8s的外部组件。
(2)为了获取K8s的数据变化,Flannel需要监听kubernetes-master-node 的api-server的数据,来获得Pod的更新。
(3)Flannel本身也是一个系统,需要有自己的中心存储,需要数据库,当Flannel 服务K8s时,常用K8s自带的etcd数据库。
(4)监听MasterNode ,运行在WorkNode上,复用etcd数据库。
3.2 Flannel 环境
1、删除原有K8s环境。
kubeadm reset
2、Flannel安装
3.3 host-gw
host-gw是 Flannel 的第一种网络实现。
思想:将外界路由器的路由下沉到主机网络空间。
host-gw是与原生的kubenet网络方案最接近的实现--依靠系统外路由器来实现跨WorkNode通信。在K8S中跨WorkNode的网络的通信是通过连接到不同WorkNode的路由器来实现的,因此需要维护这个路由器。
Flanneld 解决方案:将系统外路由器的功能下沉到WorkNode中:Flanneld 配置主机路由,替代在外部路由器上配置路由条目,这也是host-gw的思想。
Flanneld连接了K8S,所以 Flanneld可以掌握K8S的全部信息,所以运行在每个WorkNode上的flanneld知道其中每个Pod上的子网与WorkNode的对应关系,所以可以维护一个Pod与WorkNode关系的路由表。
连接Pod 的 Linuxbridge在K8S环境下叫cbi0;连接Pod 的 Linuxbridge在Flannel环境下改名为cni0 (冲突的问题,要删除cbi0避免IP地址冲突)
所有的Worker Node 必须在一个二层网络中,因为添加路由时,nexthop必须是直接二层可达的,在同一个二层网络中,这样的特性也导致Flannel的应用规模受限。
本地Pod CIDR-->cni0(linuxbridge)-->linuxbridge-->所有本地Pod上
3.3 Flannel Vxlan实现
MAC Header : 14 Bytes
IP Header : 20 Bytes
UDP Header : 8 Bytes
VXLAN Header : 8 Bytes 16 Million Segments
原始的二层报文被一层层封装之后,从WorkNode来看,是看不到被封装的Pod网络数据的,只能看到WorkNode之间的VXLAN数据:Pod的数据被封装在外层是WorkNode IP地址头。
Flannel 的VXLAN实现,就是现将Pod的网络,作为VXLAN内层的数据(Overlay),封装在WorkNode的原有网络(Underlay)中,从而实现跨WorkNode的容器连接。
因为多个了VXLAN的封装头(50Bytes)所以在Flannel VXLAN模式下,容器网卡的MTU为1450。
(1)LinuxBridge based VxLAN实现
Flannel 就是基于linuxBridge来实现 VxLAN的。
Linux原生实现 VxLAN的方式
Linux中通过VxLAN子接口来实现VxLAN, 不同 VxLAN的vni(区分 VxLAN的标识符)都对应一个类型为VxLAN dev设备就是网络子接口, VxLAN 默认UDP端口是8472。
发送端通过eth0解封装,发送出VxLan报文:
ip link add vxlan0 type vxlan id 1 remote x.x.x.x local x.x.x.x dstport 8472 dev eth0
接收端,会启动8472端口进行监听,linux内核根据vni将数据转给相应的 VxLAN子接口,再在此子接口上根据配置信息来实现 VxLAN的解封装,得到原始的二层报文。
(2)Flannel利用Linux来实现 VxLAN
在Flannel VxLAN下,Flannel会在WorkNode里创建一个“flannel.<vni>”的设备,并挂载一个IP地址,这个IP地址是当前WorkNode Pod的 CIDR第0个地址(网络地址)作为接口的loopback地址,相比与之间的host-gw多了一个flannel.<vni>设备,通过它来实现跨WorkNode的通信。
10.10.0.8-->cni0-->flannel.<vni>-->VxLAN-->flannel.<vni>-->cni0-->10.10.1.17
再进行数据包的MAC地址交换
3.5 UDP
UDP是Flannel的第三种网络实现,仅在内核不支持host-gw 和VxLAN时才使用的模式。也是不建议使用的模式。
实现方式: 通过用户态的进程转发来实现。UDP默认端口是8282
在UDP模式下,与Vxlan模式相似,Flannel会在每个WorkNode上创建一个类型为TUN的netDev设备,通常设备名叫“flannel0”,在Flannel模式下,flannel0的地址是/16位的,flannel0包含了Pod的全部网段,flannel0收到数据包后,会转发给运行在WorkNode上的flanneld进程,由flanneld进程完成UDP的封装。数据的封装解封装都是由用户态进程flanneld来完成的,而在Vxlan模式下,数据的封装与解封装都是由Linux内核模块来完成的。
Pod--> veth-->flannel0-->flanneld-->flanneld-->flannel0-->veth
UDP模式下,数据的封装解封装,由用户态进程完成,而原生的Linux网络数据转发是在内核里完成的,所以UDP模式下就存在用户态进程的低效率情况:存在内核态数据与用户态数据的频繁切换、拷贝,所以性能差,不适宜用在生产环境中。