一文吃透 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

执行创建命令并查看资源状态:

复制代码
[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.ymlhttp-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 滚动更新的实现原理和版本回滚方法。

核心要点总结

  1. 滚动更新 通过逐渐增加新 ReplicaSet 的 Pod 数量并减少旧 ReplicaSet 的 Pod 数量,实现了零停机的应用升级。

  2. Deployment 控制器 自动管理多个 ReplicaSet 的"扩容/缩容"过程,用户只需修改 Pod 模板并重新 apply。

  3. 版本回滚 依赖于 Deployment 的 revision 历史记录,通过 kubectl rollout undo 可以快速恢复到任意稳定版本。

  4. 重要命令和参数

    • kubectl apply -f xxx.yml --record:应用配置并记录命令。

    • kubectl rollout history deployment <name>:查看历史版本。

    • kubectl rollout undo deployment <name> --to-revision=<N>:回滚到指定版本。

    • revisionHistoryLimit:控制保留的历史版本数量。

熟练掌握滚动更新和回滚技巧,是保障 Kubernetes 生产服务稳定性的必备能力。希望本文能帮助你更好地理解和运用 Deployment 的更新机制。

相关推荐
Zhu7581 小时前
在Docker环境部署ApacheGuacamole,对接PG数据库
数据库·docker·容器
少陽君2 小时前
Docker 部署 Loki + Promtail + Grafana 日志监控栈
docker·容器·grafana
独自归家的兔2 小时前
2026年7月2日 Docker 容器化部署 PaddleOCR-VL 实战(OpenAI 协议兼容 HTTP 调用)
运维·docker·容器
kylin-运维3 小时前
k8s可视化看板嵌入AI助手(悬浮对话球)
人工智能·容器·kubernetes·aicoding
会飞的土拨鼠呀4 小时前
Docker 容器维护手册
docker·容器·eureka
hhb_6184 小时前
云原生AI工作流分布式调度实战
人工智能·云原生
慕伏白4 小时前
【慕伏白】Docker 常用指令
docker·容器·eureka
阿里云云原生4 小时前
明文不扩散,日志仍可用:阿里云可观测敏感数据保护方案
云原生
java_logo4 小时前
Docker 部署 Trilium Notes 完整教程:自建大型知识库
docker·容器·trilium·trilium note·trilium部署·docker部署trilium·trilium部署文档