Kubernetes 经典PaaS的记忆:作业副本与水平扩展

而在今天这篇文章中,我就来为你详细讲解一下,Kubernetes 里第一个控制器模式的完整实现:Deployment。

Deployment 看似简单,但实际上,它实现了 Kubernetes 项目中一个非常重要的功能:Pod 的“水平扩展 / 收缩”(horizontal scaling out/in)。这个功能,是从 PaaS 时代开始,一个平台级项目就必须具备的编排能力。

举个例子,如果你更新了 Deployment 的 Pod 模板(比如,修改了容器的镜像),那么Deployment 就需要遵循一种叫作“滚动更新”(rolling update)的方式,来升级现有的容器。

而这个能力的实现,依赖的是 Kubernetes 项目中的一个非常重要的概念(API 对象):ReplicaSet。

ReplicaSet 的结构非常简单,我们可以通过这个 YAML 文件查看一下:

apiVersion: apps/v1
kind: ReplicaSet
metadata:
  name: nginx-set
  labels:
    app: nginx
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.7.9

从这个 YAML 文件中,我们可以看到,一个 ReplicaSet 对象,其实就是由副本数目的定义和一个 Pod 模板组成的。不难发现,它的定义其实是 Deployment 的一个子集。

更重要的是,Deployment 控制器实际操纵的,正是这样的 ReplicaSet 对象,而不是 Pod 对象。

对于一个 Deployment 所管理的 Pod,它的 ownerReference 是谁?这个问题的答案就是:ReplicaSet。 

明白了这个原理,我再来和你一起分析一个如下所示的 Deployment:


apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
  labels:
    app: nginx
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.7.9
        ports:
        - containerPort: 80

那么,在具体的实现上,这个 Deployment,与 ReplicaSet,以及 Pod 的关系是怎样的呢?我们可以用一张图把它描述出来:

通过这张图,我们就很清楚地看到,一个定义了 replicas=3 的 Deployment,与它的 ReplicaSet,以及 Pod 的关系,实际上是一种“层层控制”的关系。

其中,ReplicaSet 负责通过“控制器模式”,保证系统中 Pod 的个数永远等于指定的个数(比如,3 个)。这也正是 Deployment 只允许容器的 restartPolicy=Always 的主要原因:只有在容器能保证自己始终是 Running 状态的前提下,ReplicaSet 调整 Pod 的个数才有意义。 

而在此基础上,Deployment 同样通过“控制器模式”,来操作 ReplicaSet 的个数和属性,进而实现“水平扩展 / 收缩”和“滚动更新”这两个编排动作。

其中,“水平扩展 / 收缩”非常容易实现,Deployment Controller 只需要修改它所控制的 ReplicaSet 的 Pod 副本个数就可以了。

比如,把这个值从 3 改成 4,那么 Deployment 所对应的 ReplicaSet,就会根据修改后的值自动创建一个新的 Pod。这就是“水平扩展”了;“水平收缩”则反之。

而用户想要执行这个操作的指令也非常简单,就是 kubectl scale,比如:

$ kubectl scale deployment nginx-deployment --replicas=4
deployment.apps/nginx-deployment scaled

那么,“滚动更新”又是什么意思,是如何实现的呢?

接下来,我还以这个 Deployment 为例,来为你讲解“滚动更新”的过程。

首先,我们来创建这个 nginx-deployment:

$ kubectl create -f nginx-deployment.yaml --record

注意,在这里,我额外加了一个–record 参数。它的作用,是记录下你每次操作所执行的命令,以方便后面查看。

然后,我们来检查一下 nginx-deployment 创建后的状态信息:

$ kubectl get deployments
NAME               DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
nginx-deployment   3         0         0            0           1s

在返回结果中,我们可以看到四个状态字段,它们的含义如下所示。

  • DESIRED:用户期望的 Pod 副本个数(spec.replicas 的值)
  • CURRENT:当前处于 Running 状态的 Pod 的个数
  • UP-TO-DATE:当前处于最新版本的 Pod 的个数,所谓最新版本指的是 Pod 的 Spec 部分与 Deployment 里 Pod 模板里定义的完全一致
  • AVAILABLE:当前已经可用的 Pod 的个数,即:既是 Running 状态,又是最新版本,并且已经处于 Ready(健康检查正确)状态的 Pod 的个数。

可以看到,只有这个 AVAILABLE 字段,描述的才是用户所期望的最终状态。

而 Kubernetes 项目还为我们提供了一条指令,让我们可以实时查看 Deployment 对象的状态变化。这个指令就是 kubectl rollout status:

$ kubectl rollout status deployment/nginx-deployment
Waiting for rollout to finish: 2 out of 3 new replicas have been updated...
deployment.apps/nginx-deployment successfully rolled out

在这个返回结果中,“2 out of 3 new replicas have been updated”意味着已经有 2 个 Pod 进入了 UP-TO-DATE 状态。

继续等待一会儿,我们就能看到这个 Deployment 的 3 个 Pod,就进入到了 AVAILABLE 状态:

NAME               DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
nginx-deployment   3         3         3            3           20s

此时,你可以尝试查看一下这个 Deployment 所控制的 ReplicaSet:

$ kubectl get rs
NAME                          DESIRED   CURRENT   READY   AGE
nginx-deployment-3167673210   3         3         3       20s

如上所示,在用户提交了一个 Deployment 对象后,Deployment Controller 就会立即创建一个 Pod 副本个数为 3 的 ReplicaSet。这个 ReplicaSet 的名字,则是由 Deployment 的名字和一个随机字符串共同组成。 

这个随机字符串叫作 pod-template-hash,在我们这个例子里就是:3167673210。ReplicaSet 会把这个随机字符串加在它所控制的所有 Pod 的标签里,从而保证这些 Pod 不会与集群里的其他 Pod 混淆。 

而 ReplicaSet 的 DESIRED、CURRENT 和 READY 字段的含义,和 Deployment 中是一致的。所以,相比之下,Deployment 只是在 ReplicaSet 的基础上,添加了 UP-TO-DATE 这个跟版本有关的状态字段。 

猜你喜欢

转载自blog.csdn.net/qq_34556414/article/details/124510614