控制器-Deployment
一、Deployment 概述
| 英文 | 英文简称 | 中文 |
|---|---|---|
| Pod | Pod | 容器组 |
| Controller | Controller | 控制器 |
| ReplicaSet | ReplicaSet | 副本集 |
| Deployment | Deployment | 部署 |
Deployment 是最常用的用于部署无状态服务的方式。Deployment 控制器使得您能够以声明的方式更新 Pod(容器组)和 ReplicaSet(副本集)。
声明式配置
声明的方式是相对于非声明方式而言的。例如,以滚动更新为例,假设有 3 个容器组,现需要将他们的容器镜像更新为新的版本。
- 非声明的方式,需要手动逐步执行以下过程:
- 使用 kubectl 创建一个新版本镜像的容器组
- 使用 kubectl 观察新建容器组的状态
- 新建容器组的状态就绪以后,使用 kubectl 删除一个旧的容器组
- 重复执行上述过程,直到所有容器组都已经替换为新版本镜像的容器组
- 声明的方式,只需要执行:
- 使用 kubectl 更新 Deployment 定义中 spec.template.spec.containers[].image 字段
二、创建 Deployment
下面的 yaml 文件定义了一个 Deployment,该 Deployment 将创建一个有 3 个 nginx Pod 副本的 ReplicaSet(副本集):
1 | apiVersion: apps/v1 |
在这个例子中:
- 将创建一个名为 nginx-deployment 的 Deployment(部署),名称由
.metadata.name字段指定 - 该 Deployment 将创建 3 个 Pod 副本,副本数量由
.spec.replicas字段指定 .spec.selector字段指定了 Deployment 如何找到由它管理的 Pod。此案例中,我们使用了 Pod template 中定义的一个标签(app: nginx)。对于极少数的情况,这个字段也可以定义更加复杂的规则- .template 字段包含了如下字段:
.template.metadata.labels字段,指定了 Pod 的标签(app: nginx).template.spec.containers[].image字段,表明该 Pod 运行一个容器nginx:1.7.9.template.spec.containers[].name字段,表明该容器的名字是nginx
注意:
必须为 Deployment 中的 .spec.selector 和 .template.metadata.labels 定义一个合适的标签(这个例子中的标签是 app: nginx)。请不要使用与任何其他控制器(其他 Deployment / StatefulSet 等)相同的 .spec.selector 和 .template.metadata.labels。否则可能发生冲突。
1.执行命令以创建 Deployment, 可以为该命令增加 –record 选项, 这样可以回顾某一个 Deployment 版本变化的原因
1 | [root@k8s-master 0402]# kubectl apply -f deploy.yaml --record |
2.查看 Deployment 的创建情况
1 | [root@k8s-master 0402]# kubectl get deployments |
字段含义
| 字段名称 | 说明 |
|---|---|
| NAME | Deployment name |
| DESIRED | Deployment 期望的 Pod 副本数,即 Deployment 中 .spec.replicas 字段指定的数值。该数值是“期望”值 |
| CURRENT | 当前有多少个 Pod 副本数在运行 |
| UP-TO-DATE | Deployment 中,符合当前 Pod Template 定义的 Pod 数量 |
| AVAILABLE | 当前对用户可用的 Pod 副本数 |
| AGE | Deployment 部署以来到现在的时长 |
3.查看该 Deployment 创建的 ReplicaSet(rs),执行命令 kubectl get rs,输出结果如下所示:
1 | [root@k8s-master 0402]# kubectl get rs |
4.查看 Pod 的标签,执行命令 kubectl get pods --show-labels,输出结果如下所示:
1 | [root@k8s-master 0402]# kubectl get pods --show-labels |
三、更新 Deployment
使用下述步骤更新您的 Deployment
方式一:
- 执行以下命令,将容器镜像从 nginx:1.7.9 更新到 nginx:1.9.1
1 | [root@k8s-master 0402]# kubectl --record deployment.apps/nginx-deployment set image deployment.v1.apps/nginx-deployment nginx=nginx:1.9.1 |
方式二:
使用 edit 该 Deployment,并将 .spec.template.spec.containers[0].image 从 nginx:1.7.9 修改为 nginx:1.9.1
1 | [root@k8s-master 0402]# kubectl edit deployment nginx-deployment |
- 如果想要修改这些新的 Pod,只需要再次修改 Deployment 的 Pod template。
- Deployment 将确保更新过程中,任意时刻只有一定数量的 Pod 被关闭。默认情况下,Deployment 确保至少
.spec.replicas的 75% 的 Pod 保持可用(25% max unavailable) - Deployment 将确保更新过程中,任意时刻只有一定数量的 Pod 被创建。默认情况下,Deployment 确保最多
.spec.replicas的 25% 的 Pod 被创建(25% max surge)
Deployment Controller 先创建一个新 Pod,然后删除一个旧 Pod,然后再创建新的,如此循环,直到全部更新。Deployment Controller 不会 kill 旧的 Pod,除非足够数量的新 Pod 已经就绪,Deployment Controller 也不会创新新 Pod 直到足够数量的旧 Pod 已经被 kill。这个过程将确保更新过程中,任意时刻,最少 2 个 / 最多 4 个 Pod 可用。
覆盖更新 Rollover (更新过程中再更新)
每创建一个 Deployment,Deployment Controller 都为其创建一个 ReplicaSet,并设定其副本数为期望的 Pod 数( .spec.replicas 字段)。如果 Deployment 被更新,旧的 ReplicaSet 将被 Scale down,新建的 ReplicaSet 将被 Scale up;直到最后新旧两个 ReplicaSet,一个副本数为 .spec.replias,另一个副本数为 0。这个过程称为 rollout。
当 Deployment 的 rollout 正在进行中的时候,如果再次更新 Deployment 的信息,此时 Deployment 将再创建一个新的 ReplicaSet 并开始将其 scale up,将先前正在 scale up 的 ReplicaSet 也作为一个旧的 ReplicaSet,并开始将其 scale down。
例如:
- 假设创建了一个 Deployment 有 5 个 nginx:1.7.9 的副本;
- 立刻更新该 Deployment 使得其
.spec.replicas为 5,容器镜像为nginx:1.9.1,而此时只有 3 个 nginx:1.7.9 的副本已创建; - 此时,Deployment Controller 将立刻开始 kill 已经创建的 3 个 nginx:1.7.9 的 Pod,并开始创建 nginx:1.9.1 的 Pod。Deployment Controller 不会等到 5 个 nginx:1.7.9 的 Pod 都创建完之后在开始新的更新
四、回滚 Deployment
模拟更新错误
假设您在更新 Deployment 的时候,犯了一个拼写错误,将
nginx:1.9.1写成了nginx:1.911
2kubectl set image deployment.v1.apps/nginx-deployment nginx=nginx:1.91 --record=true
deployment.apps/nginx-deployment image updated该更新将卡住,执行命令
kubectl rollout status deployment.v1.apps/nginx-deployment检查其状态,输出结果如下所示:1
Waiting for rollout to finish: 1 out of 3 new replicas have been updated...
检查Deployment的更新历史
1 | [root@k8s-master 0402]# kubectl rollout history deployment nginx-deployment |
执行命令 kubectl rollout history deployment.v1.apps/nginx-deployment --revision=2,查看 revision(版本)的详细信息,输出结果如下所示:
1 | [root@k8s-master 0402]# kubectl rollout history deployment.v1.apps/nginx-deployment --revision=2 |
回滚到前一个 revision(版本)
下面的步骤可将 Deployment 从当前版本回滚到前一个版本(version 2)
执行命令 kubectl rollout undo deployment.v1.apps/nginx-deployment 将当前版本回滚到前一个版本,输出结果如下所示:
1 | deployment.apps/nginx-deployment |
或者,也可以使用 --to-revision 选项回滚到前面的某一个指定版本,执行如下命令:
1 | kubectl rollout undo deployment.v1.apps/nginx-deployment --to-revision=2 |
- 此时,Deployment 已经被回滚到前一个稳定版本。可以看到 Deployment Controller 为该 Deployment 产生了 DeploymentRollback event。
- 执行命令
kubectl get deployment nginx-deployment,检查该回滚是否成功,Deployment 是否按预期的运行。
五、伸缩 Deployment
执行伸缩
- 执行命令
kubectl scale deployment nginx-deployment --replicas=5,可以伸缩 Deployment,输出结果如下所示:
1 | [root@k8s-master 0402]# kubectl scale deployment nginx-deployment --replicas=5 |
如果集群启用了自动伸缩,执行以下命令,就可以基于 CPU 的利用率在一个最大和最小的区间自动伸缩您的 Deployment:
1 | kubectl autoscale deployment.v1.apps/nginx-deployment --min=10 --max=15 --cpu-percent=80 |
按比例伸缩
例如,假设已经运行了一个 10 副本数的 Deployment,其 maxSurge=3, maxUnavailable=2。
- 执行命令
kubectl scale deployment.v1.apps/nginx-deployment --replicas=15,将 Deployment 的 replicas 调整到 15。此时,Deployment Controller 需要决定如何分配新增的 5 个 Pod 副本。根据“按比例伸缩”的原则:- 更大比例的新 Pod 数被分配到副本数最多的 ReplicaSet
- 更小比例的新 Pod 数被分配到副本数最少的 ReplicaSet
- 如果还有剩余的新 Pod 数未分配,则将被增加到副本数最多的 ReplicaSet
- 副本数为 0 的 ReplicaSet,scale up 之后,副本数仍然为 0
- 在本例中,3 个新副本被添加到旧的 ReplicaSet,2个新副本被添加到新的 ReplicaSet。如果新的副本都达到就绪状态,滚动更新过程最终会将所有的副本数添加放到新 ReplicaSet。
六、暂停和继续 Deployment
可以先暂停 Deployment,然后再触发一个或多个更新,最后再继续(resume)该 Deployment。这种做法使得在暂停和继续中间对 Deployment 做多次更新,而无需触发不必要的滚动更新。
执行命令 kubectl rollout pause deployment.v1.apps/nginx-deployment 暂停 Deployment,输出结果如下所示:
1 | [root@k8s-master 0402]# kubectl rollout pause deployment nginx-deployment |
执行命令 kubectl set image deployment.v1.apps/nginx-deployment nginx=nginx:1.17.9,更新 Deployment 的容器镜像,输出结果如下所示:
1 | [root@k8s-master 0402]# kubectl set image deployment/nginx-deployment nginx=nginx:1.17.9 |
执行命令 kubectl rollout resume deployment/nginx-deployment,继续(resume)该 Deployment,可使前面所有的变更一次性生效,输出结果如下所示:
1 | [root@k8s-master 0402]# kubectl rollout resume deployment nginx-deployment |
执行命令 kubectl get rs -o wide 查看 ResultSet 的最终状态,输出结果如下所示:
1 | [root@k8s-master 0402]# kubectl get rs -o wide |
不能回滚(rollback)一个已暂停的 Deployment,除非继续(resume)该 Deployment。
七、查看Deployment的状态
Progressing 状态
当如下任何一个任务正在执行时,Kubernete 将 Deployment 的状态标记为 progressing:
- Deployment 创建了一个新的 ReplicaSet
- Deployment 正在 scale up 其最新的 ReplicaSet
- Deployment 正在 scale down 其旧的 ReplicaSet
- 新的 Pod 变为 就绪(ready) 或 可用(available)
可以使用命令 kubectl rollout status 监控 Deployment 滚动更新的过程
Complete 状态
如果 Deployment 符合以下条件,Kubernetes 将其状态标记为 complete:
- 该 Deployment 中的所有 Pod 副本都已经被更新到指定的最新版本
- 该 Deployment 中的所有 Pod 副本都处于 可用(available) 状态
- 该 Deployment 中没有旧的 ReplicaSet 正在运行
可以执行命令 kubectl rollout status 检查 Deployment 是否已经处于 complete 状态。如果是,则该命令的退出码为 0。
例如,执行命令 kubectl rollout status deployment.v1.apps/nginx-deployment,输出结果如下所示:
Failed 状态
Deployment 在更新其最新的 ReplicaSet 时,可能卡住而不能达到 complete 状态。如下原因都可能导致此现象发生:
- 集群资源不够
- 就绪检查(readiness probe)失败
- 镜像抓取失败
- 权限不够
- 资源限制
- 应用程序的配置错误导致启动失败
指定 Deployment 定义中的 .spec.progressDeadlineSeconds 字段,Deployment Controller 在等待指定的时长后,将 Deployment 的标记为处理失败。
操作处于 Failed 状态的 Deployment
可以针对 Failed 状态下的 Deployment 执行任何适用于 Deployment 的指令,例如:
- scale up / scale down
- 回滚到前一个版本
- 暂停(pause)Deployment,以对 Deployment 的 Pod template 执行多处更新
八、清除策略
通过 Deployment 中 .spec.revisionHistoryLimit 字段,可指定为该 Deployment 保留多少个旧的 ReplicaSet。超出该数字的将被在后台进行垃圾回收。该字段的默认值是 10。
如果该字段被设为 0,Kubernetes 将清理掉该 Deployment 的所有历史版本(revision),将无法对该 Deployment 执行回滚操作 kubectl rollout undo。
九、部署策略
通过 Deployment 中 .spec.strategy 字段,可以指定使用 滚动更新 RollingUpdate 的部署策略还是使用 重新创建 Recreate 的部署策略
其中字段的含义如下:
| 字段名称 | 可选值 | 字段描述 |
|---|---|---|
| 类型 | 滚动更新 重新创建 | 如果选择重新创建,Deployment将先删除原有副本集中的所有 Pod,然后再创建新的副本集和新的 Pod。如此,更新过程中将出现一段应用程序不可用的情况; |
| 最大超出副本数 | 数字或百分比 | 滚动更新过程中,可以超出期望副本数的最大值。 该取值可以是一个绝对值(例如:5),也可以是一个相对于期望副本数的百分比(例如:10%); 如果填写百分比,则以期望副本数乘以该百分比后向上取整的方式计算对应的绝对值; 当最大超出副本数 maxUnavailable 为 0 时,此数值不能为 0;默认值为 25%。 例如:假设此值被设定为 30%,当滚动更新开始时,新的副本集(ReplicaSet)可以立刻扩容, 但是旧 Pod 和新 Pod 的总数不超过 Deployment 期待副本数(spec.repilcas)的 130%。 一旦旧 Pod 被终止后,新的副本集可以进一步扩容,但是整个滚动更新过程中,新旧 Pod 的总 数不超过 Deployment 期待副本数(spec.repilcas)的 130%。 |
| 最大不可用副本数 | 数字或百分比 | 滚动更新过程中,不可用副本数的最大值。 该取值可以是一个绝对值(例如:5),也可以是一个相对于期望副本数的百分比(例如:10%); 如果填写百分比,则以期望副本数乘以该百分比后向下取整的方式计算对应的绝对值; 当最大超出副本数 maxSurge 为 0 时,此数值不能为 0;默认值为 25%; 例如:假设此值被设定为 30%,当滚动更新开始时,旧的副本集(ReplicaSet)可以缩容到期望 副本数的 70%;在新副本集扩容的过程中,一旦新的 Pod 已就绪,旧的副本集可以进一步缩容, 整个滚动更新过程中,确保新旧就绪副本数之和不低于期望副本数的 70%。 |
十、金丝雀发布(灰度发布)
可以使用 Deployment 将最新的应用程序版本发布给一部分用户(或服务器),为每个版本创建一个 Deployment,此时,应用程序的新旧两个版本都可以同时获得生产上的流量。
部署第一个版本
第一个版本的 Deployment 包含了 3 个Pod副本,Service 通过 label selector app: nginx 选择对应的 Pod,nginx 的标签为 1.7.9
1 | apiVersion: apps/v1 |
假设此时想要发布新的版本 nginx 1.8.0,可以创建第二个 Deployment:
1 | apiVersion: apps/v1 |
- 因为 Service 的LabelSelector 是
app: nginx,由nginx-deployment和nginx-deployment-canary创建的 Pod 都带有标签app: nginx,所以,Service 的流量将会在两个 release 之间分配 - 在新旧版本之间,流量分配的比例为两个版本副本数的比例,此处为 1:3
局限性
按照 Kubernetes 默认支持的这种方式进行金丝雀发布,有一定的局限性:
- 不能根据用户注册时间、地区等请求中的内容属性进行流量分配
- 同一个用户如果多次调用该 Service,有可能第一次请求到了旧版本的 Pod,第二次请求到了新版本的 Pod
在 Kubernetes 中不能解决上述局限性的原因是:Kubernetes Service 只在 TCP 层面解决负载均衡的问题,并不对请求响应的消息内容做任何解析和识别。