前言
在传统的应用升级流程中,通常需要先将旧版本服务下线,停止业务,然后更新配置和代码,最后再重新启动服务。这种"先停后更"的方式会导致服务出现一段明显的不可用窗口。如果业务集群规模较大,逐个实例升级虽然可行,但操作复杂且容易出错。Kubernetes 针对这一痛点提供了**滚动更新(Rolling Update)**机制,使得应用可以在不中断服务的前提下完成版本迭代。本文将结合一个实际的 httpd 镜像升级案例,详细分析滚动更新的实现逻辑,并介绍如何利用 Deployment 的 revision 历史记录实现快速版本回滚。
一、什么是滚动更新
滚动更新是 Kubernetes 对 Pod 进行批量更新的策略。其核心思想是:一次只更新一小部分副本,成功后再更新更多副本,直至所有副本都更新到新版本 。在整个过程中,始终有部分副本处于运行状态,因此服务不会中断,实现了零停机的发布效果。
Deployment 控制器通过管理多个 ReplicaSet 来实现滚动更新:每发布一个新版本,就会创建一个新的 ReplicaSet,然后逐步增加新 ReplicaSet 的 Pod 数量,同时逐步减少旧 ReplicaSet 的 Pod 数量,直到旧 ReplicaSet 的 Pod 数量降为 0。
二、实例分析滚动更新实现逻辑
下面我们通过一个具体的例子来观察滚动更新的全过程。
2.1 创建初始 Deployment
编写 httpd.yml 文件,内容如下:
apiVersion: apps/v1
kind: Deployment
metadata:
name: httpd2.4.33-deployment
spec:
replicas: 3
selector:
matchLabels:
app: httpd
template:
metadata:
labels:
app: httpd
spec:
containers:
- name: httpd
image: httpd:2.4.33
ports:
- containerPort: 80
执行创建命令并查看资源状态:
[root@master k8s]# kubectl apply -f httpd.yml
[root@master k8s]# kubectl get deploy -o wide
NAME READY UP-TO-DATE AVAILABLE AGE CONTAINERS IMAGES SELECTOR
httpd2.4.33-deployment 3/3 3 3 30s httpd httpd:2.4.33 app=httpd
[root@master k8s]# kubectl get replicaset -o wide
NAME DESIRED CURRENT READY AGE CONTAINERS IMAGES SELECTOR
httpd2.4.33-deployment-67cc966bc4 3 3 3 45s httpd httpd:2.4.33 app=httpd,pod-template-hash=67cc966bc4
[root@master k8s]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE
httpd2.4.33-deployment-67cc966bc4-22r6k 1/1 Running 0 50s 10.244.2.27 node2
httpd2.4.33-deployment-67cc966bc4-cf468 1/1 Running 0 50s 10.244.2.26 node2
httpd2.4.33-deployment-67cc966bc4-px62t 1/1 Running 0 50s 10.244.1.32 node1
可以看到,初始状态是一个 ReplicaSet(hash 后缀 67cc966bc4)管理 3 个 httpd:2.4.33 的 Pod。
2.2 更新镜像版本
将 httpd.yml 中的镜像版本从 httpd:2.4.33 修改为 httpd:2.4.38,然后再次执行 kubectl apply:
[root@master k8s]# kubectl apply -f httpd.yml
随后观察 ReplicaSet 的变化:
[root@master k8s]# kubectl get replicaset -o wide
NAME DESIRED CURRENT READY AGE CONTAINERS IMAGES SELECTOR
httpd2.4.33-deployment-65996464c7 3 3 3 2m httpd httpd:2.4.38 app=httpd,pod-template-hash=65996464c7
httpd2.4.33-deployment-67cc966bc4 0 0 0 5m httpd httpd:2.4.33 app=httpd,pod-template-hash=67cc966bc4
旧的 ReplicaSet(67cc966bc4)的 Pod 数量已变为 0,新的 ReplicaSet(65996464c7)则接管了 3 个新 Pod,镜像更新为 2.4.38。
2.3 查看滚动更新的事件过程
通过 kubectl describe deployment 可以查看更新过程中的详细事件:
[root@master k8s]# kubectl describe deployment httpd2.4.33-deployment
输出的事件摘要如下(简化为文字描述):
| 时间点 | 事件 |
|---|---|
| 开始 | 新 ReplicaSet(2.4.38)扩容到 1 个 Pod,旧 ReplicaSet(2.4.33)缩容到 2 个 Pod |
| 间隔几秒 | 新 ReplicaSet 扩容到 2 个 Pod,旧 ReplicaSet 缩容到 1 个 Pod |
| 最后 | 新 ReplicaSet 扩容到 3 个 Pod,旧 ReplicaSet 缩容到 0 个 Pod |
整个滚动更新过程一次只替换一个 Pod,始终保持至少有 2 个 Pod 可用,因此服务不会中断。
2.4 滚动更新的关键参数
Deployment 支持通过以下两个字段控制滚动更新的行为(可在 spec 中配置):
-
maxSurge:更新过程中允许超出期望副本数的最大 Pod 数量(绝对值或百分比),默认 25%。 -
maxUnavailable:更新过程中允许不可用的最大 Pod 数量(绝对值或百分比),默认 25%。
例如,在 Deployment 的 spec.strategy.rollingUpdate 下可自定义这些值。
三、K8s 中版本回滚方法
在实际生产环境中,新版本上线后如果出现问题,我们需要能够快速回滚到之前的稳定版本。Kubernetes 的 Deployment 会为每一次更新保存一个 revision(版本记录) ,通过 rollout 命令可以查看历史并执行回滚。
3.1 准备多个版本的 Deployment
创建三个 Deployment 文件,分别对应 httpd 的 2.4.39、2.4.41、2.4.43 镜像。这里以 http-2.4.39.yml 为例:
apiVersion: apps/v1
kind: Deployment
metadata:
name: httpd-roll-deployment
spec:
revisionHistoryLimit: 10 # 保留最近10个版本
replicas: 3
selector:
matchLabels:
app: httpd
template:
metadata:
labels:
app: httpd
spec:
containers:
- name: httpd
image: httpd:2.4.39
ports:
- containerPort: 80
类似地,创建 http-2.4.41.yml 和 http-2.4.43.yml,只需修改 image 字段的版本号。
3.2 依次应用并记录版本
使用 --record 参数将执行的命令记录到 revision 中:
[root@master roll]# kubectl apply -f http-2.4.39.yml --record
[root@master roll]# kubectl apply -f http-2.4.41.yml --record
[root@master roll]# kubectl apply -f http-2.4.43.yml --record
3.3 查看 revision 历史
[root@master roll]# kubectl rollout history deployment httpd-roll-deployment
deployment.apps/httpd-roll-deployment
REVISION CHANGE-CAUSE
1 kubectl apply --filename=http-2.4.39.yml --record=true
2 kubectl apply --filename=http-2.4.41.yml --record=true
3 kubectl apply --filename=http-2.4.43.yml --record=true
当前运行的版本是 3(httpd:2.4.43):
[root@master roll]# kubectl get deploy -o wide
NAME READY UP-TO-DATE AVAILABLE AGE CONTAINERS IMAGES SELECTOR
httpd-roll-deployment 3/3 3 3 2m httpd httpd:2.4.43 app=httpd
3.4 回滚到指定版本
假设我们需要回滚到 revision 2(httpd:2.4.41),执行:
[root@master roll]# kubectl rollout undo deployment httpd-roll-deployment --to-revision=2
deployment.apps/httpd-roll-deployment rolled back
查看当前镜像已回退到 2.4.41:
[root@master roll]# kubectl get deploy -o wide
NAME READY UP-TO-DATE AVAILABLE AGE CONTAINERS IMAGES SELECTOR
httpd-roll-deployment 3/3 3 3 4m httpd httpd:2.4.41 app=httpd
3.5 回滚后的 revision 变化
再次查看历史记录:
[root@master roll]# kubectl rollout history deployment httpd-roll-deployment
deployment.apps/httpd-roll-deployment
REVISION CHANGE-CAUSE
1 kubectl apply --filename=http-2.4.39.yml --record=true
3 kubectl apply --filename=http-2.4.43.yml --record=true
4 kubectl apply --filename=http-2.4.41.yml --record=true
原来的 revision 2 变成了 revision 4(因为回滚操作本身产生了一个新的 revision)。注意 :如果当初创建 Deployment 时没有加 --record,那么 CHANGE-CAUSE 列会显示 <none>,不利于识别每个 revision 的内容,因此建议在 apply 时始终加上 --record 参数。
3.6 控制 revision 保留数量
通过 Deployment 的 spec.revisionHistoryLimit 字段可以设置保留多少个历史 revision,默认值为 10。如果设置成 0,则不会保留任何历史版本,也就无法回滚。
结尾
本文通过两个实战案例,详细介绍了 Kubernetes 滚动更新的实现原理和版本回滚方法。
核心要点总结:
-
滚动更新 通过逐渐增加新 ReplicaSet 的 Pod 数量并减少旧 ReplicaSet 的 Pod 数量,实现了零停机的应用升级。
-
Deployment 控制器 自动管理多个 ReplicaSet 的"扩容/缩容"过程,用户只需修改 Pod 模板并重新 apply。
-
版本回滚 依赖于 Deployment 的 revision 历史记录,通过
kubectl rollout undo可以快速恢复到任意稳定版本。 -
重要命令和参数:
-
kubectl apply -f xxx.yml --record:应用配置并记录命令。 -
kubectl rollout history deployment <name>:查看历史版本。 -
kubectl rollout undo deployment <name> --to-revision=<N>:回滚到指定版本。 -
revisionHistoryLimit:控制保留的历史版本数量。
-
熟练掌握滚动更新和回滚技巧,是保障 Kubernetes 生产服务稳定性的必备能力。希望本文能帮助你更好地理解和运用 Deployment 的更新机制。