Kubernetes 基础体系 · 第 57/83 篇。示例基于 Kubernetes 当前稳定 API;弃用、版本偏差、云厂商差异和生产风险会明确说明。

Kubernetes 发布策略:Rolling、Canary、Blue-Green、流量和回滚

Kubernetes 中的“发布”不是单一动作,而是多个控制器共同完成的一组状态变化:

  1. 修改工作负载的期望状态,例如 Deployment 的 Pod 模板或副本数;
  2. Deployment 控制器创建、扩缩 ReplicaSet;
  3. ReplicaSet 创建或删除 Pod;
  4. Pod 经过调度、启动和就绪探针检查;
  5. Service、EndpointSlice、Ingress 或 Gateway 将流量发送到符合条件的 Pod;
  6. 监控系统判断新版本是否满足业务指标;
  7. 必要时把期望状态恢复到旧版本。

因此,滚动更新解决的是 Pod 替换过程,Canary 和 Blue-Green 解决的是版本暴露方式,流量管理解决的是请求如何到达版本,回滚解决的是如何恢复期望状态。它们可以组合使用,也不能互相简单替代。


一、发布前需要区分的几个对象

1. Deployment、ReplicaSet 和 Pod

Deployment 描述应用的期望状态,例如:

  • 使用哪个容器镜像;
  • 运行多少个副本;
  • Pod 具有什么标签;
  • 更新时最多增加或减少多少副本。

Deployment 控制器不会直接长期管理每一个 Pod,而是通过 ReplicaSet 管理 Pod。每当 Pod 模板发生变化,Deployment 通常会创建一个新的 ReplicaSet,并逐步缩小旧 ReplicaSet、扩大新 ReplicaSet。

例如,下面两个 Deployment 的 spec.template 不同:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: web
spec:
  replicas: 3
  selector:
    matchLabels:
      app: web
  template:
    metadata:
      labels:
        app: web
    spec:
      containers:
        - name: web
          image: example/web:v1
          ports:
            - containerPort: 8080

把镜像改成 example/web:v2 后,Deployment 控制器通常会保留旧 ReplicaSet,并新建一个 ReplicaSet。旧 ReplicaSet 和新 ReplicaSet 的 Pod 可能在一段时间内同时存在。

2. Service 不是版本控制器

Service 通过标签选择器找到后端 Pod。例如:

apiVersion: v1
kind: Service
metadata:
  name: web
spec:
  selector:
    app: web
  ports:
    - port: 80
      targetPort: 8080

这个 Service 只关心 Pod 是否具有 app: web 标签,不关心 Pod 来自哪个 Deployment 或 ReplicaSet。

如果滚动更新期间新旧 Pod 都具有 app: web,那么 Service 会同时把流量发送给两个版本。也就是说:

  • Deployment 决定 Pod 如何替换;
  • Service 决定哪些 Pod 可以接收流量;
  • Service 本身不理解“版本”“灰度比例”或“发布批次”。

3. Ready 不等于业务正确

Service 通常只会把通过就绪检查的 Pod 作为可用端点。一个常见的 Deployment 配置如下:

spec:
  template:
    spec:
      containers:
        - name: web
          image: example/web:v2
          readinessProbe:
            httpGet:
              path: /ready
              port: 8080
            initialDelaySeconds: 2
            periodSeconds: 5
            failureThreshold: 3
          livenessProbe:
            httpGet:
              path: /healthz
              port: 8080
            periodSeconds: 10

readinessProbe 影响流量是否进入 Pod;livenessProbe 影响 kubelet 是否重启容器。就绪探针只能证明 /ready 达到了应用声明的条件,不能证明:

  • 关键业务请求一定成功;
  • 数据库迁移已经兼容;
  • 延迟没有显著上升;
  • 新版本只对特定用户失败。

因此,发布控制不能只依赖 Pod 的 Ready 状态,还需要业务指标和日志。


二、Rolling:Deployment 的滚动更新

1. Rolling 的核心含义

Rolling update(滚动更新)是指新旧 Pod 在一段时间内并存,控制器逐步创建新 Pod、等待其就绪,再逐步删除旧 Pod。

Deployment 的默认更新策略通常是:

spec:
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxUnavailable: 25%
      maxSurge: 25%

两个参数含义不同:

  • maxUnavailable:更新过程中,允许低于期望副本数的最大数量;
  • maxSurge:更新过程中,允许超过期望副本数的最大数量。

假设:

spec:
  replicas: 10
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxUnavailable: 2
      maxSurge: 3

则控制器在更新过程中大致遵守:

可用 Pod 数 >= 10 - 2 = 8
总 Pod 数 <= 10 + 3 = 13

这里的“可用”不是“进程存在”,而是满足 Deployment 可用性判断的 Pod,通常要求 Pod 已经 Ready,并满足 minReadySeconds 等条件。

百分比会按 Kubernetes 的规则转换为数量。对 replicas: 10

  • maxSurge: 25% 通常向上取整为 3;
  • maxUnavailable: 25% 通常向下取整为 2。

如果把两者都设置为 0,更新将无法推进,因为既不能增加新 Pod,也不能减少旧 Pod:

maxUnavailable: 0
maxSurge: 0

这个组合不是合法的可推进滚动策略。

2. 一次滚动更新的中间状态

仍以 10 个副本、maxSurge: 3maxUnavailable: 2 为例,可能经历以下过程:

初始:
旧 ReplicaSet = 10
新 ReplicaSet = 0
可用 Pod = 10

创建新 Pod:
旧 = 10,新 = 3,总数 = 13
如果新 Pod 尚未 Ready,可用 Pod 仍可能只有 10

新 Pod 就绪后:
旧 = 8,新 = 3,可用 Pod = 11
删除 2 个旧 Pod,释放更新空间

继续创建:
旧 = 8,新 = 5,总数 = 13

反复执行“等待新 Pod 就绪 -> 缩小旧 ReplicaSet -> 扩大新 ReplicaSet”

最终:
旧 = 0,新 = 10

实际顺序会受到调度、镜像拉取、探针、终止宽限期和控制器调谐时机影响。上面的过程表示约束和方向,而不是一个固定的逐秒算法。

旧 Pod 的终止也不是瞬间完成的。容器可能执行 preStop,进程还要等待 terminationGracePeriodSeconds。因此在终止期间,节点上短时间内可能同时存在已进入终止状态的 Pod 和新 Pod,实际资源峰值可能高于简单的 replicas + maxSurge 估算。生产容量评估必须考虑终止延迟。

3. minReadySecondsprogressDeadlineSeconds

minReadySeconds 要求新 Pod 在被视为可用前持续 Ready 一段时间:

spec:
  minReadySeconds: 30

它可以避免 Pod 刚通过探针就立即被认为稳定,但它不是业务稳定性检测。应用即使连续 30 秒健康,也可能在真实流量下失败。

progressDeadlineSeconds 用于声明发布在多长时间内没有进展时应被认为异常:

spec:
  progressDeadlineSeconds: 600

重要边界是:Deployment 检测到 ProgressDeadlineExceeded 不会自动回滚 Deployment。它会更新状态条件,供运维系统、CI/CD 或发布控制器采取动作。

可以通过以下命令查看状态:

kubectl rollout status deployment/web --timeout=10m
kubectl describe deployment web
kubectl get rs -l app=web
kubectl get pods -l app=web -o wide

如果新 Pod 因镜像不存在、配置错误或探针失败而无法 Ready,常见现象是:

  • 新 ReplicaSet 副本数增加但 Ready 数为 0;
  • 旧 ReplicaSet 没有完全缩小;
  • kubectl rollout status 超时;
  • Deployment 出现 ProgressDeadlineExceeded
  • Service 仍可能把流量发送给旧版本。

这通常是保护行为,而不是“滚动更新没有生效”。


三、暂停、恢复和 Revision

1. Revision 表示什么

Deployment 的每一次 Pod 模板变更通常会产生一个新的 revision,并记录在 ReplicaSet 的注解中:

kubectl rollout history deployment/web

可能看到:

deployment.apps/web
REVISION  CHANGE-CAUSE
1         <none>
2         <none>
3         <none>

可以通过命令补充变更原因:

kubectl annotate deployment/web \
  kubernetes.io/change-cause="upgrade web image to v2" \
  --overwrite

更常见的做法是在声明文件或发布系统中维护变更信息。kubernetes.io/change-cause 主要帮助人阅读,它不会改变控制器行为。

revision 不是一个永久保存的完整快照。旧 ReplicaSet 会受 revisionHistoryLimit 影响:

spec:
  revisionHistoryLimit: 10

默认值通常为 10。历史 ReplicaSet 被清理后,就不能依赖它执行对应的 Deployment 回滚。并且,即使 ReplicaSet 仍然存在,外部依赖的 ConfigMap、Secret、镜像仓库内容或数据库状态也未必能恢复到原样。

2. 暂停发布以便检查

可以暂停一个正在更新的 Deployment:

kubectl rollout pause deployment/web

暂停后,控制器不会继续推进该 Deployment 的滚动更新。可以在暂停期间检查 Pod、修改容器资源或探针配置,再恢复:

kubectl rollout resume deployment/web
kubectl rollout status deployment/web

暂停不是事务边界。已经创建的 Pod 不会自动消失,外部系统也可能继续修改资源。尤其在 GitOps 系统中,手工暂停可能很快被声明状态覆盖,或者恢复后又被调谐到仓库中的配置。


四、Canary:先让少量流量接触新版本

1. Canary 的定义

Canary release(Canary 发布、灰度发布)是让新版本只接收一部分真实流量或用户,观察指标后再扩大暴露范围。

Canary 的关键不是“新版本只有一个 Pod”,而是:

  1. 新版本与稳定版本同时运行;
  2. 两个版本都可能接收生产请求;
  3. 暴露比例或用户范围可控制;
  4. 根据指标决定继续放量、暂停或回滚。

Kubernetes 原生 Deployment 没有提供“按请求权重把 10% 流量发送到某个 Deployment”的通用 API。通常需要组合多个 Deployment、Service,以及 Ingress、Gateway 或外部负载均衡器。

2. 仅按副本数做近似灰度

一种简单方案是让两个 Deployment 使用同一个 Service:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: web-stable
spec:
  replicas: 9
  selector:
    matchLabels:
      app: web
      track: stable
  template:
    metadata:
      labels:
        app: web
        track: stable
    spec:
      containers:
        - name: web
          image: example/web:v1
          ports:
            - containerPort: 8080
          readinessProbe:
            httpGet:
              path: /ready
              port: 8080
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: web-canary
spec:
  replicas: 1
  selector:
    matchLabels:
      app: web
      track: canary
  template:
    metadata:
      labels:
        app: web
        track: canary
    spec:
      containers:
        - name: web
          image: example/web:v2
          ports:
            - containerPort: 8080
          readinessProbe:
            httpGet:
              path: /ready
              port: 8080
---
apiVersion: v1
kind: Service
metadata:
  name: web
spec:
  selector:
    app: web
  ports:
    - port: 80
      targetPort: 8080

这里 Service 选择 app: web,所以稳定版和 Canary 都是后端。

如果有 9 个稳定 Pod 和 1 个 Canary Pod,常被口头称为“10% 灰度”。但这只是粗略估算:

名义 Pod 比例 = 1 / (9 + 1) = 10%

它不等于严格的请求比例,原因包括:

  • Service 的转发通常以连接或流为粒度,不一定每个 HTTP 请求重新选择后端;
  • HTTP/2、Keep-Alive、WebSocket 会让一个连接承载大量请求;
  • 不同 Pod 的处理能力、请求耗时和连接数可能不同;
  • 客户端重试可能改变实际流量;
  • 节点、可用区和外部负载均衡器可能引入额外分布差异。

因此,“副本数 1:9”适合低成本近似灰度,不适合需要严格请求比例、按用户分组或按请求头路由的场景。

3. 按流量做 Canary

如果需要真正控制 HTTP 请求比例,流量入口必须理解路由规则。例如,Ingress 控制器、服务网格或云负载均衡器可能提供:

  • 权重路由;
  • 按 Cookie 路由;
  • 按请求头路由;
  • 按用户 ID、租户或地域路由;
  • 逐步修改权重。

这些能力不是 Service API 的通用保证。不同 Ingress 控制器对注解的解释不同,云厂商的负载均衡器也可能有独立配置。不能把某个控制器的注解当成 Kubernetes 通用字段。

Gateway API 提供更结构化的 HTTP 路由模型,但具体的权重能力、实现完整度和版本仍取决于所安装的 Gateway 实现。部署前应检查目标控制器支持的 Gateway API 版本和字段,而不是只验证 YAML 能否通过 API Server。

一个流量型 Canary 的数据流可以表示为:

flowchart LR
    C[客户端] --> LB[外部负载均衡器或 Ingress/Gateway]
    LB -->|90%| S[稳定版 Service/后端]
    LB -->|10%| G[Canary Service/后端]
    S --> SP[稳定版 Pods]
    G --> CP[Canary Pods]
    M[指标系统] --> D[发布控制器或人工决策]
    D --> LB
    D --> G

关键路径是:流量入口先做版本选择,Service 再把请求转发到对应版本的可用 Pod。指标系统不在请求转发链路中,它负责观察结果并推动下一次配置变更。

4. Canary 的放量不是自动发生的

一个严谨的 Canary 流程至少需要定义状态:

稳定版 100%
    -> Canary 5%
    -> Canary 25%
    -> Canary 50%
    -> Canary 100%

每一步都应有:

  • 观察窗口;
  • 成功条件;
  • 失败条件;
  • 最大等待时间;
  • 回滚动作。

例如设错误率为 e,请求数为 n,则一段时间内的错误请求数可记为:

errors = e × n

但仅比较错误率还不够。低流量阶段的样本数过少时,e 的波动很大;Canary 只接收少数特定用户时,整体错误率也可能掩盖该群体的严重失败。因此应同时观察请求量、错误率、P95/P99 延迟、资源使用、业务成功率和日志异常。

Kubernetes 原生 Deployment 不负责上述统计和逐步放量。可以由 CI/CD、GitOps 流水线或专门的渐进式交付控制器执行;使用第三方控制器时,应明确其 CRD、控制器版本和故障行为。


五、Blue-Green:两个完整环境之间切换

1. Blue-Green 的定义

Blue-Green 发布同时维护两套完整环境:

  • Blue:当前在线版本;
  • Green:待验证的新版本。

新版本先在不接收生产流量,或只接收内部验证流量的环境中启动。验证通过后,将入口从 Blue 切换到 Green。

与 Rolling 的主要差异是:

  • Rolling 通常在同一个 Deployment 内逐步替换 Pod;
  • Blue-Green 通常保留两套独立的工作负载;
  • Rolling 的新旧版本天然可能混合接收流量;
  • Blue-Green 可以在服务级别把生产流量集中切到一套环境。

一种简化配置如下:

apiVersion: v1
kind: Service
metadata:
  name: web
spec:
  selector:
    app: web
    track: blue
  ports:
    - port: 80
      targetPort: 8080
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: web-blue
spec:
  replicas: 3
  selector:
    matchLabels:
      app: web
      track: blue
  template:
    metadata:
      labels:
        app: web
        track: blue
    spec:
      containers:
        - name: web
          image: example/web:v1
          ports:
            - containerPort: 8080
          readinessProbe:
            httpGet:
              path: /ready
              port: 8080
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: web-green
spec:
  replicas: 3
  selector:
    matchLabels:
      app: web
      track: green
  template:
    metadata:
      labels:
        app: web
        track: green
    spec:
      containers:
        - name: web
          image: example/web:v2
          ports:
            - containerPort: 8080
          readinessProbe:
            httpGet:
              path: /ready
              port: 8080

确认 Green 已经 Ready 后切换 Service:

kubectl patch service web \
  --type=merge \
  -p '{"spec":{"selector":{"app":"web","track":"green"}}}'

验证入口:

kubectl get service web -o yaml
kubectl get endpointslice -l kubernetes.io/service-name=web
kubectl rollout status deployment/web-green

回切 Blue:

kubectl patch service web \
  --type=merge \
  -p '{"spec":{"selector":{"app":"web","track":"blue"}}}'

2. Service 切换不是全局瞬时事务

修改 Service 的 Selector 后,API Server 中的对象会先变化,EndpointSlice 控制器再根据选择器更新端点,节点上的转发规则和外部负载均衡器还可能继续传播。因此不同节点、不同代理或不同客户端在短时间内可能观察到不同状态。

此外,已经建立的长连接可能继续服务原来的后端,具体行为取决于连接跟踪和代理实现。Blue-Green 的“切换”应理解为控制面配置在一个 API 对象上的变更,以及数据面最终收敛,而不是所有请求在同一纳秒完成切换。

3. Blue-Green 的资源和数据风险

Blue-Green 通常需要同时运行两套副本,因此资源需求接近两倍,至少要为新环境预留:

  • CPU 和内存;
  • Pod IP;
  • 节点容量;
  • 外部负载均衡器后端配额;
  • 数据库连接数;
  • 缓存和消息消费者容量。

它也不能自动解决数据库兼容性问题。假设 v2 执行了不可逆数据库迁移,随后切回 v1,而 v1 无法读取新结构,那么切换 Service 并不能完成真正的回滚。

安全的数据库演进通常要求先采用向后兼容的扩展,再部署能够读写新旧结构的应用,最后在确认旧版本不再需要后清理旧结构。发布策略只能控制应用流量,不能撤销已经提交的业务数据或数据库 DDL。


六、Rolling、Canary 和 Blue-Green 的组合关系

三种策略可以组合,而不是只能三选一。

例如:

Blue-Green 准备 Green 环境
    -> Green 内部使用 Rolling 更新
    -> 通过内部入口验证 Green
    -> 外部入口先给 Green 5% 流量
    -> 逐步提高到 25%、50%、100%

这个流程同时使用了:

  • Rolling:构建或更新 Green 环境中的 Pod;
  • Canary:逐步暴露 Green;
  • Blue-Green:保留 Blue 作为快速回切目标;
  • 流量管理:控制外部请求到两个环境的比例;
  • 回滚:降低 Green 权重,或把入口切回 Blue。

选择策略时,关键不是名称,而是系统需要哪种控制粒度:

需求 更匹配的方案 主要代价
逐步替换 Pod,减少单次资源峰值 Rolling 新旧版本会混合接流量
按请求、用户或租户控制暴露 流量型 Canary 需要入口或服务网格能力
保留完整旧环境并快速切换 Blue-Green 资源成本高,切换有传播延迟
只希望 Kubernetes 原生能力 Deployment Rolling 不能原生完成精细流量权重

七、Deployment 回滚:恢复 Pod 模板,而不是撤销世界

1. 使用 kubectl rollout undo

查看历史:

kubectl rollout history deployment/web

回滚到上一版本:

kubectl rollout undo deployment/web

回滚到指定 revision:

kubectl rollout undo deployment/web --to-revision=2

等待回滚完成:

kubectl rollout status deployment/web --timeout=10m

检查实际 Pod 镜像:

kubectl get pods -l app=web \
  -o jsonpath='{range .items[*]}{.metadata.name}{"  "}{.spec.containers[0].image}{"\n"}{end}'

rollout undo 的本质不是把控制器恢复到某个不可变快照,而是把旧 ReplicaSet 中的 Pod 模板重新写回 Deployment,使 Deployment 再次触发一次滚动更新。于是回滚本身也会产生新的状态变化,通常可以把它理解为:

v1 -> v2 -> 回到 v1 的模板 -> 新的一次 rollout

如果旧版本镜像已经从仓库删除、旧 Secret 不存在、旧 ConfigMap 已被修改,或者数据库已经发生不兼容变化,Deployment 回滚可能只恢复了镜像字段,却无法恢复应用的完整运行环境。

2. 回滚不等于删除当前 Pod

直接执行:

kubectl delete pod ...

只会删除某个 Pod。ReplicaSet 会按照自己的期望副本数重新创建 Pod,Deployment 的版本仍然是当前版本。删除 Pod 可用于重新触发启动,但不是版本回滚。

同理,修改 Service Selector 可以回切 Blue-Green 流量,但不会修改 Blue 或 Green Deployment 的镜像。它是流量回滚,不是工作负载回滚。

3. 回滚失败的典型路径

假设 v2 的新 Pod 因探针失败无法 Ready:

Deployment v2
    -> 新 ReplicaSet 创建 Pod
    -> Pod 启动
    -> readinessProbe 失败
    -> 新 Pod 不进入 Service EndpointSlice
    -> 旧 ReplicaSet 保持部分或全部副本
    -> rollout status 超时或出现 ProgressDeadlineExceeded

此时应先确认失败点:

kubectl describe deployment web
kubectl get rs -l app=web
kubectl get pods -l app=web
kubectl describe pod <pod-name>
kubectl logs <pod-name> -c web --previous
kubectl get events --sort-by=.lastTimestamp

检查结果时要区分:

  • ImagePullBackOff:镜像名称、凭据或仓库可达性问题;
  • CrashLoopBackOff:进程启动后反复退出;
  • Unready:应用运行但探针失败;
  • Pending:资源不足、节点选择、污点或 PVC 调度问题;
  • Pod Ready 但业务失败:探针覆盖范围不足,需看指标和业务日志。

确认目标版本、配置和依赖仍然存在后,再执行 rollout undo。如果发布由 GitOps 管理,优先提交回滚后的声明配置,而不是只在集群中手工执行回滚。


八、流量、状态和故障传播

1. 从请求到 Pod 的路径

以普通 Service 为例,请求路径可以抽象为:

客户端
  -> 外部负载均衡器(如果有)
  -> Ingress/Gateway(如果有)
  -> Service 虚拟 IP 或 DNS
  -> 节点数据面规则
  -> EndpointSlice 中的可用端点
  -> Pod IP
  -> 容器端口

Service 的后端来源是 EndpointSlice。Pod 被创建后,只有满足 Service 选择器并且处于可接收流量的状态,才会被加入可用端点。不同 Kubernetes 发行版可能使用 iptables、IPVS、eBPF 或云厂商数据面实现这些转发,具体调度细节不是 Kubernetes API 的稳定保证。

因此诊断“新版本没有流量”时,不能只看 Deployment:

kubectl get pods -l app=web --show-labels
kubectl get service web -o yaml
kubectl get endpointslice \
  -l kubernetes.io/service-name=web -o yaml

如果 Pod Ready,但 EndpointSlice 中没有它,重点检查标签、Service Selector 和就绪状态。如果 EndpointSlice 正确,但外部仍无流量,问题可能在 Ingress、Gateway、云负载均衡器缓存或客户端长连接。

2. 版本标签必须由各自控制器管理

Service 选择器和 Deployment 的标签设计不严谨,会导致错误版本接流量。例如下面的选择器过于宽泛:

selector:
  app: web

如果另一个临时测试 Deployment 也使用 app: web,它会被自动加入生产 Service。版本发布通常应使用稳定且有明确边界的标签组合,例如:

app: web
track: stable

或者:

app: web
version: v2

但要注意,Deployment 的 .spec.selectorapps/v1 中不可变。选择器一旦设计错误,后续不能简单修改,需要谨慎迁移工作负载。


九、GitOps 场景下的发布与回滚

GitOps 的核心是:仓库中的声明配置代表期望状态,控制器持续把集群实际状态调谐到该状态。

因此存在两类回滚:

1. 集群内临时回滚

kubectl rollout undo deployment/web

它立即改变集群中的 Deployment,但如果 Git 仓库仍然声明 v2,GitOps 控制器可能再次把它改回 v2:

手工 rollback 到 v1
    -> 集群暂时为 v1
    -> GitOps 发现仓库期望为 v2
    -> 调谐回 v2

这不是 Kubernetes 回滚失效,而是两个控制循环的期望状态不同。

2. Git 提交回滚

把仓库中的镜像、流量权重或环境选择器恢复到上一版本,再由 GitOps 控制器调谐:

Git revert
    -> GitOps 读取新的期望状态
    -> 更新 Deployment/Service/Ingress/Gateway
    -> Kubernetes 控制器执行滚动更新或端点收敛

对于 Secret 也要特别谨慎。Secret 的名字不变并不代表内容历史可恢复;如果旧版本依赖的密钥已被轮换或删除,回滚镜像仍可能无法启动。版本化 Secret、外部密钥系统和应用兼容策略需要单独设计。


十、一个可执行的 Rolling 发布流程

下面的过程要求已经存在 web Deployment 和 web Service。

更新镜像:

kubectl set image deployment/web \
  web=example/web:v2

查看 Deployment 观察到的 Pod 模板:

kubectl get deployment web \
  -o jsonpath='{.spec.template.spec.containers[0].image}{"\n"}'

等待更新:

kubectl rollout status deployment/web --timeout=10m

观察 ReplicaSet 和 Pod:

kubectl get rs,pods -l app=web

确认 Service 的端点:

kubectl get endpointslice \
  -l kubernetes.io/service-name=web

如果成功,预期是:

  • Deployment 的新 ReplicaSet 达到期望副本数;
  • 新 Pod 全部 Ready;
  • 旧 ReplicaSet 副本数最终降为 0,除非历史保留策略保留其对象;
  • Service 的 EndpointSlice 只包含当前 Ready 的后端;
  • 应用指标没有达到失败阈值。

如果失败,不应通过反复删除 Pod“催促”发布完成。应先查看事件和容器日志,判断是调度、镜像、配置、依赖还是探针问题,然后根据发布目标选择修复、暂停或回滚。


十一、常见误解与生产边界

误解一:maxSurge: 10% 就代表 10% 流量进入新版本

错误。maxSurge 只限制滚动更新期间额外 Pod 的数量,不定义请求路由权重。新旧 Pod 同时处于 Service 后端时,流量比例还受连接复用和数据面实现影响。

误解二:Pod Ready 就说明可以安全放量

错误。Ready 只决定是否进入可用端点,不能替代业务验证。至少应验证真实请求路径、依赖连接、关键业务指标和错误日志。

误解三:Deployment 报 ProgressDeadlineExceeded 会自动回滚

错误。Deployment 会报告进度失败,但不会根据该条件自动恢复旧版本。自动回滚需要外部发布系统或专门控制器执行。

误解四:Blue-Green 切换可以瞬时撤销所有旧请求

错误。Service、EndpointSlice、节点数据面、外部负载均衡器和长连接都可能有传播或存活时间。回切后仍可能有少量请求落到旧的连接路径上。

误解五:应用回滚就等于数据回滚

错误。Deployment 主要控制 Pod 模板;它不会撤销数据库写入、消息发送、对象存储变更或外部 API 副作用。需要把应用版本回滚和数据补偿、数据库兼容、消息处理设计分开。

发布策略最终应由故障影响范围、流量控制粒度、资源预算、状态兼容性和回滚速度共同决定。Rolling 是 Kubernetes 原生 Deployment 的基础机制;Canary 依赖额外的暴露控制和指标决策;Blue-Green 依赖双环境资源与入口切换;真正可靠的回滚则必须同时恢复工作负载声明、流量配置以及仍然兼容的运行依赖。


系列导航与关联阅读

官方资料

本文依据 Kubernetes、CNCF 与相关项目官方文档重新梳理;正文和生产清单由 WR BLOG 编写。