在云原生的背景下,对 Kube.NETes 的集群的生命周期的管理的期望和需求越来越多,很多企业的集群数量也与日俱增,少则几个,多则几十个。有的分布在企业的私有云,有的分布在公有云,有的分布在混合云。社区也涌现了很多项目专注于这个方向的研究和实践,这里以开源项目 cluster-api 为切入点,来介绍云原生集群生命周期的管理能力,主要介绍 cluster-api 中的 ControlPlane 的工作原理。对于 cluster-api 整体介绍可参见 Kubernetes 多云管理利器 -- cluster-api 。
以下整理出了与本篇文章相关联的概念的描述,帮助接下来的理解。
Cluster:代表一个完整的集群的安装,包括安装 Kubernetes 集群需要的集群层面的资源,如 LB、VPC 等;包括机器的创建;暴露整个控制平面组件的安装;包括集群的健康检查等等。
KubeadmControlPlane:代表了一个集群的控制平面的完整安装,包括 api server、controller manager 等 Kubernetes 的管理组件。其中也包含了机器的创建,以及不同机器需要的 BootstrapConfig 的创建、集群证书的创建、集群控制平面的组件的健康检查、版本升级、控制平面的扩缩容等。一个 ControlPlane 被创建,同时状态变为 ready 的话,代表着一个 Kubernetes 的所有的管理组件都是 ready 的,可以直接使用 kubectl 去操作集群,这是一个具体的 ControlPlane 的实现。控制平面的整体的安装和管理,使用的是 kubeadm,是社区比较流行和广泛使用的 Kubernetes 安装程序。
InfrastructureRef:Infrastructure 的定义是基础设施,可以理解成 IaaS 层面的资源定义。Ref 指的是 Reference 引用,理解成对基础设施资源对象的引用。举例来说,在 Cluster 定义的时候,会指定对应的厂商实现的 Cluster,如 VSphereCluster;在定义 KubeadmControlPlane 的时候,可以指定这个控制平面使用的机器的虚拟机模版是哪个厂商提供的,如 VSpheremachineTemplate。
Machine:代表被集群纳管和使用的机器。不同的 Provider 可以根据自己的平台,实现自己怎样创建机器出来。这里有一点需要注意的是,机器和启动配置的关系,每台机器的启动都会绑定一个当前机器自己的 BootstrapConfig。举例 aws 对应的实现是 AWSMachine,vmware 对应的实现是 VSphereMachine。
BootstrapConfig:代表了集群的不同角色的机器,在启动时候的配置,如控制节点的 BootstrapConfig 和计算节点的 BootstrapConfig 是不同的,以及控制节点里的第一台机器和剩余的控制节点也是不同的。这需要注意的一点,BootstrapConfig 是和 Machine 绑定的,不同的机器的 BootstrapConfig 是不同的。每个提供商在实现的时候可以完全不同,比如可以实现 kubeadm 的启动配置,也可以使用提供商自己封装好的启动配置 (aws 就是自己实现的),所以可以理解成 bootstrap 其实是一种启动的类型定义,默认实现的 kubeadm 作为 bootstrap 的类型的,也可以自定义,比如像 aws 也是另外一种 bootstrap 的类型。目前社区的实现,主要就是包含了 kubeadm 和 aws,这两种 BootstrapConfig 的实现。
对于 cluster-api 整体架构的介绍,以及整体架构中的 Cluster 的原理部分可参见 Kubernetes 多云管理利器 -- cluster-api 。本篇文章重点介绍整体架构中的 ControlPlane 的原理部分。
本篇文章以 vmware 的 VSphere 为 Infrastructure Provider,介绍 cluster-api 中,与 ControlPlane 相关的业务模型。
Cluster: 代表了一套完整的 Kubernetes 集群,一个集群的定义,就从定义 Cluster 开始。其中包括 Cluster 对应的 Kubernetes 集群的集群层面的基础设施管理,比如集群高可用依赖的 VIP 能力,比如公有云的 VPC 的创建,在这些集群层面的基础设施创建好之后,才会进行控制平面 ControlPlane 的创建;还包括 Cluster 关联的控制平面 ControlPlane 是什么样的。在 Cluster 定义中,使用 controlPlaneRef 引用了 KubeadmControlPlane 类型的资源对象,使用 infrastructureRef 引用了对应的 provider 实现的 Cluster,这里举例以 VSphere 为例,对应的 infrastructureRef 为 VSphereCluster 类型的资源对象;还包括集群 Cluster 的网络相关的定义,如 Pod 的 cidr 是哪个网段,如 Service 的 cidr 是哪个网段,如 APIServer 的端口是多少,如 Service 服务的域名是什么;还包括集群 Cluster 的部署的拓扑结构的定义,如有多少台控制平面的机器,如有多少台计算节点的机器等。
VSphereCluster:这里的 VSphereCluster 是其中的一个 Provider 的实现,用来说明 Cluster 对象的 infrastructureRef 所引用的资源对象,VSphereCluster 的资源对象被创建之后,会在 VSphere 中为 Kubernetes 集群准备 VIP,haproxy (用于高可用)。
KubeadmControlPlane:这里的意思是使用 kubeadm 完成 Kubernetes 的控制平面的安装,也就是会安装出指定规模机器的控制节点,不包含计算节点。KubeadmControlPlane 中会指定和 kubeadm 要安装一套 Kubernetes 集群所相关的配置信息,包括 kubeadm 在 ini 时候的配置,在 join 时候的配置,在执行 kubeadm 之前要执行的命令,以及对 Kubernetes 集群创建的 apiServer,controllerManager 的一些参数设置。
VSphereMachineTemplate:定义了用于创建对应 Provider 机器的模版,这里以 VSphereMachineTemplate 为例,VSphereMachineTemplate 中的定义包含了基于 VSphere 的虚拟机模版的技术,按需创建出虚拟机的能力。如使用哪个数据中心的 VSphere 服务器,使用哪个资源池,虚拟机模版是哪个,内存多大,CPU 是几核,存储用哪个 datastore,磁盘空间是多大,网卡是不是开启 dhcp 的开关。
1. 获取当前的控制平面 kcp 对应的 Cluster 集群对象,因为控制平面 kcp 是属于一个 Cluster 集群对象的,关联关系在 Cluster 控制器里已经通过给控制平面 kcp 的 OwnerReferences 设置了 Cluster 进行了绑定。
2. 判断当前集群对应的 Cluster 级别的基础设施,比如 VPC,LB 之类的是不是已经创建好了,如果没有创建好就跳过,前提是基础设施要准备好,才可以继续进行后面的操作。
3. 给这个控制平面的对象设置 Finalizer 用于清理操作时候的处理逻辑。
4. 调用 reconcile 方法去完成真正的控制平面对象的调和,具体的逻辑将在下面进行分析。
5. 调和外部对象,也就是需要更新一下外部的对象,这样外部对象的控制器,就会执行自己的逻辑,去完成外部对象需要的期望状态。这里要调和的外部对象,是控制平面 kcp 使用的基础设施 VSphere 的 VSphereMachineTemplate 虚拟机模版对象(
kcp.Spec.MachineTemplate.InfrastructureRef),将 kcp 当前所属的集群对象 Cluster 对象设置给 VSphereMachineTemplate 资源对象的 OwnerReferences。为什么说是外部,因为 VSphereMachineTemplate 资源对象不是在 cluster-api 这个项目中管理的,而是由 cluster-api-provider-vsphere 这个项目来完成,在 cluster-api-provider-vsphere 中实现了 VSphereMachineTemplate 的具体能力。
6. 为当前控制平面生成这个新集群的证书,这个证书就是安装 Kubernetes 需要的集群的证书。
7. 为当前控制平面生成这个新集群的 kubeconfig 文件。
8. 找出控制平面 kcp 的所有 Machines,用于后面进一步的处理,这里 Machines 有可能是空的,这种情况就是控制平面第一次创建和初始化,还没有 Machine,在后面的分析过程中会分析,在开始没有找到属于控制平面 kcp 的所有 Machines 是怎么处理的。
9. 根据集群 Cluster、Machines 等信息,初始化一个完成控制平面 kcp 业务逻辑的对象,这个对象不是控制平面 kcp 的 cr 对象,而是处理 kcp cr 对象,封装了处理一个控制平面 kcp 实例的相关业务逻辑。
10. 调和这个集群应该有的 etcd 集群,以及保证 etcd 的健康,这里主要是保证 etcd 的成员要和控制平面的所有 Machine 数量匹配,不是创建 etcd 集群。同时调和不健康的 Machine,对于不健康的 Machine 进行相关的处理,比如对于不健康的 Machine 是 etcd 的 master 节点,那需要先找一个健康的 Machine,通过 etcd 的客户端切换新的健康的 Machine 为 etcd 的新的 master 节点,然后删除 Machine cr 对象,这个删除的操作,会触发基础设施的控制器去删除物理的 Machine;判断 Machine 健康不健康是通过 c.Machines.Filter(
collections.HasUnhealthyCondition) 来完成的,也就是看看机器的 Condition 里有没有 HasUnhealthyCondition。
11. 判断当前控制平面的期待的副本数和当前已有的 Machine 数量对比,来判断是初始化,还是扩容,还是缩容控制平面。1) 如果当前的机器数小于期望数,同时当前这个控制平面拥有的机器数是 0 台机器,就执行初始化控制平面的操作;2) 如果当前的机器数小于期望数,同时当前这个控制平面拥有的机器数大于 0,就执行扩容控制平面的操作;3) 如果当前的机器数大于期望数,就执行缩容控制平面的操作。这里重点说明第一种情况,也就是初始化控制平面的情况。
12. 去 API Server 读取最新的数据,找到当前控制平面所控制的所有的机器。
13. 如果找到的被控制平面控制的机器的数量是大于 0 的,意思就是已经初始化了控制平面的第一台 master,就退出了,因为已经不不需要初始化了,已经有了第一个 master,后面只要 join 其他的控制节点就可以。
14. 初始化控制平面的配置,这里会创建一个 KubeadmConfig,用于机器的 boot,这个 KubeadmConfig 是和机器对应的,不同的机器的配置是不一样,如下,设置了 join 相关的配置为空,说明了这个就是第一个 master, 因为 master 需要设置 InitConfiguration,对应到 kubeadm 的 init 命令,而 JoinConfiguration 对应到 kubeadm 的 join 命令。
bootstrapSpec := c.KCP.Spec.KubeadmConfigSpec.DeepCopy()
bootstrapSpec.JoinConfiguration = nil
15.根据上述的 bootstrapspec 和
kcp.Spec.MachineTemplate.InfrastructureRef 的配置,通过 clone infra 的模版来创建机器。在创建机器的时候,是必须要知道机器对应的 bootstrap 的 config,也就是 bootstrapspec,同时还要设置机器是属于这个集群的,也是属于这个集群的控制平面的 (这里会给机器的 spec 加上一个控制平面的 label,但是这个 label 的 value 是空的,但是这样已经可以标记是一个控制平面的机器了就足够了)。
16. 克隆基础设施的模版,这里指的是虚拟机模版,这里指的是 VSphereMachineTemplate,用于创建控制平面的机器。
17. 根据控制平面, 集群,bootstrapspec 创建 bootstrapv1.KubeadmConfig,创建出来的 bootstrapv1.KubeadmConfig 是属于当前这个控制平面。KubeadmConfig 中包含 clusterConfiguration 这种用于配置 Kubernetes 集群组件的属性;包含了 files 这种用于 cloud-init 去在机器上创建的文件;包含了 kubeadm 安装节点的 initConfiguration 和 joinConfiguration;包含了在 kubeadm 执行前和执行后的命令 preKubeadmCommands 和 postKubeadmCommands;包含了操作系统的用户的预创建;包括了磁盘的安装;包括了 NTP 的配置;包括机器的 mountpoint 等。
18. 创建 clusterv1.Machine,同时设置 Machine 是属于哪个集群的,哪个控制平面 kcp 的,以及 Machine 的 InfrastructureRef (也就是哪个 Infrastructure Provider 的虚拟机模版,这里指的是 VSphereMachineTemplate),以及 Machine 启动需要的 bootstrapv1.KubeadmConfig (如 kubeadm 的配置文件和 init/join 的命令文件)。当 Machine 资源对象被创建,vmware 的 VSphere Provider 实现的 controller 就会 watch 到这个 Machine,根据 Machine 资源对象的定义,创建自己的 VSphereMachine 资源对象,VSphereMachine 资源对象对应的才是真正运行在 VSphere 中的虚拟机。
19. 当前集群已经初始化好了,获取连接集群控制平面的客户端,用于对集群进行进一步的操作;如果是 Kubernetes 1.18 版本以上的,还需要设置 kubeadm 的 Role Binding 配置;更新 kube-proxy 的镜像;根据 Kubernetes 的版本,更新对应版本的 coreDNS 的镜像等。
经过上述的分析,可以看出,一个 Cluster 会包含一个 VSphereCluster,一个 KubeadmControlPlane。VSphereCluster 负责为 Cluster 处理好集群层面的资源,KubeadmControlPlane 负责创建出机器,以及根据 kubeadm 的配置,结合 VSphere 的虚拟机模版技术完成基于 kubeadm 安装 Kubernetes 控制节点。当 VSphereCluster 和 KubeadmControlPlane 都 ready 了之后,Cluster 也就 ready 了,这样完整的一套不包含计算节点的 Kubernetes 集群就创建好了。从而体现出 cluster-api 在管理 Kubernetes 集群生命周期的能力。
本文作者
熊中祥
「DaoCloud 道客」技术合伙人
云原生技术专家