第9章-Deployment镜像变更和滚动更新
原创
本博客原创文章,转载请注明出处
- name: 原创
desc: 本博客原创文章,转载请注明出处
bgColor: '#F0DFB1'
textColor: '#1078E6'
2
3
4
# 第9章-Deployment镜像变更和滚动更新
本章要点 |
---|
Deployment 镜像的变更和回滚 |
Deployment 镜像的滚动更新 |
# 11. Deployment镜像变更
当一个容器镜像有了新版本,我该如何将其应用到现有的 Deployment 及其 Pod 当中呢?难道要整个删了重建?
在开始实验之前,我们先下载几个不同版本的nginx
镜像:
# 已经有的镜像 就不用重复下载了
ctr -n k8s.io i pull docker.io/library/nginx:latest
ctr -n k8s.io i pull docker.io/library/nginx:1.9
ctr -n k8s.io i pull docker.io/library/nginx:1.8
ctr -n k8s.io i pull docker.io/library/nginx:1.7.9
2
3
4
5
6
当前已经拥有了四个不同版本的nginx
镜像。
ctr -n k8s.io i ls | grep nginx
# 1.11.1 变更Deployment镜像的三种方式
和修改 Deployment 的副本数一样,修改镜像的方式也有三种:
- 通过命令行修改
- 通过
kubectl edit
编辑 Deployment 的运行信息 - 修改原始 yaml 配置文件,然后通过
kubectl apply
更新配置
但在这里,将仅介绍第一种方式——通过命令行修改 Deployment 的镜像。这是因为,通过命令行修改镜像时可以产生镜像变更历史记录,这对于回滚镜像有着至关重要的作用。
在这之前,我们先创建一个 Deployment:
# deploy-update.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: deploy-update
spec:
replicas: 2
selector:
matchLabels:
app: update
template:
metadata:
labels:
app: update
spec:
containers:
- image: nginx:latest
imagePullPolicy: IfNotPresent
name: nginx
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
创建这个控制器:
kubectl apply -f deploy-update.yaml
# 1.21.2 通过命令行变更镜像
# 1.2.11.2.1 变更镜像
基本语法:
--record
:记录镜像变更信息
kubectl set image deployment <控制器名称> <容器名称>=<新镜像> --record
如果不添加--record
选项,则当前变更会在历史记录中显示为<none>
(空),所以推荐每次都加上该选项。
1、我们可以通过-o wide
选项来列出 deployment 的详细信息:
kubectl get deployments -o wide
多出了三列信息:
- “CONTAINERS”:容器名称
- “IMAGES”:当前使用的镜像
- “SELECTOR”:通过哪些标签来追踪 Pod
2、然后通过命令kubectl set image
将这个 deployment 的镜像变更为nginx:1.8
,同时使用--record
选项来记录本次变更:
kubectl set image deployment deploy-update nginx=nginx:1.8 --record
命令执行成功,该 deployment 的镜像变更为了nginx:1.8
,随后它会删除之前的 Pod,并使用新镜像创建两个新的 Pod。
在不删除控制器的情况下,我们成功更换了 Pod 所使用的镜像版本。
同时,在执行命令之后,你可能会看到一条突兀的提示信息:
Flag --record has been deprecated, --record will be removed in the future
选项 --record 已被弃用,--record 将在未来被删除
2
3
这其实是高版本 k8s 的一个改动,选项--record
即将被移除。你不必担心,因为我将在后面介绍一个替代方案。
# 1.2.21.2.2 查看镜像变更记录
基本语法:
kubectl rollout history deployment <控制器名称>
1、例如,查看deploy-update
的镜像变更记录:
kubectl rollout history deployment deploy-update
我们可以看到两条历史记录:
- 记录 1(初始记录):这条记录是 deployment 被创建出来的那一刻自动产生的,并没有记录变更信息
- 记录 2:这是我们在 “1.2.1” 小节中所做的变更,将原本的镜像变更为了
nginx:1.8
2、我们再次将 deployment 的镜像变更为nginx:1.9
,但这次不添加--record
选项:
kubectl set image deployment deploy-update nginx=nginx:1.9
变更成功。
3、再次查看镜像变更历史记录:
kubectl rollout history deployment deploy-update
这时有人会说了:诶?这个 “记录 3” 里面不是有变更记录吗?
你再仔细看看,这条变更记录和上一次的相同,都是 nginx:1.8
。我们刚刚变更的目标镜像明明是 nginx:1.9
,但是这两条记录都是1.8
?
4、我们验证一下,将镜像变更为 nginx:1.7.9
,且不记录变更:
kubectl set image deployment deploy-update nginx=nginx:1.7.9
# 再次查看记录
kubectl rollout history deployment deploy-update
2
3
4
可以看到,虽然变更的镜像为 nginx:1.7.9
,但由于没有添加--record
选项,本次历史记录会沿用上一次的变更信息。
# 1.2.31.2.3 --record替代方案
在 “1.2.1” 小节中得知--record
选项即将被移除,那么它的替代方案是什么呢?
1、我们来查看一下 deployment 的详细信息:
kubectl describe deployment deploy-update | head -10
这个 deployment 有两条注释信息:
deployment.kubernetes.io/revision: 4
(镜像变更记录数为 4)kubernetes.io/change-cause: ...
(镜像变更信息)
没错,在高版本的 k8s 中,选项--record
的替代方案就是注释。
2、修改注释信息kubernetes.io/change-cause
,看看能否产生新的历史记录:
kubectl annotate deployment deploy-update kubernetes.io/change-cause="Update image to nginx:1.7.9"
kubectl rollout history deployment deploy-update
2
3
在改变了注释信息kubernetes.io/change-cause
的内容之后,记录 4 的历史变更信息也发生了改变。
那么,如果我想修改记录 3 的变更信息该怎么办呢?诶嘿,改不了!改不了!
在官方文档中,我暂未找到修改前置记录的方法。而在 k8s 的官方论坛中,我遇到了一个具有相同问题的帖子:
(但该帖子已发布接近两年,至今零回复。难道官方还没开发出这种功能?)
3、所以,在没有--record
选项的情况下,每当你变更一次镜像之后,就必须手动修改一次注释......就像这样:
kubectl set image deployment deploy-update nginx=nginx:latest
kubectl annotate deployment deploy-update kubernetes.io/change-cause="Update image to nginx:latest"
2
3
笔记
未来--record
注定会被抛弃,所以从现在开始要养成良好习惯。
每当执行变更命令之后,都手动记录一下注释信息kubernetes.io/change-cause="<变更原因>"
如果你没有添加--record
选项,又没有手动修改注释信息。那不好意思,本次变更将不会记录新信息。
# 22. Deployment镜像回滚
当你变更了一个 deployment 的镜像之后,发现这个镜像有 BUG,或者遇到了不兼容当前业务之类的情况,该如何将镜像还原到之前的版本呢?
在列出镜像变更历史记录的时候,你可以通过--revision
选项查看某次变更的详细信息。
1、例如,查看记录 3 的变更详情:
kubectl rollout history deployment deploy-update --revision=3
成功列出了记录 3 的变更详情,虽然注释信息里写的是... nginx=nginx:1.8
,但是实际变更的还是 nginx:1.9
版本。这印证了之前说过的一句话 “注释只是对集群资源的解释和说明,旨在提高阅读性,不会被作用于任何方面”。
所以,就算你忘记为某一条记录设置注释了,也不会对那一次的实际变更产生影响。
history
命令和--revision
选项可以列出某条记录的详情。如果想要回滚镜像,则需要使用undo
命令和--to-revision
选项。
2、例如,将这个控制器的镜像回滚至记录 3 的状态:
kubectl rollout undo deployment deploy-update --to-revision=3
成功将其镜像回滚到了记录 3 所设置的镜像nginx:1.9
。
3、再次查看镜像变更记录:
kubectl rollout history deployment deploy-update
kubectl rollout history deployment deploy-update --revision=6
2
3
我们刚刚执行的明明是 “回滚” 操作,但却离奇多出了一条 “变更” 记录 6。
查看记录 6 的详情之后你会发现,所谓的 “回滚”,原来就是 k8s 自动帮你执行了一条变更命令,将状态改回去。
好嘛,原来回滚(rollout undo
)里面包装了一个变更(set image
),那我自己变回去不也是一样的?为啥还要回滚?
其实,在查看 变更详情 的时候你应该注意到了,能够 “变更” 的参数不止镜像一个。当变更的参数多起来时,你也需要添加更多的参数选项。
一条命令的参数选项越多,出错的几率也越大。而 “回滚” 可以识别历史变更记录,然后自动为你执行 “变更” 命令,这有助于节省工作量。
笔记
“回滚” 是简洁化的 “变更”。
# 33. Deployment镜像滚动更新
# 3.13.1 为什么需要滚动更新
在 “1.2.1” 小节中我们得知,当 deployment 的镜像被变更之后,它会删除旧的 Pod,然后使用新镜像来创建新的 Pod。
假设有一个 deployment 控制了五个 Pod,默认情况下,当它的镜像被变更之后,它会立马删除五个 Pod,然后再创建另外五个新的 Pod。
这种默认的变更方式有点粗暴,这五个 Pod 在同一时间被删除,而重新创建和运行肯定需要一些时间,在此期间业务都会处于中断状态。
这时候就需要用到滚动更新。
# 3.23.2 配置滚动更新
在创建 deployment 时可以通过spec.strategy
字段来为其配置滚动更新:
maxSurge
:当镜像被变更时,依次创建25%
个新的 Pod,直到新的 Pod 数量达到控制器上限maxUnavailable
:当镜像被变更时,依次删除25%
个旧的 Pod,直到旧的 Pod 全部被删光
...
spec:
strategy:
rollingUpdate:
maxSurge: 25%
maxUnavailable: 25%
type: RollingUpdate
...
2
3
4
5
6
7
8
1、为了更好地看出效果,先将deploy-update
的副本数修改为4
:
kubectl scale deployment deploy-update --replicas=4
2、由于我们的 deployment 已经处于运行状态,所以通过edit
命令来编辑它,然后为其添加滚动更新:
kubectl edit deployment deploy-update
刚想编辑,发现这个 deployment 已经配置了滚动更新。可是我们之前创建这个 deployment 的时候明明没有为其配置?
k8s 想的很周到,如果你没有为 deployment 配置滚动更新,那么它就会提供一个默认值:依次删除25%
个旧的 Pod、并依次创建25%
个新的 Pod。这个百分比是根据 副本数 来计算具体数量的。
我们刚刚将 deployment 的副本数调整为了4
个,也就是说,当这个 deployment 的镜像被变更时,它会依次删除1
个旧的 Pod、并依次创建1
个新的 Pod,直到所有的 Pod 都完成镜像变更。
3、此外,如果你不想使用百分比,你也可以提供一个绝对数值:
...
spec:
strategy:
rollingUpdate:
maxSurge: 1
maxUnavailable: 1
type: RollingUpdate
...
2
3
4
5
6
7
8
如图所示,不用改变字段名称,直接将 值 修改为数字即可。
然后保存退出。
4、然后变更其镜像,并不断查看 Pod 情况:
kubectl set image deployment deploy-update nginx=nginx:1.7.9
kubectl get pods
2
3
删除和创建 Pod 的速度很快,但看得还是很清楚。
- 第一次查看时
- 已经有三个新的 Pod 被创建出来,其中一个刚刚进入 “Running” 状态
- 已经有一个旧的 Pod 被删除;一个旧的 Pod 正处于删除状态(Terminating);还有两个旧的 Pod 处于运行状态,等着被删除
- 第二次查看时
- 新的 Pod 已经全部创建完成,并进入运行状态,它们接手了之前的工作任务
- 旧的 Pod 即将被完全删除,还剩下一个
- 第三次查看时,滚动更新已经完成
笔记
在执行滚动更新时,创建新 Pod 和删除旧 Pod 这两个操作是同时进行的,并不存在先后顺序。
# 44. 一个常见疑惑
我们前面变更镜像,都只是单纯地更改了 nginx 镜像的版本号,能否将 nginx 镜像修改为其它镜像?
1、当然可以,还是以之前的 deployment 为例,将其镜像变更为alpine
:
kubectl set image deployment deploy-update nginx=alpine
成功将镜像nginx:1.7.9
变更为了alpine
。
在查看 Pod 信息时,你会发现两个新创建出来的 Pod 貌似遇到了错误,状态一直停留在 “CrashLoopBackOff”。
别担心,这其实是 Pod 生命周期的缘故,由于镜像alpine
默认不会在容器中执行任何启动命令,所以 Pod 的生命周期会很快结束,然后状态会被标记为 “Completed”(运行完毕)。
而由于 滚动更新 的存在,如果 deployment 在创建新的 Pod 时遇到了错误,便会停止变更。可想而知,如果此时没有滚动更新,那么所有的 Pod 都将处于不可用状态,这会对业务造成多大的影响?
2、如果你想解决这个问题,可以编辑 deployment 中的 Pod 模板,为其添加spec.containers.command
(启动时命令):
kubectl edit deployment deploy-update
# 这将使容器休眠 9999999 秒
command: ['sh', '-c', 'sleep 9999999']
2
保存退出之后,由于sleep
命令使 Pod 生命周期得到了延长,错误得以解决,所以滚动更新会继续进行。
# 55. 小结
本章内容较为简单,但你仍需掌握:
- 通过命令行变更 deployment 的镜像
- 通过注释来记录变更信息
- 查看历史变更记录(列表)
- 查看历史变更记录(详情)
- 回滚镜像
- 配置滚动更新
去做一些练习吧(练习题-7),这肯定难不到你。