12 升级设计与实现
得益于活跃的开源社区,Kubernetes的迭代速度较快,目前保持每个季度发行一个新版本的节奏。新版本的Kubernetes有着更为先进的新特性、更加全面的安全加固和漏洞修复机制。
为了让用户能够尽快地使用更新版本的Kubernetes,也为了维护线上多集群版本的统一性以减少运维成本,我们需要将现存的集群安全且“平滑”地升级到更新的版本。集群升级是系统运维领域的焦点问题之一,也是一个面临着很多挑战的问题。
在这一章中,我们会对集群升级及其相关的知识进行完整的介绍,包括升级预检、原地升级与替换升级,以及集群升级“三部曲”。
12.1 升级预检
大家把为正在对外提供服务的Kubernetes集群升级比作“给飞行中的飞机换引擎”,所以升级的难度可想而知。
升级难度主要源自两点:一是集群经过长时间的运行,积累了复杂的运行时状态;二是集群已经被进行了各种个性化配置。这两点都会带来升级流程中难以处理的情况,从而导致升级失败。
这就需要我们在升级集群之前对集群进行全面的检查,从而保证升级可以顺利完成。下面我们就以阿里云容器服务Kubernetes集群升级预检为例,对预检的各个检查项进行详细的介绍。
集群升级预检功能目前被放置在运维中心里。如图12-1所示,运维中心支持集群升级前置检查、组件升级前置检查和集群检查三种检查类型。本章主要对集群升级前置检查进行介绍与解析。
图12-1 运维中心的组成 (此处原为图片,描述了运维中心支持三种检查类型的示意,请参考原图)
12.1.1 核心组件检查项
说到集群健康检查,就不得不剖析一下集群的健康对于集群升级的重要性。一个不健康的集群很可能会在升级中出现各种异常的问题,就算侥幸完成了升级,各种问题也会在后续使用中逐渐凸显出来。
也有的用户会说,我的集群看起来挺健康的,但是升级之后就出现问题了。一般来说,之所以会发生这种情况,是因为在集群在升级之前这个问题已经存在了,只不过是在经历了集群升级之后才显现出来。
了解了在升级前对集群做健康检查的重要性之后,我们来对前置检查的各个检查项进行分类讲解。我们将核心组件检查项分为三类,分别是云资源检查、核心组件检查以及节点配置检查。
1. 集群云资源
容器服务Kubernetes需要依赖阿里云底层的各种资源,集群所依赖的云资源一旦不健康,或发生配置错误,都会影响整个集群的状态。
下面就我们需要检查的云资源、它们所包含的检查项,以及检查项异常可能带来的影响进行分析,具体分析如表12-1所示。
表12-1 集群云资源检查项 (此处原为表格,详细列出了云资源及其检查项与影响,请参考原书表格内容)
2. 集群核心组件
集群核心组件的健康与否影响着整个集群的健康。下面我们就所需要检查的组件、它们所包含的检查项,以及检查项异常可能带来的影响进行分析,具体分析如表12-2所示。
表12-2 集群核心组件检查项 (此处原为表格,详细列出了核心组件及其检查项与影响,请参考原书表格内容)
3. 集群节点配置
节点作为承载Kubernetes 的底层元计算资源,不仅运行着Kubelet、Docker等重要的系统进程,也充当着集群和底层硬件交互接口的角色。
确保节点的健康性和配置的正确性是确保整个集群健康性的重要一环。下面我们就所需要检查的节点组件、它们所包含的检查项,以及检查项异常可能带来的影响进行分析,具体分析如表12-3所示。
表12-3 集群节点配置检查项 (此处原为表格,详细列出了节点组件及其检查项与影响,请参考原书表格内容)
12.1.2 前置检查增项
1. 节点水位检查
目前容器服务Kubernetes的集群升级方式为原地升级。这种升级方式可以保证整个集群“平滑”升级,但相应也要为集群升级预留一定的资源,以保证集群的顺利升级。
具体的资源检查项主要有剩余可运行Pod数量、磁盘剩余容量、可用句柄数、进程数,以及可用内存。
- 对于剩余的可运行Pod数量,我们需要确保集群至少可以创建一个新Pod,用于运行升级CRD Controller的组件,且被升级的节点至少可以运行一个Pod用于节点升级。
- 对于系统磁盘剩余容量,至少要有1GB的剩余空间用于存储升级临时文件。
- 对于可用句柄数和进程数,要求有10%余量可用。
- 对于内存,至少需要200MB用于升级。
2. 升级依赖资源检查
目前容器服务Kubernetes在升级集群的过程中,除了对管控面板和节点进行升级之外,也会升级集群中的kube-proxy和CoreDNS系统组件。所以在升级之前,我们也需要对kube-proxy和CoreDNS的相关配置进行检查,防止升级后发生错误。
3. 废弃资源与不兼容项检查
Kubernetes的不同版本之间可能存在着资源废弃与不兼容的情况,我们需要在升级集群之前对这些检查点进行逐一检查,并进行相应的处理。这里以升级到1.16版和升级到1.18版为例说明。
-
升级到1.16版:需要对CoreDNS所使用的Corefile进行检查,并将Corefile 迁移到CoreDNS 1.6版所支持的版本中。这是因为,Kubernetes社区推荐的Kubernetes 1.16版所对应的CoreDNS为1.6版。与之前版本的CoreDNS相比,其主要的配置变化为将默认的转发插件从
proxy切换到了forward组件,并从镜像中移除了proxy插件。这就导致如果我们不提前修改Corefile的话,在我们升级完Kubernetes与CoreDNS之后,CoreDNS会因为无法识别Corefile中的proxy插件而导致程序崩溃。 -
升级到1.18版:需要对集群和本地工作流水线中的工作负载进行检查,需要确保所有工作负载都不再使用
extensions/v1beta1、apps/v1beta1和apps/v1beta2这三个APIVersion。因为这三个APIVersion会在1.18版中被彻底删除,所以:- 位于
apps/v1beta1和apps/v1beta1下的资源使用apps/v1替代。 - 位于
extensions/v1beta1下的资源daemonsets、deployments、replicasets使用apps/v1替代。 - 位于
extensions/v1beta1下的资源networkpolicies使用networking.k8s.io/v1替代。
- 位于
12.2 原地升级与替代升级
在软件升级领域,有两种主流的软件升级方式,即原地升级和替换升级。这两种升级方式同样适用于Kubernetes集群。这两种方式采用了不同的思路,存在着各自的利弊。下面我们对这两种集群升级方式及其优缺点展开讲解。
12.2.1 原地升级
原地升级会通过在ECS上原地替换Kubernetes组件的方式完成整个集群的升级工作,阿里云容器服务Kubernetes为客户提供的集群升级就是基于这种方式的。
以1.14版升级到1.16版为例,我们会通过直接升级节点上的Kubelet及其配置的方式,将集群所有节点升级到1.16版。在这个过程当中节点保持运行,ECS的相关配置也不会被修改,如图12-2所示。
图12-2 集群原地升级 (此处原为示意图,描述直接升级节点的Kubelet及配置,节点保持运行,ECS配置不变)
优点:
- 通过原地替换的方式对节点进行升级,保证了节点上的Pod不会因为集群升级而重建,确保了业务的连贯性。
- 不对底层ECS进行修改和替换,保证了依赖特定节点调度的业务可以正常运行,也对ECS的包年、包月客户更加友好。
缺点:
- 需要在节点上进行一系列升级操作,才能完成整个节点的升级工作。这就导致整个升级过程不够“原子化”,可能会在中间的某一步失败,从而导致该节点升级失败。
- 需要预留一定量的资源,只有在资源足够的情况下升级程序才能在ECS上完成对节点的升级。
12.2.2 替代升级
替代升级又称轮转升级。替代升级会逐个将旧版本的节点从集群中移除,并用全新的新版本节点来替换,从而完成整个Kubernetes集群的升级工作。
同样以1.14版升级到1.16版为例,用替代轮转方式,我们会将集群中1.14版的节点依次进行排水(drain)并从集群中移除,并直接加入1.16版的节点。完成所有节点的轮转之后,整个集群就升级到1.16版了,如图12-3所示。
图12-3 集群替代升级 (此处原为示意图,描述逐个排水旧节点、加入新节点的过程)
优点:
- 通过将旧版本的节点替换为新版本的节点从而完成集群升级。替换的过程相较于原地升级更为“原子化”,也不存在那么复杂的中间状态,所以也不需要在升级之前进行太多的前置检查。
缺点:
- 将集群内的节点全部进行替换和重置,所有节点都会经历排水的过程,这就会使集群内的Pod大量迁移重建。在对Pod重建容忍度比较低的业务中可能会引发故障。
- 节点经历重置后,储存在节点本地磁盘上的数据都会丢失。
- 可能会带来宿主机IP地址变化等问题,对包年、包月用户也不够友好。
12.3 升级三部曲
了解了两种集群升级方式之后,我们以阿里云容器服务Kubernetes目前所支持的原地升级方式为例,对集群升级功能展开讲解。
首先我们来看集群升级状态机。如图12-4所示,客户进行升级操作之后,集群的状态会从“运行中”转换为“升级中”状态,如果升级顺利结束,则集群会转入“运行中”状态。
如果客户主动暂停升级,或者遭遇升级失败,那么集群会转为集群暂停状态。在升级暂停状态中的集群会暂停集群升级的进程,已经开始升级的节点会继续升级直到完成,没开始升级的节点则不会继续升级。
在暂停状态之下,我们可以将集群状态转为“升级中”,也可以取消本次升级。取消升级之后集群的状态会转为“运行中”。这时已经完成升级的节点不会回滚,未升级的节点也不会继续升级。
图12-4 集群升级状态机 (此处原为状态转换图,描述运行中 → 升级中 → 运行中 / 暂停状态 → 升级中 / 取消 → 运行中等转换)
整体升级过程如图12-5所示,可以划分为如下三个步骤:
- 滚动升级Master节点。
- 分批升级Worker节点。
- 升级核心系统组件。
图12-5 集群升级过程 (此处原为流程图,描述三步式升级过程)
12.3.1 升级Master节点
升级集群的第一步是升级Master节点。因为Kubernetes社区会保证Master对Kubelet有向下两个版本的兼容性,例如1.16版的Master可以兼容1.14版的Kubelet。但是Kubernetes社区不保证低版本Kubelet对高版本Master的兼容性。
在前面章节我们介绍过,Master上运行的主要是kube-apiserver、kube-scheduler和kube-controller-manager三大管控组件。升级Master主要就是对这三大组件的升级。
在专有版集群中: Master三大组件是依靠静态Pod机制运行的,所以升级过程中会依次在所有Master节点上进行如下操作:
- 备份节点上的相关配置,主要包括系统组件的Yaml文件。
- 修改节点上的配置文件,以适应新版本的Kubernetes。
- 升级节点上的kubeadm(升级static Pod的工具)。
- 使用kubeadm将static Pod升级到目标版本。
- 升级节点上的GPU相关配置(如果有的话)。
- 根据备份信息对全新的static Pod进行个性化配置。
在托管版集群中: Master是部署在管控集群中的三个Deployment。我们在对Master进行升级的时候,需要将这三个Deployment更新为目标版本。
12.3.2 升级Worker节点
在完成Master升级之后,我们就可以开始对Worker节点进行升级了。节点的版本是通过节点上的Kubelet进行上报的,也就是说Kubelet的版本号决定了节点的版本号。
节点升级的主要工作,是升级Kubelet核心组件及其相关配置,具体操作如下:
- 备份节点上的相关配置,包括Kubelet的配置文件等。
- 将节点上的CNI升级到与目标版本相匹配的版本。
- 将节点上的Kubectl升级到目标版本。
- 将节点上的Kubelet升级到目标版本。
- 根据节点的信息和Kubelet的目标版本对其进行个性化配置。
为了控制节点的升级节奏,更好地控制原地升级带来的风险,我们自主研发了用于控制升级任务的CRD。这个CRD的开源版本就是OpenKruise中的BroadcastJob。通过使用该CRD的能力,用户可以对升级过程执行以下精细化的操作:
- 升级暂停:暂停正在进行的升级任务,已经开始升级的节点会完成升级,没开始升级的节点会暂停升级。
- 升级恢复:继续整个升级流程,开始对未升级的节点进行升级。
- 升级取消:在暂停整个升级流程后,可以取消本次集群升级。取消升级后,已经完成升级的节点不会回滚,尚未进行升级的节点不会进行升级。
- 批量升级:可以对每个批次升级的节点数目进行控制。
- 慢启动:再开始启动升级后,第一批次先对一个节点进行升级,后面每批次升级的节点数按2的幂指数规律增长,直到达到设定的批次最大值(默认为10%)。
12.3.3 核心组件升级
在成功升级Master与Worker之后,我们会对集群中的核心组件进行升级。目前跟随Kubernetes一起进行升级的核心组件主要为kube-proxy和CoreDNS。
- kube-proxy:版本号是跟随Kubernetes集群的版本进行统一管理的,两者的版本号是一致的。我们在升级Kubernetes集群的过程中,会将kube-proxy升级到相应的版本。
- CoreDNS:有着自己的版本声明管理周期,其版本号并不与Kubernetes保持一致。为了保证CoreDNS与Kubernetes的版本兼容性,Kubernetes社区为CoreDNS的版本与Kubernetes版本设定了一个对应矩阵。在升级Kubernetes集群的过程中,我们会按照版本矩阵将CoreDNS升级到对应的版本。
12.4 总结
随着软件的不断更新与迭代,如何安全、“平滑”地将现存软件系统升级到最新的版本也越来越受到广大用户的重视。
Kubernetes作为云原生操作系统,其分布式的属性,以及快速的迭代更新,都对集群本身的升级带来不少困难。
本章我们分别从升级预检、升级的两种典型方式,以及升级三部曲等角度,对阿里云容器服务Kubernetes的升级设计做了深入的剖析,希望对读者理解和使用容器产品有所帮助。