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小时内删除侵权内容。
发表评论
暂时没有评论,来抢沙发吧~