一文吃透 K8s:Deployment 滚动更新与版本回滚
前言在传统的应用升级流程中通常需要先将旧版本服务下线停止业务然后更新配置和代码最后再重新启动服务。这种“先停后更”的方式会导致服务出现一段明显的不可用窗口。如果业务集群规模较大逐个实例升级虽然可行但操作复杂且容易出错。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执行创建命令并查看资源状态[rootmaster k8s]# kubectl apply -f httpd.yml [rootmaster 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 apphttpd [rootmaster 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 apphttpd,pod-template-hash67cc966bc4 [rootmaster 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可以看到初始状态是一个 ReplicaSethash 后缀 67cc966bc4管理 3 个 httpd:2.4.33 的 Pod。2.2 更新镜像版本将httpd.yml中的镜像版本从httpd:2.4.33修改为httpd:2.4.38然后再次执行kubectl apply[rootmaster k8s]# kubectl apply -f httpd.yml随后观察 ReplicaSet 的变化[rootmaster 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 apphttpd,pod-template-hash65996464c7 httpd2.4.33-deployment-67cc966bc4 0 0 0 5m httpd httpd:2.4.33 apphttpd,pod-template-hash67cc966bc4旧的 ReplicaSet67cc966bc4的 Pod 数量已变为 0新的 ReplicaSet65996464c7则接管了 3 个新 Pod镜像更新为 2.4.38。2.3 查看滚动更新的事件过程通过kubectl describe deployment可以查看更新过程中的详细事件[rootmaster k8s]# kubectl describe deployment httpd2.4.33-deployment输出的事件摘要如下简化为文字描述时间点事件开始新 ReplicaSet2.4.38扩容到 1 个 Pod旧 ReplicaSet2.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 中[rootmaster roll]# kubectl apply -f http-2.4.39.yml --record [rootmaster roll]# kubectl apply -f http-2.4.41.yml --record [rootmaster roll]# kubectl apply -f http-2.4.43.yml --record3.3 查看 revision 历史[rootmaster roll]# kubectl rollout history deployment httpd-roll-deployment deployment.apps/httpd-roll-deployment REVISION CHANGE-CAUSE 1 kubectl apply --filenamehttp-2.4.39.yml --recordtrue 2 kubectl apply --filenamehttp-2.4.41.yml --recordtrue 3 kubectl apply --filenamehttp-2.4.43.yml --recordtrue当前运行的版本是 3httpd:2.4.43[rootmaster 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 apphttpd3.4 回滚到指定版本假设我们需要回滚到 revision 2httpd:2.4.41执行[rootmaster roll]# kubectl rollout undo deployment httpd-roll-deployment --to-revision2 deployment.apps/httpd-roll-deployment rolled back查看当前镜像已回退到 2.4.41[rootmaster 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 apphttpd3.5 回滚后的 revision 变化再次查看历史记录[rootmaster roll]# kubectl rollout history deployment httpd-roll-deployment deployment.apps/httpd-roll-deployment REVISION CHANGE-CAUSE 1 kubectl apply --filenamehttp-2.4.39.yml --recordtrue 3 kubectl apply --filenamehttp-2.4.43.yml --recordtrue 4 kubectl apply --filenamehttp-2.4.41.yml --recordtrue原来的 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-revisionN回滚到指定版本。revisionHistoryLimit控制保留的历史版本数量。熟练掌握滚动更新和回滚技巧是保障 Kubernetes 生产服务稳定性的必备能力。希望本文能帮助你更好地理解和运用 Deployment 的更新机制。