深入剖析Kubernetes学习笔记:水平扩展(17)

一、Deployment,与 ReplicaSet,以及 Pod 的关系是怎样的呢?

1、Deployment,与 ReplicaSet,以及 Pod 的关系是怎样的呢?

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

1、一个 ReplicaSet 对象,其实就是由副本数目的定义和一个 Pod 模板组成的。不难发现,它的定义其实是 Deployment 的一个子集。

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

3、对于一个 Deployment 所管理的 Pod,它的 ownerReference 是谁?

所以,这个问题的答案就是:ReplicaSet。

2、 Deployment 只允许容器的 restartPolicy=Always 的主要原因

明白了这个原理,我再来和你一起分析一个如下所示的 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

可以看到,这就是一个我们常用的 nginx-deployment,它定义的 Pod 副本个数是 3(spec.replicas=3)。

那么,在具体的实现上,这个 Deployment,与 ReplicaSet,以及 Pod 的关系是怎样的呢?

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

2、其中,ReplicaSet 负责通过“控制器模式”,保证系统中 Pod 的个数永远等于指定的个数(比如,3 个)。这也正是 Deployment 只允许容器的 restartPolicy=Always 的主要原因:

  只有在容器能保证自己始终是 Running 状态的前提下,ReplicaSet 调整 Pod 的个数才有意义。

3、水平扩展 / 收缩

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

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

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

二、滚动更新”的过程

1、deployment状态字段含义

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

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

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

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

DESIRED

CURRENT

UP-TO-DATE

AVAILABLE

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

2、实时查看 Deployment 对象的状态变化

而 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:

3、Deployment 的名字组成

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

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

1、组成

 2、pod-template-hash

4、自动触发自动更新

这个时候,如果我们修改了 Deployment 的 Pod 模板,“滚动更新”就会被自动触发。

1、kubectl edit 指令

修改 Deployment 有很多方法。比如,我可以直接使用 kubectl edit 指令编辑 Etcd 里的 API 对象。

$ kubectl edit deployment/nginx-deployment
... 
spec:
containers:
- name: nginx
image: nginx:1.9.1 # 1.7.9 -> 1.9.1
ports:
- containerPort: 80
...
deployment.extensions/nginx-deployment edited

备注:kubectl edit 并不神秘,它不过是把 API 对象的内容下载到了本地文件,让你修改完成后再提交上去。

2、kubectl rollout status 指令查看 nginx-deployment 的状态变化

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

3、这时,你可以通过查看 Deployment 的 Events,看到这个“滚动更新”的流程:

$ kubectl describe deployment nginx-deployment
...
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
...
Normal ScalingReplicaSet 24s deployment-controller Scaled up replica set nginx-deployment-1764197365 to 1
Normal ScalingReplicaSet 22s deployment-controller Scaled down replica set nginx-deployment-3167673210 to 2
Normal ScalingReplicaSet 22s deployment-controller Scaled up replica set nginx-deployment-1764197365 to 2
Normal ScalingReplicaSet 19s deployment-controller Scaled down replica set nginx-deployment-3167673210 to 1
Normal ScalingReplicaSet 19s deployment-controller Scaled up replica set nginx-deployment-1764197365 to 3
Normal ScalingReplicaSet 14s deployment-controller Scaled down replica set nginx-deployment-3167673210 to 0

首先

然后

紧接着

 如此交替

像这样,将一个集群中正在运行的多个 Pod 版本,交替地逐一升级的过程,就是“滚动更新”。

在这个“滚动更新”过程完成之后,你可以查看一下新、旧两个 ReplicaSet 的最终状态:

$ kubectl get rs
NAME DESIRED CURRENT READY AGE
nginx-deployment-1764197365 3 3 3 6s
nginx-deployment-3167673210 0 0 0 30s

其中,旧 ReplicaSet(hash=3167673210)已经被“水平收缩”成了 0 个副本。

三、滚动更新的好处

1、服务并不会受到太大的影响

比如,在升级刚开始的时候,集群里只有 1 个新版本的 Pod。如果这时,新版本 Pod 有问题启动不起来,那么“滚动更新”就会停止,从而允许开发和运维人员介入。而在这个过程中,由于应用本身还有两个旧版本的 Pod 在线,所以服务并不会受到太大的影响。

2、在任何时间窗口内,只有指定比例的 Pod 处于离线状态

1、Deployment Controller 还会确保在任何时间窗口内,只有指定比例的 Pod 处于离线状态。
2、同时,它也会确保,在任何时间窗口内,只有指定比例的新 Pod 被创建出来。这两个比例的值都是可以配置的,
3、默认都是 DESIRED 值的 25%。

3、RollingUpdateStrategy

apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
labels:
app: nginx
spec:
...
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 1

注意事项:

4、Deployment、ReplicaSet 和 Pod 的关系图了

Deployment 控制的是 ReplicaSet 的数目

Pod 的控制

对应版本的描述

四、Deployment 对应用进行版本控制的具体原理

1、kubectl set image的指令

直接修改 nginx-deployment 所使用的镜像。这个命令的好处就是,你可以不用像 kubectl edit 那样需要打开编辑器。

我把这个镜像名字修改成为了一个错误的名字,比如:nginx:1.91。这样,这个 Deployment 就会出现一个升级失败的版本。

$ kubectl set image deployment/nginx-deployment nginx=nginx:1.91
deployment.extensions/nginx-deployment image updated

由于这个 nginx:1.91 镜像在 Docker Hub 中并不存在,所以这个 Deployment 的“滚动更新”被触发后,会立刻报错并停止。

这时,我们来检查一下 ReplicaSet 的状态,如下所示:

$ kubectl get rs
NAME DESIRED CURRENT READY AGE
nginx-deployment-1764197365 2 2 2 24s
nginx-deployment-3167673210 0 0 0 35s
nginx-deployment-2156724341 2 2 0 7s

2、我们如何让这个 Deployment 的 3 个 Pod,都回滚到以前的旧版本呢?

我们只需要执行一条 kubectl rollout undo 命令,就能把整个 Deployment 回滚到上一个版本:

$ kubectl rollout undo deployment/nginx-deployment
deployment.extensions/nginx-deployment

很容易想到,在具体操作上,Deployment 的控制器,

3、如果我想回滚到更早之前的版本,要怎么办呢?

1、首先,我需要使用 kubectl rollout history 命令,查看每次 Deployment 变更对应的版本。

而由于我们在创建这个 Deployment 的时候,指定了–record 参数,所以我们创建这些版本时执行的 kubectl 命令,都会被记录下来。这个操作的输出如下所示:

$ kubectl rollout history deployment/nginx-deployment
deployments "nginx-deployment"
REVISION CHANGE-CAUSE
1 kubectl create -f nginx-deployment.yaml --record
2 kubectl edit deployment/nginx-deployment
3 kubectl set image deployment/nginx-deployment nginx=nginx:1.91

可以看到,我们前面执行的创建和更新操作,分别对应了版本 1 和版本 2,而那次失败的更新操作,则对应的是版本 3。

当然,你还可以通过这个 kubectl rollout history 指令,看到每个版本对应的 Deployment 的 API 对象的细节,具体命令如下所示:

$ kubectl rollout history deployment/nginx-deployment --revision=2

2、然后,我们就可以在 kubectl rollout undo 命令行最后,加上要回滚到的指定版本的版本号,就可以回滚到指定版本了

$ kubectl rollout undo deployment/nginx-deployment --to-revision=2
deployment.extensions/nginx-deployment

这样,Deployment Controller 还会按照“滚动更新”的方式,完成对 Deployment 的降级操作。

4、Deployment 的多次更新操作,最后 只生成一个 ReplicaSet。

具体的做法是,在更新 Deployment 前,你要先执行一条 kubectl rollout pause 指令。它的用法如下所示:

$ kubectl rollout pause deployment/nginx-deployment
deployment.extensions/nginx-deployment paused

 

而等到我们对 Deployment 修改操作都完成之后,只需要再执行一条 kubectl rollout resume 指令,就可以把这个 Deployment“恢复”回来,如下所示:

$ kubectl rollout resume deploy/nginx-deployment
deployment.extensions/nginx-deployment resumed

而在这个 kubectl rollout resume 指令执行之前,在 kubectl rollout pause 指令之后的这段时间里,我们对 Deployment 进行的所有修改,最后只会触发一次“滚动更新”。

当然,我们可以通过检查 ReplicaSet 状态的变化,来验证一下 kubectl rollout pause 和 kubectl rollout resume 指令的执行效果,如下所示:

$ kubectl get rs
NAME DESIRED CURRENT READY AGE
nginx-1764197365 0 0 0 2m
nginx-3196763511 3 3 3 28s

通过返回结果,我们可以看到,只有一个 hash=3196763511 的 ReplicaSet 被创建了出来。

5、我们又该如何控制这些“历史”ReplicaSet 的数量呢?

不过,即使你像上面这样小心翼翼地控制了 ReplicaSet 的生成数量,随着应用版本的不断增加,Kubernetes 中还是会为同一个 Deployment 保存很多很多不同的 ReplicaSet。

那么,我们又该如何控制这些“历史”ReplicaSet 的数量呢?

很简单,Deployment 对象有一个字段,叫作 spec.revisionHistoryLimit,就是 Kubernetes 为 Deployment 保留的“历史版本”个数。所以,如果把它设置为 0,你就再也不能做回滚操作了。

posted @ 2020-03-05 22:27  活的潇洒80  阅读(637)  评论(0编辑  收藏  举报