Kubernetes 工作负载控制器Controller Deployment

网友投稿 694 2022-09-04

Kubernetes 工作负载控制器Controller Deployment

Kubernetes 工作负载控制器Controller Deployment

一个 Pod 被创建出来,不管是由你直接创建,还是由其他工作负载控制器(Workload Controller)自动创建,经过调度器调度以后,就永久地“长”在某个节点上了,直到该 Pod 被删除,或者因为资源不够被驱逐,抑或由于对应的节点故障导致宕机等。因此单独地用一个 Pod 来承载业务,是没办法保证高可用、可伸缩、负载均衡等要求,而且 Pod 也无法“自愈”。

这时我们就需要在 Pod 之上做一层抽象,通过多个副本(Replica)来保证可用 Pod 的数量,避免业务不可用。在介绍 Kubernetes 中对这种抽象的实现之前,我们先来看看应用的业务类型。

有状态服务 VS 无状态服务 一般来说,业务的服务类型可分为无状态服务和有状态服务。举个简单的例子,像打网络游戏这类的服务,就是有状态服务,而正常浏览网页这类服务一般都是无状态服务。其实判断两种请求的关键在于,两个来自相同发起者的请求在服务器端是否具备上下文关系。

如果是有状态服务,其请求是状态化的,服务器端需要保存请求的相关信息,这样每个请求都可以默认地使用之前的请求上下文。而无状态服务就不需要这样,每次请求都包含了需要的所有信息,每次请求都和之前的没有任何关系。

有状态服务和无状态服务分别有各自擅长的业务类型和技术优势,在 Kubernetes 中,分别有不同的工作负载控制器来负责承载这两类服务。

这篇文章,我们先介绍 Kubernetes 中的无状态工作负载。

Kubernetes 中的无状态工作负载

Kubernetes 中各个对象的 metadata 字段都有 label(标签)和 annotation(注解) 两个对象,可以用来标识一些元数据信息。不论是 label 还是 annotation,都是一组键值对,键值不允许相同。

annotation 主要用来记录一些非识别的信息,并不用于标识和选择对象。label 主要用来标识一些有意义且和对象密切相关的信息,用来支持labelSelector(标签选择器)以及一些查询操作,还有选择对象。

为了让这种抽象的对象可以跟 Pod 关联起来,Kubernetes 使用了labelSelector来跟 label 进行选择匹配,从而达到这种松耦合的关联效果。

$ kubectl get pod -l label1=value1,label2=value2 -n my-namespace

比如,我们就可以通过上述命令,查询出 my-namespace 这个命名空间下面,带有标签​​label1=value1​​​和​​label2=value2​​的 pod。label 中的键值对在匹配的时候是“且”的关系。

ReplicationController

Kubernetes 中有一系列的工作负载可以用来部署无状态服务。在最初,Kubernetes 中使用了ReplicationController来做 Pod 的副本控制,即确保该服务的 Pod 数量维持在特定的数量。为了简洁并便于使用,ReplicationController通常缩写为“rc”,并作为 kubectl 命令的快捷方式,例如:

$ kubectl get rc -n my-namespace

如果副本数少于预定的值,则创建新的 Pod。如果副本数大于预定的值,就删除多余的副本。因此即使你的业务应用只需要一个 Pod,你也可以使用 rc 来自动帮你维护和创建 Pod。

ReplicaSet

随后社区开发了下一代的 Pod 控制器 ReplicaSet(可简写为 rs) 用来替代 ReplicaController。虽然 ReplicaController 目前依然可以使用,但是社区已经不推荐继续使用了。这两者的功能和目的完全相同,但是 ReplicaSet 具备更强大的​​基于集合的标签选择器​​​,这样你可以通过一组值来进行标签匹配选择。目前支持三种操作符:​​in​​​、​​notin​​​和​​exists​​。

例如,你可以用​​environment in (production, qa)​​​来匹配 label 中带有​​environment=production​​​或​​environment=qa​​的 Pod。

同样你也可以使用​​tier notin (frontend,backend)​​​来匹配 label 中不带有​​tier=frontend​​​或​​tier=backend​​的 Pod。

或者你可以用 partition来匹配 label 中带有 partition 这个 key 的 Pod。

了解了标签选择器,我们就可以通过如下的 kubectl 命令查找 Pod:

kubectl get pods -l environment=production,tier=frontend

或者使用:

kubectl get pods -l 'environment in (production),tier in (frontend)'

虽然 Replicaset 可以独立使用,但是为了能够更好地协调 Pod 的创建、删除以及更新等操作,我们都是直接使用更高级的 Deployment来管理 Replicaset,社区也是一直这么定位和推荐的。比如一些业务升级的场景,使用单一的 ReplicaController 或者 Replicaset 是无法实现滚动升级的诉求,至少需要定义两个该对象才能实现,而且这两个对象使用的标签选择器中的 label 至少要有一个不相同。通过不断地对这两个对象的副本进行增减,也可以称为调和(Reconcile),才可以完成滚动升级。这样使用起来不方便,也增加了用户的使用门槛,极大地降低了业务发布的效率。

[root@k8s-master ~]# kubectl rollout undo deployment/webdeployment.apps/web rolled back[root@k8s-master ~]# kubectl get replicaset -wNAME DESIRED CURRENT READY AGEweb-6c8f6b7f84 3 3 3 29mweb-7cc58d88d9 0 0 0 63mweb-7cc58d88d9 0 0 0 64mweb-7cc58d88d9 1 0 0 64mweb-6c8f6b7f84 1 3 3 30mweb-7cc58d88d9 1 0 0 64mweb-7cc58d88d9 3 0 0 64mweb-6c8f6b7f84 1 3 3 30mweb-6c8f6b7f84 1 1 1 30mweb-7cc58d88d9 3 1 0 64mweb-7cc58d88d9 3 1 0 64mweb-7cc58d88d9 3 3 0 64mweb-7cc58d88d9 3 3 1 64mweb-6c8f6b7f84 0 1 1 30mweb-6c8f6b7f84 0 1 1 30mweb-7cc58d88d9 3 3 2 64mweb-6c8f6b7f84 0 0 0 30mweb-7cc58d88d9 3 3 3 64m^C[root@k8s-master ~]# kubectl get replicasetNAME DESIRED CURRENT READY AGEweb-6c8f6b7f84 0 0 0 30mweb-7cc58d88d9 3 3 3 64m

Deployment

通过 Deployment,我们就不需要再关心和操作 ReplicaSet 了。

Deployment、ReplicaSet 和 Pod 这三者之间的关系见上图。通过 Deployment,我们可以管理多个 label 各不相同的 ReplicaSet,每个 ReplicaSet 负责保证对应数目的 Pod 在运行。

我们来看一个定义 Deployment 的例子:

[root@k8s-master ~]# cat deployment.yml apiVersion: apps/v1kind: Deploymentmetadata: name: nginx-deployment-demo namespace: defaultspec: replicas: 3 selector: matchLabels: app: nginx template: metadata: labels: app: nginx version: v1 spec: containers: - name: nginx image: nginx:1.14.2 ports: - containerPort: 80

我们这里定义了副本数​​spec.replicas​​​为 3,同时在​​spec.selector.matchLabels​​​中设置了​​app=nginx​​​的 label,用于匹配​​spec.template.metadata.labels​​​的 label。我们还增加了​​version=v1​​的 label 用于后续滚动升级做比较。

我们将上述内容保存到​​deploy-demo.yaml​​文件中。

注意,​​spec.selector.matchLabels​​​中写的 label 一定要能匹配得了​​spec.template.metadata.labels​​中的 label。否则该 Deployment 无法关联创建出来的 ReplicaSet。

然后我们通过下面这些命令,便可以在命名空间 demo 中创建名为 nginx-deployment-demo 的 Deployment。

[root@k8s-master ~]# kubectl apply -f deployment.yml deployment.apps/nginx-deployment-demo created[root@k8s-master ~]# kubectl get pod --show-labelsNAME READY STATUS RESTARTS AGE LABELSnginx-deployment-demo-675745c9cd-5q65l 1/1 Running 0 2m30s app=nginx,pod-template-hash=675745c9cd,version=v1nginx-deployment-demo-675745c9cd-kktkv 1/1 Running 0 2m30s app=nginx,pod-template-hash=675745c9cd,version=v1nginx-deployment-demo-675745c9cd-xbtgs 1/1 Running 0 2m30s app=nginx,pod-template-hash=675745c9cd,version=v1

创建完成后,我们可以查看自动创建出来的 rs。(ReplicaSet )

[root@k8s-master ~]# kubectl get rsNAME DESIRED CURRENT READY AGEnginx-deployment-demo-675745c9cd 3 3 3 4m10s[root@k8s-master ~]# kubectl get deployNAME READY UP-TO-DATE AVAILABLE AGEnginx-deployment-demo 3/3 3 3 6m6s

接下来我们用 ​​kubectl describe deployment​​ 了解更详细的信息。

[root@k8s-master ~]# kubectl describe deployment nginx-deployment-demoNewReplicaSet: nginx-deployment-demo-675745c9cd (3/3 replicas created)Events: Type Reason Age From Message ---- ------ ---- ---- ------- Normal ScalingReplicaSet 7m32s deployment-controller Scaled up replica set nginx-deployment-demo-675745c9cd to 3

这里告诉我们创建了一个 ReplicaSet  nginx-deployment-demo-675745c9cd,​​Events​​ 是 Deployment 的日志,记录了 ReplicaSet 的启动过程。

通过上面的分析,也验证了 Deployment 通过 ReplicaSet 来管理 Pod 的事实。接着我们将注意力切换到 nginx-deployment-demo-675745c9cd,执行 ​​kubectl describe replicaset​​:

[root@k8s-master ~]# kubectl describe replicaset nginx-deployment-demo-675745c9cdControlled By: Deployment/nginx-deployment-demoEvents: Type Reason Age From Message ---- ------ ---- ---- ------- Normal SuccessfulCreate 10m replicaset-controller Created pod: nginx-deployment-demo-675745c9cd-xbtgs Normal SuccessfulCreate 10m replicaset-controller Created pod: nginx-deployment-demo-675745c9cd-5q65l Normal SuccessfulCreate 10m replicaset-controller Created pod: nginx-deployment-demo-675745c9cd-kktkv

​​Controlled By​​​ 指明此 ReplicaSet 是由 Deployment ​​nginx-deployment​​​ 创建。​​Events​​​ 记录了三个副本 Pod 的创建。接着我们来看 Pod,执行 ​​kubectl get pod​​:

[root@k8s-master ~]# kubectl get podNAME READY STATUS RESTARTS AGEnginx-deployment-demo-675745c9cd-5q65l 1/1 Running 0 16mnginx-deployment-demo-675745c9cd-kktkv 1/1 Running 0 16mnginx-deployment-demo-675745c9cd-xbtgs 1/1 Running 0 16m

两个副本 Pod 都处于 ​​Running​​​ 状态,用 ​​kubectl describe pod​​ 查看更详细的信息:

[root@k8s-master ~]# kubectl describe pod nginx-deployment-demo-675745c9cd-5q65l Controlled By: ReplicaSet/nginx-deployment-demo-675745c9cd[root@k8s-master ~]# kubectl describe pod nginx-deployment-demo-675745c9cd-kktkv Controlled By: ReplicaSet/nginx-deployment-demo-675745c9cd[root@k8s-master ~]# kubectl describe pod nginx-deployment-demo-675745c9cd-xbtgsControlled By: ReplicaSet/nginx-deployment-demo-675745c9cd

​​Controlled By​​​ 指明此 Pod 是由 ReplicaSet ​​nginx-deployment-demo-675745c9cd​​​ 创建。​​Events​​ 记录了 Pod 的启动过程。如果操作失败(比如 image 不存在),也能在这里查看到原因。

总结一下这个过程:

用户通过​​kubectl​​ 创建 Deployment。Deployment 创建 ReplicaSet。ReplicaSet 创建 Pod。

从上图也可以看出,对象的命名方式是:​​子对象的名字​​​ = ​​父对象名字​​​ + ​​随机字符串或数字​​。

更新镜像

现在我们试着做下镜像更新看看。更改​​spec.template.metadata.labels​​​中的​​version=v1​​​为​​version=v2​​​,同时更新镜像​​nginx:1.14.2​​​为​​nginx:1.19.2​​。

你可以直接通过下述命令来直接更改:

$ kubectl edit deploy nginx-deployment-demo

也可以更改​​deploy-demo.yaml​​这个文件:

[root@k8s-master ~]# cat deployment.yml apiVersion: apps/v1kind: Deploymentmetadata: name: nginx-deployment-demo namespace: defaultspec: replicas: 3 selector: matchLabels: app: nginx template: metadata: labels: app: nginx version: v2 spec: containers: - name: nginx image: nginx:1.19.2 ports: - containerPort: 80

然后运行这些命令:

[root@k8s-master ~]# kubectl apply -f deployment.yml deployment.apps/nginx-deployment-demo configured

这个时候,我们来看看 ReplicaSet 有什么变化:

[root@k8s-master ~]# kubectl get rs -wNAME DESIRED CURRENT READY AGEnginx-deployment-demo-675745c9cd 3 3 3 28m#下面为执行apply之后的状态nginx-deployment-demo-f6bf65976 1 0 0 0snginx-deployment-demo-f6bf65976 1 0 0 0snginx-deployment-demo-f6bf65976 1 1 0 0snginx-deployment-demo-f6bf65976 1 1 1 41snginx-deployment-demo-675745c9cd 2 3 3 30mnginx-deployment-demo-f6bf65976 2 1 1 41snginx-deployment-demo-675745c9cd 2 3 3 30mnginx-deployment-demo-675745c9cd 2 2 2 30mnginx-deployment-demo-f6bf65976 2 1 1 41snginx-deployment-demo-f6bf65976 2 2 1 41snginx-deployment-demo-f6bf65976 2 2 2 110snginx-deployment-demo-675745c9cd 1 2 2 31mnginx-deployment-demo-f6bf65976 3 2 2 110snginx-deployment-demo-675745c9cd 1 2 2 31mnginx-deployment-demo-675745c9cd 1 1 1 31mnginx-deployment-demo-f6bf65976 3 2 2 111snginx-deployment-demo-f6bf65976 3 3 2 111snginx-deployment-demo-f6bf65976 3 3 3 113snginx-deployment-demo-675745c9cd 0 1 1 31mnginx-deployment-demo-675745c9cd 0 1 1 31mnginx-deployment-demo-675745c9cd 0 0 0 31m[root@k8s-master ~]# kubectl get rsNAME DESIRED CURRENT READY AGEnginx-deployment-demo-675745c9cd 1 1 1 31mnginx-deployment-demo-f6bf65976 3 3 2 113s[root@k8s-master ~]# kubectl get rsNAME DESIRED CURRENT READY AGEnginx-deployment-demo-675745c9cd 0 0 0 33mnginx-deployment-demo-f6bf65976 3 3 3 3m52s

可以看到,这个时候自动创建了一个新的 rs 出来:nginx-deployment-demo-f6bf65976。一般 Deployment 的默认更新策略是 RollingUpdate,即先创建一个新的 Pod,并待其成功运行以后,再删除旧的。

还有一个更新策略是Recreate,即先删除现有的 Pod,再创建新的。关于 Deployment,我们还可以设置最大不可用(maxUnavailable)和最大增量(maxSurge),来更精确地控制滚动更新操作。

我建议你使用默认的策略来保证可用性。后面 Deployment 控制器会不断对这两个 rs 进行调和,直到新的 rs 副本数为 3,老的 rs 副本数为 0。

我们可以通过如下命令,能够观察到 Deployment 的调和过程。

[root@k8s-master ~]# kubectl get pod -wNAME READY STATUS RESTARTS AGEnginx-deployment-demo-675745c9cd-5q65l 1/1 Running 0 28mnginx-deployment-demo-675745c9cd-kktkv 1/1 Running 0 28mnginx-deployment-demo-675745c9cd-xbtgs 1/1 Running 0 28m#apply之后产生的nginx-deployment-demo-f6bf65976-bwjlk 0/1 Pending 0 0snginx-deployment-demo-f6bf65976-bwjlk 0/1 Pending 0 0snginx-deployment-demo-f6bf65976-bwjlk 0/1 ContainerCreating 0 0snginx-deployment-demo-f6bf65976-bwjlk 0/1 ContainerCreating 0 2snginx-deployment-demo-f6bf65976-bwjlk 1/1 Running 0 41snginx-deployment-demo-675745c9cd-xbtgs 1/1 Terminating 0 30mnginx-deployment-demo-f6bf65976-s5j6v 0/1 Pending 0 0snginx-deployment-demo-f6bf65976-s5j6v 0/1 Pending 0 0snginx-deployment-demo-f6bf65976-s5j6v 0/1 ContainerCreating 0 0snginx-deployment-demo-675745c9cd-xbtgs 1/1 Terminating 0 30mnginx-deployment-demo-675745c9cd-xbtgs 0/1 Terminating 0 30mnginx-deployment-demo-f6bf65976-s5j6v 0/1 ContainerCreating 0 2snginx-deployment-demo-675745c9cd-xbtgs 0/1 Terminating 0 30mnginx-deployment-demo-675745c9cd-xbtgs 0/1 Terminating 0 30mnginx-deployment-demo-f6bf65976-s5j6v 1/1 Running 0 69snginx-deployment-demo-675745c9cd-kktkv 1/1 Terminating 0 31mnginx-deployment-demo-f6bf65976-t5hc4 0/1 Pending 0 1snginx-deployment-demo-f6bf65976-t5hc4 0/1 Pending 0 1snginx-deployment-demo-f6bf65976-t5hc4 0/1 ContainerCreating 0 1snginx-deployment-demo-675745c9cd-kktkv 1/1 Terminating 0 31mnginx-deployment-demo-f6bf65976-t5hc4 0/1 ContainerCreating 0 2snginx-deployment-demo-675745c9cd-kktkv 0/1 Terminating 0 31mnginx-deployment-demo-f6bf65976-t5hc4 1/1 Running 0 3snginx-deployment-demo-675745c9cd-5q65l 1/1 Terminating 0 31mnginx-deployment-demo-675745c9cd-kktkv 0/1 Terminating 0 31mnginx-deployment-demo-675745c9cd-kktkv 0/1 Terminating 0 31mnginx-deployment-demo-675745c9cd-5q65l 1/1 Terminating 0 31mnginx-deployment-demo-675745c9cd-5q65l 0/1 Terminating 0 31mnginx-deployment-demo-675745c9cd-5q65l 0/1 Terminating 0 31mnginx-deployment-demo-675745c9cd-5q65l 0/1 Terminating 0 31m[root@k8s-master ~]# kubectl get pod -l version=v2,app=nginxNAME READY STATUS RESTARTS AGEnginx-deployment-demo-f6bf65976-bwjlk 1/1 Running 0 11mnginx-deployment-demo-f6bf65976-s5j6v 1/1 Running 0 10mnginx-deployment-demo-f6bf65976-t5hc4 1/1 Running 0 9m19s

写在最后

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:Laravel 底层是如何处理 HTTP 请求的?(laravel文档)
下一篇:关于redis的keys命令的性能问题
相关文章

 发表评论

暂时没有评论,来抢沙发吧~