前言
大家好,我是秋意零。
在上一篇中,我们介绍了控制器的基本设计思想:控制器模式。通过这个 “控制器模式” 我们来看看 Deployment 是如何依靠它来实现的。
最近搞了一个扣扣群,旨在技术交流、博客互助,希望各位大佬多多支持!
获取方式:
- 1.在我主页推广区域,如图:
- 2.文章底部推广区域,如图:
👿 简介
- 🏠 个人主页: 秋意零
- 🧑 个人介绍:在校期间参与众多云计算相关比赛,如:🌟 “省赛”、“国赛”,并斩获多项奖项荣誉证书
- 🎉 目前状况:24 届毕业生,拿到一家私有云(IAAS)公司 offer,暑假开始实习
- 🔥 账号:各个平台, 秋意零 账号创作者、 云社区 创建者
- 💕欢迎大家:欢迎大家一起学习云计算,走向年薪 30 万
系列文章目录
【云原生|探索 Kubernetes-1】容器的本质是进程
【云原生|探索 Kubernetes-2】容器 Linux Cgroups 限制
【云原生|探索 Kubernetes 系列 3】深入理解容器进程的文件系统
【云原生|探索 Kubernetes 系列 4】现代云原生时代的引擎
【云原生|探索 Kubernetes 系列 5】简化 Kubernetes 的部署,深入解析其工作流程
更多点击专栏查看:深入探索 Kubernetes
文章目录
- 前言
- 系列文章目录
- 一、介绍水平扩展、水平收缩、滚动更新
- 二、水平扩展/收缩的实现
- 三、滚动更新
- 四、回滚
- 回滚到上一个版本
- 回滚到更早之前版本
- 删除多余的 RplicaSet
- 总结
正文开始:
- 快速上船,马上开始掌舵了(Kubernetes),距离开船还有 3s,2s,1s…
一、介绍水平扩展、水平收缩、滚动更新
Deployment 实现了非常重要的功能:Pod 的水平扩展、水平收缩、滚动更新。
举个栗子:
想象一个场景,现在我们有一个 Deployment 控制器上面部署的是一个 Web 服务,期望 Pod 的个数为:3。对外发布后,起初用户量比较少,我们的 Web 服务还能扛住,但是某一天(618、双11)为了宣传搞活动,导致用户量访问量突然暴涨,这个时候我们 3 个 Pod 部署的 Web 服务就扛不住了。
-
这个时候,我们怎么办呢?当活动结束访问量不大的时候怎么办?手动一个一个添加、删除 Pod 服务吗?这种显然是不现实的。因为,手动操作即费时间,又费人力资源,最重要的是损失了大量流量导致错过商机。
-
这种情况下就需要使用水平扩展、水平收缩(弹性机制)了。而我们控制器就包含这些功能,看看是怎么实现的吧。
- 水平扩展:我们可以设置一个资源阈值,当 CPU 或者 Mem 利用率达到 80% 或 90% 时就利用 Deployment 扩展我们 Pod 服务;
- 水平收缩:当 CPU 或者 Mem 利用率降低到 20% 或 40% 时就利用 Deployment 收缩我们 Pod 服务。
-
如果 Web 程序需要更新版本这时候怎么办?
- 滚动更新:滚动更新,在 Kubernetes 中就是替换掉 YAML 文件中的镜像版本,替换之后,控制器会重新创建一组新版本的服务,一个新版 Pod 服务创建成功之后,会删除一个旧版本的 Pod 服务,这样依次循环直到替换完毕;如果新版不稳定也可以马上回到之前旧版本,这个操作称为:“回滚”。这样的好处是对于用户是透明的,无感知的;对于管理人员来说是便捷高效的。
注意:目前这篇还不会提到监控资源的利用率自动来扩展和收缩 Pod(感兴趣可以先去了解 HorizontalPodAutoscaler(HPA)),这里介绍的是手动执行命令来扩展和收缩 Pod。
二、水平扩展/收缩的实现
知道了这三个功能水平扩展、水平收缩、滚动更新,那么我们就来看看它们是由谁实现的,是 Deployment ?
其实答案在上一篇中结尾部分,我们介绍了 Deployment 管理的 Pod ,这个 Pod 的 ownerReference(所有者)是 ReplicaSet;ReplicaSet 的 ownerReference(所有者)是 Deployment。
答案:所以,水平扩展、水平收缩、滚动更新三个功能主要依靠 ReplicaSet 来实现。不过还是需要 Deployment 管理辅助。
明白了这个原理之后,我们接着来看看 Deployment,如下:
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
labels:
app: nginx
spec:
replicas: 3 # 指定副本数量
selector:
matchLabels:
app: nginx # 选择标签为`app: nginx`的Pod进行管理
template:
metadata:
labels:
app: nginx # Pod的标签,与 matchLabels 字段下的标签绑定
spec:
containers:
- name: nginx-web
image: nginx # 指定应用程序的镜像
imagePullPolicy: IfNotPresent
ports:
- containerPort: 80 # 应用程序监听的端口号
上述 Deployment 的 YAML 文件,可以用下图表示:
- 通过这张图,我们可以看到,一个定义了
replicas: 3
的 Deployment 与 ReplicaSet 和 Pod 的关系结构,是 “层层控制” 的。 - ReplicaSet 负责通过 “控制器模式”,保证 Pod 的个数永远是 YAML 文件中所期望的个数(replicas: 3),所以 Deployment 只允许容器的 restartPolicy=Always ,这样才能保证,容器始终是 Running 状态的前提下,ReplicaSet 调整 Pod 的个数才有意义。
Deployment 同样通过 “控制器模式”,来操作 ReplicaSet 的个数和属性,从而实现 “水平扩展 / 收缩” 和 “滚动更新” 这两个编排动作。
“水平扩展 / 收缩” 的实现,Deployment 只需要修改它所控制的 ReplicaSet 的 Pod 副本个数就可以了,也就是 replicas: 3
字段。我们将上面 Deployment 的 YAML 文件创建,如下:
1.创建 Deployment:
[root@master01 yaml]# kubectl apply -f deploy-web.yaml
deployment.apps/nginx-deployment created
[root@master01 yaml]#
[root@master01 yaml]# kubectl get -f deploy-web.yaml
NAME READY UP-TO-DATE AVAILABLE AGE
nginx-deployment 3/3 3 3 6s
2.扩展 Pod 的副本数为:6
[root@master01 yaml]# kubectl scale deployment nginx-deployment --replicas=6
deployment.apps/nginx-deployment scaled
[root@master01 yaml]#
[root@master01 yaml]# kubectl get -f deploy-web.yaml
NAME READY UP-TO-DATE AVAILABLE AGE
nginx-deployment 6/6 6 6 119s
[root@master01 yaml]# kubectl get pod
NAME READY STATUS RESTARTS AGE
nginx-deployment-54fdb8fb59-948nn 1/1 Running 0 2s
nginx-deployment-54fdb8fb59-bgqhd 1/1 Running 0 3m13s
nginx-deployment-54fdb8fb59-hl9pv 1/1 Running 0 2s
nginx-deployment-54fdb8fb59-ptwz2 1/1 Running 0 3m13s
nginx-deployment-54fdb8fb59-s9rkp 1/1 Running 0 2s
nginx-deployment-54fdb8fb59-wf4mh 1/1 Running 0 2s
3.收缩 Pod 的副本数为:2
[root@master01 yaml]# kubectl scale deployment nginx-deployment --replicas=2
deployment.apps/nginx-deployment scaled
[root@master01 yaml]#
[root@master01 yaml]# kubectl get -f deploy-web.yaml
NAME READY UP-TO-DATE AVAILABLE AGE
nginx-deployment 2/2 2 2 2m46s
[root@master01 yaml]# kubectl get pod
NAME READY STATUS RESTARTS AGE
nginx-deployment-54fdb8fb59-bgqhd 1/1 Running 0 2m59s
nginx-deployment-54fdb8fb59-ptwz2 1/1 Running 0 2m59s
[root@master01 yaml]#
三、滚动更新
首先查看 Deployment 所控制的 ReplicaSet
这个 ReplicaSet 的名字,是由 Deployment 的名字和一个随机字符串共同组成。这个随机字符串叫作 pod-template-hash,在我们这个例子里就是:54fdb8fb59。ReplicaSet 会把这个随机字符串加在它所控制的所有 Pod 的标签里,从而保证这些 Pod 不会与集群里的其他 Pod 混淆。
[root@master01 yaml]# kubectl get rs
NAME DESIRED CURRENT READY AGE
nginx-deployment-54fdb8fb59 2 2 2 65s
现在,修改了 Deployment 的 Pod 模板,“滚动更新” 就会被自动触发。
我们可以使用 kubectl edit 指令编辑 Etcd 里的 API 对象。 kubectl edit 指令,会帮你直接打开 nginx-deployment 的 API 对象。然后,你就可以修改这里的 Pod 模板部分了。
- kubectl edit 并不神秘,它不过是把 API 对象的内容下载到了本地文件,让你修改完成后再提交上去。
1.修改 deployment 模板。
这里将原本镜像是: nginx:latest
的版本,修改为:nginx:alpine
。
kubectl edit deploy/nginx-deployment
2.通过查看 Deployment 的 Events,看到这个“滚动更新”的流程:
- 第二条 Events:Deployment 首先创建了一个新的 ReplicaSet (nginx-deployment-86d5bf894b),初始 Pod 副本数为:0。
- 第三条 Events:缩放 ReplicaSet 控制器 nginx-deployment-54fdb8fb59 从 3 缩小到 2。
- 第四条 Events:缩放 ReplicaSet 控制器 nginx-deployment-86d5bf894b 从1 放大到 2。
- 第五条 Events:缩放 ReplicaSet 控制器 nginx-deployment-54fdb8fb59 从 2 缩小到 1。
- 第六条 Events:缩放 ReplicaSet 控制器 nginx-deployment-86d5bf894b 从 2 放大到 3。
- 第七条 Events:缩放 ReplicaSet 控制器 nginx-deployment-54fdb8fb59 从1 缩小到 0。
像这样,交替逐一升级的过程,就是 “滚动更新”。这个过程保证了用户的体验,因为在升级的过程中,只有当新版本的运行成功之后旧版本才会删除,所以这个过程始终都会有服务提供给用户。
[root@master01 ~]# kubectl describe deploy/nginx-deployment
...
...
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal ScalingReplicaSet 55m deployment-controller Scaled up replica set nginx-deployment-54fdb8fb59 to 3
Normal ScalingReplicaSet 32m deployment-controller Scaled up replica set nginx-deployment-86d5bf894b to 1
Normal ScalingReplicaSet 31m deployment-controller Scaled down replica set nginx-deployment-54fdb8fb59 to 2 from 3
Normal ScalingReplicaSet 31m deployment-controller Scaled up replica set nginx-deployment-86d5bf894b to 2 from 1
Normal ScalingReplicaSet 31m deployment-controller Scaled down replica set nginx-deployment-54fdb8fb59 to 1 from 2
Normal ScalingReplicaSet 31m deployment-controller Scaled up replica set nginx-deployment-86d5bf894b to 3 from 2
Normal ScalingReplicaSet 31m deployment-controller Scaled down replica set nginx-deployment-54fdb8fb59 to 0 from 1
3.再次查看 ReplicaSet:
- 这里,可以看到有两个 ReplicaSet,一个新创建的,一个旧的。旧的 ReplicaSet 还存在的意义就是为了保证,切换为之前的版本,“回滚” 的操作。
[root@master01 yaml]# kubectl get rs
NAME DESIRED CURRENT READY AGE
nginx-deployment-54fdb8fb59 0 0 0 25m
nginx-deployment-86d5bf894b 3 3 3 2m13s
综上所述,再来看看此时 Deployment、ReplicaSet 和 Pod 的关系图(黄色代表旧 Pod,绿色代表新 Pod,目前状态是 新 Pod 只更新了一个):
- 通过图我们了解到,Deployment 可以控制 ReplicaSet 的数量,以及每个 ReplicaSet 的属性;
- 而一个应用的版本,对应的是一个 ReplicaSet。
四、回滚
首先,我们使用 kubectl set image
的指令,直接修改 nginx-deployment 所使用的镜像。这个命令的好处就是,你可以不用像 kubectl edit 那样需要打开编辑器。
这次,我们把镜像改为一个不存在的镜像,如改为 nginx:1.111,这个 Deployment 就会出现一个升级失败的版本(nginx-deployment-746bd689d9)。
1.修改 Deployment 的 image 版本:
[root@master01 ~]# kubectl get deploy -o wide
NAME READY UP-TO-DATE AVAILABLE AGE CONTAINERS IMAGES SELECTOR
nginx-deployment 3/3 3 3 126m nginx-web nginx:alpine app=nginx
[root@master01 ~]#
[root@master01 ~]# kubectl set image deploy/nginx-deployment nginx-web=nginx:1.111
deployment.apps/nginx-deployment image updated
2.查看 RplicaSet 情况:
- nginx-deployment-746bd689d9 控制器,可以看到 READY 状态的个数为:0
[root@master01 ~]# kubectl get deploy
NAME READY UP-TO-DATE AVAILABLE AGE
nginx-deployment 3/3 1 3 127m
[root@master01 ~]#
[root@master01 ~]# kubectl get rs #746bd689d9
NAME DESIRED CURRENT READY AGE
nginx-deployment-54fdb8fb59 0 0 0 127m
nginx-deployment-746bd689d9 1 1 0 20s
nginx-deployment-86d5bf894b 3 3 3 104m
查看 Pod,可以看到不正常状态的 Pod。这是因为 “滚动更新” 被触发后,报错会立刻停止。
[root@master01 ~]# kubectl get pod
NAME READY STATUS RESTARTS AGE
nginx-deployment-746bd689d9-qcsxf 0/1 ImagePullBackOff 0 34m
nginx-deployment-86d5bf894b-fzcd7 1/1 Running 0 138m
nginx-deployment-86d5bf894b-hc28d 1/1 Running 0 138m
nginx-deployment-86d5bf894b-vfkxg 1/1 Running 0 138m
回滚到上一个版本
执行一条 kubectl rollout undo 命令,就能把整个 Deployment 回滚到上一个版本:
kubectl rollout undo deployment/nginx-deployment
查看 RplicaSet
[root@master01 ~]# kubectl get rs
NAME DESIRED CURRENT READY AGE
nginx-deployment-54fdb8fb59 0 0 0 170m
nginx-deployment-746bd689d9 0 0 0 43m
nginx-deployment-86d5bf894b 3 3 3 147m
回滚到更早之前版本
如果我想回滚到更早之前的版本,要怎么办呢?
1.首先,使用 kubectl rollout history
命令,查看每次 Deployment 变更对应的版本。
- 我们的的操作从最开始依次顺序的记录在了这里,那次失败的更新操作,则对应的是版本 3,因为在这之前执行了回滚的命令。
[root@master01 ~]# kubectl rollout history deployment/nginx-deployment
deployment.apps/nginx-deployment
REVISION CHANGE-CAUSE
1 kubectl create --filename=deploy-web.yaml --record=true
3 kubectl create --filename=deploy-web.yaml --record=true
4 kubectl create --filename=deploy-web.yaml --record=true
2.查看对应版本细节。
通过 --revision=3
参数加上对应的步骤版本,就可以查看细节,如:
[root@master01 ~]# kubectl rollout history deployment/nginx-deployment --revision=3
deployment.apps/nginx-deployment with revision #3
Pod Template:
Labels: app=nginx
pod-template-hash=746bd689d9
Annotations: kubernetes.io/change-cause: kubectl create --filename=deploy-web.yaml --record=true
Containers:
nginx-web:
Image: nginx:1.111
Port: 80/TCP
Host Port: 0/TCP
Environment: <none>
Mounts: <none>
Volumes: <none>
3.回滚到指定版本。
通过 kubectl rollout undo
命令,加上 --to-revision=2
要回滚到的指定版本的版本号,就可以回到指定版本了。
[root@master01 ~]# kubectl rollout undo deployment/nginx-deployment --to-revision=1
deployment.apps/nginx-deployment rolled back
[root@master01 ~]#
回到指定版本后,可以看到 RplicaSet 控制器也就发生了变化,一个 RplicaSet 对应一个版本。
[root@master01 ~]# kubectl get rs
NAME DESIRED CURRENT READY AGE
nginx-deployment-54fdb8fb59 3 3 3 3h5m
nginx-deployment-746bd689d9 0 0 0 57m
nginx-deployment-86d5bf894b 0 0 0 162m
删除多余的 RplicaSet
不过,Deployment 进行的每一次更新操作,都会生成一个新的 ReplicaSet 对象,是不是有些多余,甚至浪费资源呢?
- 我们在确定了,不需要某个版本之后就可以将其删除。
[root@master01 ~]# kubectl get rs
NAME DESIRED CURRENT READY AGE
nginx-deployment-54fdb8fb59 3 3 3 3h12m
nginx-deployment-746bd689d9 0 0 0 65m
nginx-deployment-86d5bf894b 0 0 0 169m
[root@master01 ~]# kubectl delete rs/nginx-deployment-746bd689d9
replicaset.apps "nginx-deployment-746bd689d9" deleted
[root@master01 ~]# kubectl get rs
NAME DESIRED CURRENT READY AGE
nginx-deployment-54fdb8fb59 3 3 3 3h12m
nginx-deployment-86d5bf894b 0 0 0 169m
除了删除之外
- Kubernetes 项目还提供了一个指令,使得我们对 Deployment 的多次更新操作,最后 只生成一个 ReplicaSet。
- 具体的做法是,在更新 Deployment 前,你要先执行一条
kubectl rollout pause
指令。
步骤如下:
- 1.让 Deployment 进入了 “暂停” 状态。
[root@master01 ~]# kubectl get rs
NAME DESIRED CURRENT READY AGE
nginx-deployment-54fdb8fb59 3 3 3 3h12m
nginx-deployment-86d5bf894b 0 0 0 169m
[root@master01 ~]# kubectl rollout pause deployment/nginx-deployment
deployment.apps/nginx-deployment paused
- 2.修改 Image 镜像版本。
[root@master01 ~]# kubectl set image deploy/nginx-deployment nginx-web=nginx:alpine
deployment.apps/nginx-deployment image updated
[root@master01 ~]#
[root@master01 ~]# kubectl get rs
NAME DESIRED CURRENT READY AGE
nginx-deployment-54fdb8fb59 3 3 3 3h46m
nginx-deployment-86d5bf894b 0 0 0 3h22m
- 3.“恢复” Deployment
[root@master01 ~]# kubectl rollout resume deployment/nginx-deployment
deployment.apps/nginx-deployment resumed
[root@master01 ~]# kubectl get rs
NAME DESIRED CURRENT READY AGE
nginx-deployment-54fdb8fb59 0 0 0 3h46m
nginx-deployment-86d5bf894b 3 3 3 3h23m
如果,你将 spec.revisionHistoryLimit
字段设置为:0;那么,你就不能做回滚操作了。
总结
今天讲解了,Deploymen 最基本的编排控制器的实现原理和使用方法。
分别说明了,水平扩展/收缩、滚动更新、回滚的机制实现。
最后:技术交流、博客互助,点击下方或主页推广加入哦!!