网络是任何分布式系统的干道,离开这些干道,系统将被分裂成一个个互不相关的孤岛。阿里云K8s集群网络基于云上专有网络VPC而建。集群网络目前有两种实现方案,分别是Flannel和Terway。
Terway和Flannel的不同之处在于,Terway支持Pod使用ENI(弹性网卡),并支持Network Policy特性。以Flannel网络方案为例,深入分析阿里云K8s集群网络的实现方法。具体分析过程从两个逻辑角度展开,一个是网络搭建过程,另一个是集群网络通信。
3.1 背景
K8s集群里,容器可以运行在不同的节点上,也可以运行在同一节点的不同网络命名空间中。这样,容器之间就产生了“距离”。K8s集群的网络系统要解决的核心问题,是不同“距离”的容器之间的通信问题。
根据容器之间“距离”的长短,可以把容器之间的通信问题划分为三个子问题:
本地容器之间的通信(相同网络命名空间内容器之间的通信)
不同网络命名空间中容器之间的通信
跨节点容器之间的通信
本地容器之间的通信时这三个子问题中最基础的一个。通信容器双方的特别指出在于,他们之间虽然有进程、文件系统等方面的隔离,但是他们共享同一个网络命名空间,即使用同一个网络协议栈。本地通信使用的是Loopback虚拟网络接口。
不同网络命名空间中容器之间的通信以及跨节点容器之间的通信这两个问题相对比较难解决,原因就在于通信双方分别使用完全独立的网络协议栈,这就设计到网络包跨网络命名空间,甚至跨集群节点的转发。
这两个子问题的解决,依赖于K8s和第三方一起实现的网络组件。利用这些网络组件,集群可以搭建出较为复杂的容器集群网络。
3.2 阿里云K8s集群网络大图
阿里云K8s集群网络搭建完成之后,包括集群的CIDR、VPC路由表、节点网络、节点的podCIDP、节点上的虚拟网桥Cni0、连接Pod和网桥的Veth组等部分。
从集群创建的过程归纳总结这些配置。可以把这些配置按集群创建的阶段分类,初始阶段之后们可以分为集群阶段、节点阶段和Pod阶段。
与这三种情况对应的,其实是对集群网络IP地址段的三次划分。三次划分分别是分配集群CIDR、在集群CIDR里划分节点podCIDR,以及在节点podCIDR网段里分配Pod的地址。
3.3 集群网络搭建
3.3.1初始阶段
集群的创建使基于专有网络VPC和云服务器ECS的。在创建完VPC和ECS之后,可以得到下图所示的资源配置。
得到一个网段为192.168.0.0/16的VPC实例,和若干从VPC网段里分配到IP地址的ECS实例。
3.3.2 集群阶段
在初始阶段的基础上,集群创建阶段会为集群指定CIDR,如下图所示。这个值会以参数的形式传递给集群节点初始化脚本,并被脚本传递给集群节点配置工具Kubeadm。之后Kubeadm会把这个参数固化到控制器管理器的编排文件里。
当控制器管理器有了这个参数之后,如果有新节点通过Kubelet注册到集群,节点控制器就会为了这个新节点划分一个子网段,而这个子网段就是节点的podCIDR、节点B的子网段是172.16.8.128/25,而节点A的子网段是172.16.0.128/25,这些配置会被纪录到对应节点的podCIDR数据项里。
3.3.3 节点阶段
经过以上阶段,K8s就有了集群CIDR,以及为每个节点划分podCIDR。
在此基础上,集群会把Flanneld部署到每个节点上,进一步为节点搭建Pod使用的网络主干道,这里主要有两个操作,参考下图。
第一个是通过Cloud Controller Manager给每个节点配置一个VPC路由项。路由项的作用是,如果VPC收到的数据包的目标地址属于某个节点的podCIDR子网段,那么VPC会把这个数据包转发到对应的节点上。
第二个是Flanneld创建虚拟网桥Cni0,以及Cni0相关的主机路由。这些主机路由的作用是,从节点外部传来的网络包,如果其目标地址属于podCIDR,则该网络包会被主机路由转发到Cni0虚拟局域网内。
note:Cni0其实是在第一个Pod被调度到节点上的时候创建的,但是从逻辑上来说,Cni0属于节点网络,不属于Pod网络。
3.3.4 Pod阶段
经过前面三个阶段,集群实际上已经为Pod搭建了网络通信的主干道。
这个时候,如果集群把一个Pod调度到节点上,Kubelet会调用Flannel CNI插件,为这个Pod创建网络命名空间和Veth设备组。其中一个Veth设备会被加入Cni0虚拟网桥,而另一个Veth设备则被安装到Pod上。这样一来,Pod就和网络主干道连接在了一起,如下图所示。
需要强调的是,前一节的Flanneld和这一节的Flannel CNI完全是两个组件。Flanneld是Daemonset下发到每个节点的Pod,它的作用是搭建网络主干道。而Flannel CNI是节点创建的时候,通过K8s-cni这个rpm包安装的CNI插件,其作用是,被Kubelet调用为最具体的Pod创建网络分支。
Flanneld和Flannel CNI相关的配置文件的用途:比如/run/flannel/subnet.env,是Flanneld创建的为Flannel CNI提供输入的一个环境变量文件,/etc/cni/net.d/10-flannel.conf,是Flanneld拷贝到节点目录给Flannel CNI使用的子网配置文件。
3.4 通信原理
集群网络搭建的四个阶段,为Pod创建了网络通信干道。基于这个网络干道,Pod可以完成下图所示的四种通信,分别是本地通信、同节点Pod通信、跨节点Pod通信,以及Pod和Pod之外网络实体的通信。
本地通信表示的是Pod内部不同容器之间的通信。因为Pod内网容器共享一个网络协议栈,所以它们之间的通信可以通过Loopback设备完成。
同节点Pod之间的通信,是Cni0虚拟网桥内部的通信,相当于一个二层局域网内部设备通信。
跨节点Pod通信略微复杂一点,但也很只管,发送端数据包通过Cni0网桥的网关流转到节点上,然后经过节点eth0发送给VPC路由。这不会经过任何封包操作。当VPC路由收到数据包时,它通过查询路由表,确认数据包目的地,并把数据包发送给对应的ECS节点。而进入节点后,因为Flanneld在节点上穿件了Cni0的路由,所以数据包会被发送目的地的Cni0局域网,再到目的地Pod。
最后一种情况,Pod与非Pod网络的实体通信,需要经过接电脑上的iptables规则做源地址转换,而此规则就是Flanneld依据命令行--ip-masq选项做的配置。