前言
本文主要会介绍笔者在学习Kubernetes Deployment Controller的实现原理时所总结的知识点,其中会涉及到ReplicaSet对象、控制循环实现细节、滚动更新与版本回退实现细节
等方面的相关内容。
笔者也会将自己的理解在文中进行阐述,这也算是在和大家交流心得的一个过程。若文中有错误的理解和概念,请大家及时纠正;吸纳大家的建议,对于我来说也是很重要的学习过程之一。
(目录)
1.概念
Deployment为Kubernetes提供了对Pod的水平扩展/收缩(horizontal scaling out/in)
。同时,其还能够基于“滚动更新”(rolling update)的方式来升级Pod中的容器。
基于Deployment的特性,其更多用于对无状态服务的管理
。
2.实现原理
Deployment对象并不是直接管理Pod对象的,而是通过ReplicaSet对象来管理Pod对象。对于Pod的弹性伸缩以及滚动更新,实际上都是通过ReplicaSet对象来实现的。即Deployment控制ReplicaSet(版本),ReplicaSet控制 Pod(副本数)
。
2.1 ReplicaSet对象
一个ReplicaSet对象由副本数目的定义和一个Pod模板组成
的。Deployment只是在ReplicaSet的基础上添加了UP-TO-DATE状态属性,该属性用于跟版本控制。
Tips: ReplicaSet可以理解为是Deployment的一个子集。
Pod的ownerReference实际上是ReplicaSet
,而不是Deployment。因为实际上管理Pod的对象是ReplicaSet对象。
例如:
apiVersion: apps/v1
kind: ReplicaSet
metadata:
name: frontend
labels:
app: guestbook
tier: frontend
spec:
# 按你的实际情况修改副本数
replicas: 3
selector:
matchLabels:
tier: frontend
template:
metadata:
labels:
tier: frontend
spec:
containers:
- name: php-redis
image: gcr.io/google_samples/gb-frontend:v3
2.2 对象关系
Deployment通过控制器模式来控制ReplicaSet的个数和属性
,从而实现“水平扩展 / 收缩”和“滚动更新”这两个编排动作。
ReplicaSet通过控制器模式保证系统中Pod的个数
永远等于指定的个数。
Tips: Deployment的这种实现形式,即是Kubernetes中“用对象管理对象”设计理念的一种体现。
2.3 水平伸缩
Deployment中的水平伸缩是通过Deployment Controller修改它所控制的ReplicaSet的Pod副本个数来实现的。
2.4 滚动更新
2.4.1 实现细节
在用户提交了一个Deployment对象后,Deployment Controller会立即创建一个Pod副本个数为用户期望数量的ReplicaSet对象。ReplicaSet的名字是由Deployment的名字和一个随机字符串(pod-template-hash)
共同组成。ReplicaSet会将pod-template-hash会加在所有管理的Pod的标签里
,从而保证这些Pod不会与集群里的其他Pod混淆。
当修改了Deployment对象中的Pod template部分
后就会触发滚动更新机制。
滚动更新实现细节为:
-
创建ReplicaSet对象 Deployment Controller会使用修改后的Pod模板
创建一个新的ReplicaSet对象
,新ReplicaSet对象的初始Pod副本数是:0
。 -
增加新ReplicaSet对象副本数 Deployment Controllers会
将新ReplicaSet对象所控制的Pod副本数加一
,即创建一个包含新版本容器的Pod。 -
减少旧ReplicaSet对象副本数 Deployment Controller同时又会
将旧ReplicaSet对象所控制的旧Pod副本数减一
,即删除一个包含新版本容器的Pod。
在之后的过程中,Deployment Controller会一直重复执行步骤2与步骤3
,直到新ReplicaSet对象能够满足用户的期望状态。
滚动更新机制就是将一个集群中正在运行的多个Pod版本交替地逐一升级的过程。通过利用多个ReplicaSet对象,Kubernetes就可以实现对多个“应用版本”的描述。即每一个ReplicaSet对象代表了一个应用程序的指定版本。
Tips: 笔者认为,之所以Deployment对象选择使用ReplicaSet对象来管理Pod对象,而不是自身亲自管理Pod对象;原因就在于通过ReplicaSet对象管理可以实现通过一个Deployment对象来管理应用程序多个版本的更新和回退。即
抽象出ReplicaSet对象这个概念,可以将Deployment对象与Pod对象进行解耦,使得Deployment对象可以更加关注于对应用程序版本更新/回退的管控,而弹性伸缩的细节就可以让ReplicaSet对象来协助管理
。如果Deployment对象只需要提供Pod弹性伸缩的功能,则本质上是不需要ReplicaSet对象的。经过这样对象关系抽象后,在Kubernetes中就可以使用Deployment对象来表示实际项目中的应用服务/程序的概念了。
2.4.2 版本回退
默认情况下,每修改一次Deployment对象,Kubernetes就会为这次修改标记一个版本号。因此,如果想回退到更早的版本,可以通过Kubernetes记录的版本号来进行回退。
版本回滚操作实际上就是Deployment Controller将旧版本的ReplicaSet再次建立并进行扩展
,使其内部的Pod状态更改为用户期望的状态,而让当前版本的ReplicaSet对象重新收缩到0个Pod
。
Tips: 版本回退的底层操作实际上与滚动更新是类似的。即将某一个ReplicaSet对象建立并扩展,同时对另一个ReplicaSet对象进行缩减并删除。
在默认情况下,如果对Deployment对象进行频繁修改,则会导致产生多个版本;从而会产生多个ReplicaSet对象。为了防止产生过多的ReplicaSet对象,可以在更新Deployment对象前事先关闭Deployment的rollout功能
;即暂时关闭其滚动更新功能。在完成所有对Deployment对象的修改后,再将其滚动更新功能开启,这样最后就只会产生一个版本。随着时间的推移,Deployment对象的版本一定还是会逐渐增多,此时可以通过设定Deployment对象中的spec.revisionHistoryLimit属性来控制生成的ReplicaSet对象的最大数量
来解决该问题。
2.4.3 保证业务服务的连续性
使用滚动更新的前提
是使用Pod的Health Check机制,即需要使用readinessProbe探针来检查容器内部的应用程序是否已经正常启动
。因为如果容器已经变成Running状态,但服务并没有启动,这就表示进行再多次的滚动更新都是无效之举。
Deployment Controller能够确保在任何时间窗口内,只有指定比例的Pod处于离线状态;同时,它也会确保在任何时间窗口内,只有指定比例的新Pod被创建出来。这个比例值是由Deployment对象的RollingUpdateStrategy属性
来进行配置的,两种状况的默认比例值(DESIRED)都是25%。
2.5 控制循环
Deployment Controller的Control Loop核心逻辑为:
-
获取相关Deployment对象的
实际状态
通过调用Kubernetes API来进行获取。 -
获取相关Deployment对象的
期望状态
使用Informer组件来从API Server中获取。 -
分析当前状况
比较两个状态,判断是否需要进行滚动更新或扩展/缩减 Pod 数量
。如果是副本数量(replica)发生了变化,则进行水平扩缩。如果是Pod template发生了变化,则进行滚动更新。如果两个状态相同,则保持现状。 -
(可选)滚动更新 在滚动更新过程中,Deployment Controller会持续监控新旧Pod副本的状态。如果有问题出现,它会回滚到之前的状态,以确保稳定性。
-
(可选)水平扩缩 如果决策结果是需要水平扩缩,则Deployment Controller会更新相应的ReplicaSet对象,使其对Pod进行扩缩。
-
操作记录 一旦新的Pod副本成功部署并运行,Deployment Controller会将更新的状态与期望状态匹配并标记更新为完成。