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=value1label2=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 具备更强大的基于集合的标签选择器,这样你可以通过一组值来进行标签匹配选择。目前支持三种操作符:innotinexists

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

同样你也可以使用tier notin (frontend,backend)来匹配 label 中不带有tier=frontendtier=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/web
deployment.apps/web rolled back

[root@k8s-master ~]# kubectl get replicaset -w
NAME             DESIRED   CURRENT   READY   AGE
web-6c8f6b7f84   3         3         3       29m
web-7cc58d88d9   0         0         0       63m

web-7cc58d88d9   0         0         0       64m
web-7cc58d88d9   1         0         0       64m
web-6c8f6b7f84   1         3         3       30m
web-7cc58d88d9   1         0         0       64m
web-7cc58d88d9   3         0         0       64m
web-6c8f6b7f84   1         3         3       30m
web-6c8f6b7f84   1         1         1       30m
web-7cc58d88d9   3         1         0       64m
web-7cc58d88d9   3         1         0       64m
web-7cc58d88d9   3         3         0       64m
web-7cc58d88d9   3         3         1       64m
web-6c8f6b7f84   0         1         1       30m
web-6c8f6b7f84   0         1         1       30m
web-7cc58d88d9   3         3         2       64m
web-6c8f6b7f84   0         0         0       30m
web-7cc58d88d9   3         3         3       64m
^C[root@k8s-master ~]# kubectl get replicaset
NAME             DESIRED   CURRENT   READY   AGE
web-6c8f6b7f84   0         0         0       30m
web-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/v1
kind: Deployment
metadata:
  name: nginx-deployment-demo
  namespace: default
spec:
  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-labels
NAME                                      READY   STATUS    RESTARTS   AGE     LABELS
nginx-deployment-demo-675745c9cd-5q65l    1/1     Running   0          2m30s   app=nginx,pod-template-hash=675745c9cd,version=v1
nginx-deployment-demo-675745c9cd-kktkv    1/1     Running   0          2m30s   app=nginx,pod-template-hash=675745c9cd,version=v1
nginx-deployment-demo-675745c9cd-xbtgs    1/1     Running   0          2m30s   app=nginx,pod-template-hash=675745c9cd,version=v1

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

[root@k8s-master ~]# kubectl get rs
NAME                                DESIRED   CURRENT   READY   AGE
nginx-deployment-demo-675745c9cd    3         3         3       4m10s

[root@k8s-master ~]# kubectl get deploy
NAME                     READY   UP-TO-DATE   AVAILABLE   AGE
nginx-deployment-demo    3/3     3            3           6m6s

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

[root@k8s-master ~]# kubectl describe deployment nginx-deployment-demo
NewReplicaSet:   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-675745c9cd
Controlled By:  Deployment/nginx-deployment-demo
Events:
  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 pod
NAME                                      READY   STATUS    RESTARTS   AGE
nginx-deployment-demo-675745c9cd-5q65l    1/1     Running   0          16m
nginx-deployment-demo-675745c9cd-kktkv    1/1     Running   0          16m
nginx-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-xbtgs
Controlled By:  ReplicaSet/nginx-deployment-demo-675745c9cd

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

总结一下这个过程:

  1. 用户通过 kubectl 创建 Deployment。

  2. Deployment 创建 ReplicaSet。

  3. ReplicaSet 创建 Pod。

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

 

更新镜像


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

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

$ kubectl edit deploy nginx-deployment-demo

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

[root@k8s-master ~]# cat deployment.yml 
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment-demo
  namespace: default
spec:
  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 -w
NAME                                DESIRED   CURRENT   READY   AGE
nginx-deployment-demo-675745c9cd    3         3         3       28m

#下面为执行apply之后的状态
nginx-deployment-demo-f6bf65976     1         0         0       0s
nginx-deployment-demo-f6bf65976     1         0         0       0s
nginx-deployment-demo-f6bf65976     1         1         0       0s
nginx-deployment-demo-f6bf65976     1         1         1       41s
nginx-deployment-demo-675745c9cd    2         3         3       30m
nginx-deployment-demo-f6bf65976     2         1         1       41s
nginx-deployment-demo-675745c9cd    2         3         3       30m
nginx-deployment-demo-675745c9cd    2         2         2       30m
nginx-deployment-demo-f6bf65976     2         1         1       41s
nginx-deployment-demo-f6bf65976     2         2         1       41s
nginx-deployment-demo-f6bf65976     2         2         2       110s
nginx-deployment-demo-675745c9cd    1         2         2       31m
nginx-deployment-demo-f6bf65976     3         2         2       110s
nginx-deployment-demo-675745c9cd    1         2         2       31m
nginx-deployment-demo-675745c9cd    1         1         1       31m
nginx-deployment-demo-f6bf65976     3         2         2       111s
nginx-deployment-demo-f6bf65976     3         3         2       111s
nginx-deployment-demo-f6bf65976     3         3         3       113s
nginx-deployment-demo-675745c9cd    0         1         1       31m
nginx-deployment-demo-675745c9cd    0         1         1       31m
nginx-deployment-demo-675745c9cd    0         0         0       31m



[root@k8s-master ~]# kubectl get rs
NAME                                DESIRED   CURRENT   READY   AGE
nginx-deployment-demo-675745c9cd    1         1         1       31m
nginx-deployment-demo-f6bf65976     3         3         2       113s
[root@k8s-master ~]# kubectl get rs
NAME                                DESIRED   CURRENT   READY   AGE
nginx-deployment-demo-675745c9cd    0         0         0       33m
nginx-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 -w
NAME                                      READY   STATUS    RESTARTS   AGE
nginx-deployment-demo-675745c9cd-5q65l    1/1     Running   0          28m
nginx-deployment-demo-675745c9cd-kktkv    1/1     Running   0          28m
nginx-deployment-demo-675745c9cd-xbtgs    1/1     Running   0          28m

#apply之后产生的
nginx-deployment-demo-f6bf65976-bwjlk     0/1     Pending   0          0s
nginx-deployment-demo-f6bf65976-bwjlk     0/1     Pending   0          0s
nginx-deployment-demo-f6bf65976-bwjlk     0/1     ContainerCreating   0          0s
nginx-deployment-demo-f6bf65976-bwjlk     0/1     ContainerCreating   0          2s
nginx-deployment-demo-f6bf65976-bwjlk     1/1     Running             0          41s
nginx-deployment-demo-675745c9cd-xbtgs    1/1     Terminating         0          30m
nginx-deployment-demo-f6bf65976-s5j6v     0/1     Pending             0          0s
nginx-deployment-demo-f6bf65976-s5j6v     0/1     Pending             0          0s
nginx-deployment-demo-f6bf65976-s5j6v     0/1     ContainerCreating   0          0s
nginx-deployment-demo-675745c9cd-xbtgs    1/1     Terminating         0          30m
nginx-deployment-demo-675745c9cd-xbtgs    0/1     Terminating         0          30m
nginx-deployment-demo-f6bf65976-s5j6v     0/1     ContainerCreating   0          2s
nginx-deployment-demo-675745c9cd-xbtgs    0/1     Terminating         0          30m
nginx-deployment-demo-675745c9cd-xbtgs    0/1     Terminating         0          30m
nginx-deployment-demo-f6bf65976-s5j6v     1/1     Running             0          69s
nginx-deployment-demo-675745c9cd-kktkv    1/1     Terminating         0          31m
nginx-deployment-demo-f6bf65976-t5hc4     0/1     Pending             0          1s
nginx-deployment-demo-f6bf65976-t5hc4     0/1     Pending             0          1s
nginx-deployment-demo-f6bf65976-t5hc4     0/1     ContainerCreating   0          1s
nginx-deployment-demo-675745c9cd-kktkv    1/1     Terminating         0          31m
nginx-deployment-demo-f6bf65976-t5hc4     0/1     ContainerCreating   0          2s
nginx-deployment-demo-675745c9cd-kktkv    0/1     Terminating         0          31m
nginx-deployment-demo-f6bf65976-t5hc4     1/1     Running             0          3s
nginx-deployment-demo-675745c9cd-5q65l    1/1     Terminating         0          31m
nginx-deployment-demo-675745c9cd-kktkv    0/1     Terminating         0          31m
nginx-deployment-demo-675745c9cd-kktkv    0/1     Terminating         0          31m
nginx-deployment-demo-675745c9cd-5q65l    1/1     Terminating         0          31m
nginx-deployment-demo-675745c9cd-5q65l    0/1     Terminating         0          31m
nginx-deployment-demo-675745c9cd-5q65l    0/1     Terminating         0          31m
nginx-deployment-demo-675745c9cd-5q65l    0/1     Terminating         0          31m


[root@k8s-master ~]# kubectl get pod -l version=v2,app=nginx
NAME                                    READY   STATUS    RESTARTS   AGE
nginx-deployment-demo-f6bf65976-bwjlk   1/1     Running   0          11m
nginx-deployment-demo-f6bf65976-s5j6v   1/1     Running   0          10m
nginx-deployment-demo-f6bf65976-t5hc4   1/1     Running   0          9m19s

 

写在最后


Kubernetes 中这些高阶的抽象对象,都是通过标签选择器来控制 Pod 的。有了 Deployment 这个高级对象,我们可以很方便地完成无状态服务的发布、更新升级,无须多余的人工参与,就能保证业务的高可用性。这也是 Kubernetes 迷人之处——声明式 API。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值