您的当前位置:首页正文

【阿里云&CNCF视频系列】第6课-应用编排与管理: Deployment(管理部署发布的控制器)

2023-02-10 来源:爱站旅游
导读【阿里云&CNCF视频系列】第6课-应用编排与管理: Deployment(管理部署发布的控制器)

目录

 


 

0.引入

1.需求来源

1.1背景问题

1.2解决方案

可以看到我们通过 Deployment 将应用A、B、C分别规划到不同的Deployment中,每个Deployment其实
是管理的一组相同的应用Pod,这组Pod我们认为它是相同的一个副本,那么Deployment能帮我们做什么事
情呢?

首先,Deployment定义了一种Pod期望数量,比如说应用A,我们期望Pod数量是四个,那么这样的话,
controller就会持续维持Pod数量为期望的数量.当我们与Pod出现了网络问题或者宿主机问题的话,
controller能帮我们恢复,也就是新扩出来对应的Pod,来保证可用的Pod数量与期望数量一致;


配置Pod发布方式,也就是说controller会按照用户给定的策略来更新Pod,而且更新过程中,也可以设定
不可用Pod数量在多少范围内;


如果更新过程中发生问题的话,即所谓“一键”回滚,也就是说你通过一条命令或者一行修改能够将Deployment 
下面所有Pod更新为某一个旧版本.

2.用例解读

2.1Deployment语法

上图可以看到一个最简单的Deployment的yaml文件.

“apiVersion:apps/v1”,也就是说Deployment当前所属的组是apps,版本是v1.“metadata”是我们看到的 
Deployment元信息,也就是往期回顾中的Labels、Selector、Pod.image,这些都是在往期中提到的知识点.


Deployment作为一个K8s资源,它有自己的metadata元信息,这里我们定义的Deployment.name是nginx.Deployment.

Deployment.spec中首先要有一个核心的字段,即replicas,这里定义期望的Pod数量为三个;
selector其实是Pod选择器,那么所有扩容出来的Pod,它的Labels必须匹配selector层上的image.labels,
也就是app.nginx。

就如上面的Pod模板template中所述,这个template它其实包含了两部分内容:

(1)我们期望Pod的metadata,其中包含了labels,即跟selector.matchLabels相匹配的一个Labels;
(2)template包含的一个 Pod.spec,这里 Pod.spec其实是 Deployment,最终创建出来Pod的时候,
它所用的Pod.spec,这里定义了一个container.nginx,它的镜像版本是 nginx:1.7.9.

下面是遇到的新知识点:
(1)replicas-是Deployment中期望的或者终态数量;
(2)template-是Pod 相关的一个模板.

 

2.2查看Deployment状态

当我们创建出一个Deployment的时候,可以通过kubectl get deployment,看到Deployment总体的一个状态.
上图中可以看到:
DESIRED:期望的Pod数量是3个;
CURRENT:当前实际Pod数量是3个;
UP-TO-DATE:其实是到达最新的期望版本的Pod数量;
AVAILABLE:这个其实是运行过程中可用的Pod数量,后面会提到,这里AVAILABLE并不简单是可用的,也就是 
Ready状态的,它其实包含了一些可用超过一定时间长度的Pod;
AGE:deployment创建的时长,如上图Deployment就是已经创建了80分钟.

2.3查看Pod

最后我们可以查看一下Pod,如上图所示:
上图中有三个Pod,从Pod名字格式我们不难看到.

最前面一段:nginx-deployment,其实是Pod所属Deployment.name;
中间一段:template-hash,这里三个Pod是一样的,因为这三个Pod其实都是同一个template中创建出来的.


最后一段,是一个random的字符串,我们通过get.pod可以看到,Pod的ownerReferences即Pod所属的 
controller资源,并不是Deployment,而是一个 ReplicaSet.这个ReplicaSet的name,其实是nginx-
deployment加上pod.template-hash,后面会提到.所有的Pod都是ReplicaSet创建出来的,
而ReplicaSet它对应的某一个具体的Deployment.template版本.

 

2.4更新镜像

接下来我们可以看一下,如何对一个给定的Deployment更新它所有Pod的镜像版本呢?这里我们可以执行一个
kubectl命令:

kubectl set image deployment.v1.apps/nginx-deployment nginx=nginx:1.9.1


首先 kubectl 后面有一个set image 固定写法,这里指的是设定镜像;其次是一个 deployment.v1.apps,
这里也是一个固定写法,写的是我们要操作的资源类型,deployment 是资源名、v1 是资源版本、apps是资源
组,这里也可以简写为deployment或者deployment.apps,比如说写为deployment的时候,默认将使用apps组 
v1版本.

第三部分是要更新的deployment的name,也就是我们的nginx-deployment;再往后的nginx其实指的是 
template,也就是Pod中的container.name;这里我们可以注意到:一个Pod 中,其实可能存在多个container,
而我们指定想要更新的镜像的container.name,就是nginx.

最后,指定我们这个容器期望更新的镜像版本,这里指的是nginx:1.9.1.
如下图所示:当执行完这条命令之后,可以看到deployment中的template.spec已经更新为nginx: 1.9.1。

2.5快速回滚

如果我们在发布过程中遇到了问题,也支持快速回滚.
通过kubectl执行的话,其实是“kubectl rollout undo”这个命令,可以回滚到Deployment 上一版本;
通过“rollout undo”加上“to-revision”来指定可以回滚到某一个具体的版本.

 

2.6DeploymentStatus(部署状态)

前面的课程我们学习到,每一个资源都有它的spec.Status.这里可以看一下,deploymentStatus中描述的
三个其实是它的conversion状态,也就是Processing、Complete以及Failed.

以 Processing 为例:Processing 指的是Deployment正在处于扩容和发布中.比如说Processing状态的 
deployment,它所有的replicas及Pod副本全部达到最新版本,而且是available,这样的话,就可以进入
complete状态.而complete状态如果发生了一些扩缩容的话,也会进入processing这个处理工作状态.

如果在处理过程中遇到一些问题:比如说拉镜像失败了,或者说 readiness probe检查失败了,就会进入 
failed 状态;如果在运行过程中即complete状态,中间运行时发生了一些pod readiness probe检查失
败,这个时候deployment也会进入failed状态.进入failed状态之后,除非所有点replicas均变成 
available,而且是updated最新版本,deployment才会重新进入complete状态.

3.操作演示

3.1查看当前集群的nodes

3.2创建对应的 deployment

创建对应的deployment,可以看到deployment中的desired、current、up-to-date以及available
已经都达到了可用的期望状态。

3.3查看Deployment 的结构 

用kubectl edit deployment nginx-deployment(:q退出编辑)查看Deployment的结构.
这里看到spec中的replicas是三个,selector以及template labels中定义的标签都是app:nginx,
spec中的image是我们期望的nginx: 1.7.9;status中的available.replicas,readReplicas以及
updatedReplicas都是3个.

3.4查看pods

3.5选择一个Pod看一下状态

我们可以再选择一个Pod看一下状态,命令如3.4中所示.

可以看到:
Pod中ownerReferences的功能(kind那一项)是ReplicaSet;
pod.spec.container 里的镜像是 1.7.9.
这个Pod已经是Running状态,而且它的conditions.status是“true”,表示它的服务已经可用了.

3.6查看最新的副本集合并升级deployment

kubectl set image”这个操作命令,后面接“deployment”,加deployment.name,最后指定容器名,以及我
们期望升级的镜像版本.

接下来我们看下deployment中的template中的image已经更新为1.9.1

这个时候我们再 get pod 看一下状态:

三个 pod 已经升级为新版本,pod 名字中的 pod-template-hash 也已更新:

可以看到:旧版本 replicaset 的 spec 数量以及 pod 数量是都是 0,新版本的 pod 数量是 3 个。

假设又做了一次更新,这个时候 get.pod 其实可以看到:当前的 pod 其实是有两个旧版本的处于 running,另一个旧版本是在删除中;

而两个新版本的 pod,一个已经进入 running,一个还在 creating 中。

这时我们可用的 pod 数量即非删除状态的 pod 数量,其实是 4 个,已经超过了 replica 原先在 deployment 设置的数量 3 个。

这个原因是我们在 deployment 中有 maxavailable 和 maxsugar 两个操作,这两个配置可以限制我们在发布过程中的一些策略。在后面架构设计中会讲到这个问题。

3.7历史版本保留 revisionHistoryLimit

上图看到,我们当前最新版本的 replicaset 是 3 个 pod,另外还有两个历史版本的 replicaset,那么会不会存在一种情况:就是随着 deployment 持续的更新,

这个旧版本的 replicaset 会越积越多呢?其实 deployment 提供了一个机制来避免这个问题:在 deployment spec 中,有一个 revisionHistoryLimit,它的默认

值为 10,它其实保证了保留历史版本的 replicaset 的数量,我们尝试把它改为 1。

由上面第二张图,可以看到两个 replicaset,也就是说,除了当前版本的 replicaset 之外,旧版本的 replicaset 其实只保留了一个。

3.8回滚

最后再尝试做一下回滚。首先再来看一下 replicaset,这时发现旧版本的 replicaset 数量从 0 个增到 2 个,而新版本的 replicaset 数量从 3 个削减

为 2 个,表示它已经开始在做回滚的操作。然后再观察一下, 旧版本的数量已经是 3 个,即已经回滚成功,而新版本的 pod 数量变为 0 个。

我们最后再 get pod 看一下:

这时,3 个 pod.template-hash 已经更新为旧版本的 hash,但其实这 3 个 pod 都是重新创建出来的,而并非我们在前一版本中创建的 3 个 pod。换句话说,

也就是我们回滚的时候,其实是创建了 3 个旧版本的 pod,而并非把先前的 3 个 pod 找回来。

3.9命令总结

kubectl create -f nginx-deployment.yaml	按照配置文件nginx-deployment.yaml进行部署
kubectl get deployment	查看部署信息
kubectl edit deployment nginx-deployment	编辑部署文件nginx-deployment,:q退出
kubectl get pod	查看pod的情况
kubectl get pod/nginx-deployment-5c689d88bb-ck974 -o yaml	查看编号为nginx-deployment-5c689d88bb-ck974的pod的情况并输出到yaml文件中
kubectl edit pod nginx-deployment-5c689d88bb-ck974	编辑编号为nginx-deployment-5c689d88bb-ck974的pod,:q退出
kubectl set image deployment.v1.apps/nginx-deployment nginx=nginx:1.9.1	更新镜像
kubectl rollout undo deployment/nginx-deployment	回滚到当前部署的上一个版本
kubectl rollout undo deployment.v1.apps/nginx-deployment --to-revision=2	回滚到某一个具体的版本
kubectl get nodes	查看当前集群的nodes

 

4.架构设计

4.1管理模式

管理模式:Deployment 只负责管理不同版本的 ReplicaSet,由 ReplicaSet 来管理具体的 Pod 副本数,每
个 ReplicaSet 对应 Deployment template 的一个版本。在上文的例子中可以看到,每一次修改 
template,都会生成一个新的 ReplicaSet,这个 ReplicaSet 底下的 Pod 其实都是相同的版本。


如上图所示:Deployment 创建 ReplicaSet,而 ReplicaSet 创建 Pod。他们的 OwnerRef 其实都对应了其
控制器的资源。

 

4.2Deployment 控制器

我们所有的控制器都是通过 Informer 中的 Event 做一些 Handler 和 Watch。这个地方 Deployment 控制
器,其实是关注 Deployment 和 ReplicaSet 中的 event,收到事件后会加入到队列中。而 Deployment 
controller 从队列中取出来之后,它的逻辑会判断 Check Paused,这个 Paused 其实是 Deployment 是否
需要新的发布,如果 Paused 设置为 true 的话,就表示这个 Deployment 只会做一个数量上的维持,不会做
新的发布。


如上图,可以看到如果 Check paused 为 Yes 也就是 true 的话,那么只会做 Sync replicas。也就是说把
 replicas sync 同步到对应的 ReplicaSet 中,最后再 Update Deployment status,那么 controller 
这一次的 ReplicaSet 就结束了。


那么如果 paused 为 false 的话,它就会做 Rollout,也就是通过 Create 或者是 Rolling 的方式来做更
新,更新的方式其实也是通过 Create/Update/Delete 这种 ReplicaSet 来做实现的。

4.3ReplicaSet 控制器

如上图,可以看到如果 Check paused 为 Yes 也就是 true 的话,那么只会做 Sync replicas。也就是说把 
replicas sync 同步到对应的 ReplicaSet 中,最后再 Update Deployment status,那么 controller 这
一次的 ReplicaSet 就结束了。

那么如果 paused 为 false 的话,它就会做 Rollout,也就是通过 Create 或者是 Rolling 的方式来做更
新,更新的方式其实也是通过 Create/Update/Delete 这种 ReplicaSet 来做实现的。


当 Deployment 分配 ReplicaSet 之后,ReplicaSet 控制器本身也是从 Informer 中 watch 一些事件,这
些事件包含了 ReplicaSet 和 Pod 的事件。从队列中取出之后,ReplicaSet controller 的逻辑很简单,就
只管理副本数。也就是说如果 controller 发现 replicas 比 Pod 数量大的话,就会扩容,而如果发现实际
数量超过期望数量的话,就会删除 Pod。

上面 Deployment 控制器的图中可以看到,Deployment 控制器其实做了更复杂的事情,包含了版本管理,而它
把每一个版本下的数量维持工作交给 ReplicaSet 来做。

4.4扩容模拟 

下面来看一些操作模拟,比如说扩容模拟。这里有一个 Deployment,它的副本数是 2,对应的 ReplicaSet 
有 Pod1 和 Pod2。这时如果我们修改 Deployment replicas, controller 就会把 replicas 同步到当前
版本的 ReplicaSet 中,这个 ReplicaSet 发现当前有 2 个 Pod,不满足当前期望 3 个,就会创建一个新的 
Pod3。

4.5发布模拟

我们再模拟一下发布,发布的情况会稍微复杂一点。这里可以看到 Deployment 当前初始的 template,比如说 
template1 这个版本。template1 这个 ReplicaSet 对应的版本下有三个 Pod:Pod1,Pod2,Pod3。

这时修改 template 中一个容器的 image, Deployment controller 就会新建一个对应 template2 的 
ReplicaSet。创建出来之后 ReplicaSet 会逐渐修改两个 ReplicaSet 的数量,比如它会逐渐增加 
ReplicaSet2 中 replicas 的期望数量,而逐渐减少 ReplicaSet1 中的 Pod 数量。

那么最终达到的效果是:新版本的 Pod 为 Pod4、Pod5和Pod6,旧版本的 Pod 已经被删除了,这里就完成了一
次发布。

4.6回滚模拟

来看一下回滚模拟,根据上面的发布模拟可以知道 Pod4、Pod5、Pod6 已经发布完成。这时发现当前的业务版
本是有问题的,如果做回滚的话,不管是通过 rollout 命令还是通过回滚修改 template,它其实都是把 
template 回滚为旧版本的 template1。

这个时候 Deployment 会重新修改 ReplicaSet1 中 Pod 的期望数量,把期望数量修改为 3 个,且会逐渐减
少新版本也就是 ReplicaSet2 中的 replica 数量,最终的效果就是把 Pod 从旧版本重新创建出来。


发布模拟的图中可以看到,其实初始版本中 Pod1、Pod2、Pod3 是旧版本,而回滚之后其实是 Pod7、Pod8、
Pod9。就是说它的回滚并不是把之前的 Pod 重新找出来,而是说重新创建出符合旧版本 template 的 Pod。

4.7 spec 字段解析

最后再来简单看一些 Deployment 中的字段解析。首先看一下 Deployment 中其他的 spec 字段:

MinReadySeconds:Deployment 会根据 Pod ready 来看 Pod 是否可用,但是如果我们设置了 
MinReadySeconds 之后,比如设置为 30 秒,那 Deployment 就一定会等到 Pod ready 超过 30 秒之后才认
为 Pod 是 available 的。Pod available 的前提条件是 Pod ready,但是 ready 的 Pod 不一定是 
available 的,它一定要超过 MinReadySeconds 之后,才会判断为 available;

revisionHistoryLimit:保留历史 revision,即保留历史 ReplicaSet 的数量,默认值为 10 个。这里可以
设置为一个或两个,如果回滚可能性比较大的话,可以设置数量超过 10;


paused:paused 是标识,Deployment 只做数量维持,不做新的发布,这里在 Debug 场景可能会用到;


progressDeadlineSeconds:前面提到当 Deployment 处于扩容或者发布状态时,它的 condition 会处于一
个 processing 的状态,processing 可以设置一个超时时间。如果超过超时时间还处于 processing,那么 
controller 将认为这个 Pod 会进入 failed 的状态

4.8升级策略字段解析

升级策略字段解析
最后来看一下升级策略字段解析。

Deployment 在 RollingUpdate 中主要提供了两个策略,一个是 MaxUnavailable,另一个是 MaxSurge。这两个字段解析的意思,可以看下图中详细的 comment,或者简单解释一下:

MaxUnavailable:滚动过程中最多有多少个 Pod 不可用;
MaxSurge:滚动过程中最多存在多少个 Pod 超过预期 replicas 数量。
上文提到,ReplicaSet 为 3 的 Deployment 在发布的时候可能存在一种情况:新版本的 ReplicaSet 和旧版本的 ReplicaSet 都可能有两个 replicas,加在一起就是 4 个,超过了我们期望的数量三个。这是因为我们默认的 MaxUnavailable 和 MaxSurge 都是 25%,默认 Deployment 在发布的过程中,可能有 25% 的 replica 是不可用的,也可能超过 replica 数量 25% 是可用的,最高可以达到 125% 的 replica 数量。

这里其实可以根据用户实际场景来做设置。比如当用户的资源足够,且更注重发布过程中的可用性,可设置 MaxUnavailable 较小、MaxSurge 较大。但如果用户的资源比较紧张,可以设置 MaxSurge 较小,甚至设置为 0,这里要注意的是 MaxSurge 和 MaxUnavailable 不能同时为 0。

理由不难理解,当 MaxSurge 为 0 的时候,必须要删除 Pod,才能扩容 Pod;如果不删除 Pod 是不能新扩 Pod 的,因为新扩出来的话,总共的 Pod 数量就会超过期望数量。而两者同时为 0 的话,MaxSurge 保证不能新扩 Pod,而 MaxUnavailable 不能保证 ReplicaSet 中有 Pod 是 available 的,这样就会产生问题。所以说这两个值不能同时为 0。用户可以根据自己的实际场景来设置对应的、合适的值。

5.课后思考实践

练习本节内容.

 

 

 

因篇幅问题不能全部显示,请点此查看更多更全内容