Kubernetes 基础体系 · 第 16/83 篇。示例基于 Kubernetes 当前稳定 API;弃用、版本偏差、云厂商差异和生产风险会明确说明。
Deployment 与 ReplicaSet:滚动更新、Revision、暂停、回滚和失败
Deployment 是 Kubernetes 中用于声明和管理无状态应用发布状态的工作负载资源。它通常不直接创建 Pod,而是创建并管理一个或多个 ReplicaSet,再由 ReplicaSet 创建 Pod。
这层间接关系是 Deployment 能够实现滚动更新、保留历史版本、暂停发布和回滚的基础:
flowchart TD
D[Deployment<br/>期望副本数、发布策略、Pod 模板] --> RS1[旧 ReplicaSet<br/>revision 1]
D --> RS2[新 ReplicaSet<br/>revision 2]
RS1 --> P1[旧版本 Pod]
RS2 --> P2[新版本 Pod]
S[Service] --> P1
S --> P2
Deployment 决定“应该处于什么发布状态”,ReplicaSet 负责“维持某个 Pod 模板对应的副本数”,Pod 则是最终运行单元。Deployment Controller 会持续比较期望状态和实际状态,并通过创建、扩容、缩容 ReplicaSet 使两者趋于一致。
本文使用 apps/v1 API,这是当前稳定的 Deployment 和 ReplicaSet API。示例中的命令需要一个可用的 Kubernetes 集群和已配置好的 kubectl。
一、ReplicaSet:维持一组相同 Pod 的控制器
1.1 ReplicaSet 的核心职责
ReplicaSet 的目标可以形式化为:
其中“实际 Pod”不是集群中所有 Pod,而是满足 ReplicaSet .spec.selector 的 Pod。
一个最小的 ReplicaSet 示例:
apiVersion: apps/v1
kind: ReplicaSet
metadata:
name: web-rs
spec:
replicas: 3
selector:
matchLabels:
app: web
version: v1
template:
metadata:
labels:
app: web
version: v1
spec:
containers:
- name: web
image: nginx:1.27
ports:
- containerPort: 80
应用:
kubectl apply -f rs.yaml
kubectl get rs
kubectl get pods -l app=web,version=v1
预期可以看到:
NAME DESIRED CURRENT READY AGE
web-rs 3 3 3 ...
这里存在三个重要关系:
spec.replicas: 3表示 ReplicaSet 希望维持三个匹配的 Pod。spec.selector.matchLabels定义 ReplicaSet 管理哪些 Pod。spec.template.metadata.labels必须匹配 selector,否则apps/v1API 会拒绝该对象。
ReplicaSet 通过标签选择 Pod。若匹配的 Pod 数量少于期望值,它会创建 Pod;若多于期望值,它会删除多余 Pod。Pod 被删除后,ReplicaSet 只会重新创建缺少的数量,不会恢复被删除 Pod 的 UID 或运行时状态。
1.2 ReplicaSet 与 Pod 的归属
ReplicaSet 创建 Pod 时,会在 Pod 上设置 ownerReferences,指向 ReplicaSet。这个引用使得 Kubernetes 能够识别控制关系,并在 ReplicaSet 被删除时按照级联删除策略处理其 Pod。
ReplicaSet 也可能接管符合 selector、但没有其他控制器归属的孤儿 Pod。因此,多个 ReplicaSet 不应使用重叠的 selector,否则可能出现相互争抢 Pod 的异常行为。
在 apps/v1 中,ReplicaSet 的 selector 是不可变字段。修改 Pod 标签通常不能通过修改已有 ReplicaSet 的 selector 实现版本切换;正确做法是创建新的 ReplicaSet,或者使用 Deployment 管理发布。
1.3 为什么通常不直接使用 ReplicaSet 发布
ReplicaSet 能够保证某一版本的 Pod 数量,但它不负责以下发布语义:
- 逐步增加新版本并减少旧版本;
- 保存多个发布历史;
- 根据历史版本回滚;
- 监控发布进度;
- 暂停和继续发布;
- 在多个 ReplicaSet 之间协调副本数。
这些功能由 Deployment Controller 实现。因此,直接使用 ReplicaSet 适合底层控制或特殊场景,而普通无状态应用通常应直接使用 Deployment。
二、Deployment:管理 ReplicaSet 的发布控制器
2.1 Deployment 的对象结构
一个 Deployment 至少包含:
spec.replicas:期望运行的 Pod 副本数;spec.selector:Deployment 管理的 Pod 选择器;spec.template:新 Pod 的模板;spec.strategy:发布策略;spec.minReadySeconds:Pod 成为可用副本前,必须连续 Ready 的时间;spec.revisionHistoryLimit:保留多少旧 ReplicaSet;spec.progressDeadlineSeconds:多长时间没有观察到发布进展后报告失败。
示例:
apiVersion: apps/v1
kind: Deployment
metadata:
name: web
labels:
app: web
spec:
replicas: 4
revisionHistoryLimit: 5
minReadySeconds: 5
progressDeadlineSeconds: 120
strategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 1
maxSurge: 1
selector:
matchLabels:
app: web
template:
metadata:
labels:
app: web
spec:
containers:
- name: web
image: nginx:1.27
ports:
- containerPort: 80
readinessProbe:
httpGet:
path: /
port: 80
initialDelaySeconds: 2
periodSeconds: 5
应用并检查:
kubectl apply -f deployment.yaml
kubectl get deployment web
kubectl get rs -l app=web
kubectl get pods -l app=web
Deployment 会创建一个 ReplicaSet。该 ReplicaSet 的 Pod 通常会带有由 Deployment 生成的 pod-template-hash 标签,例如:
app=web,pod-template-hash=7d8f6c9b7
pod-template-hash 用于区分不同 Pod 模板生成的 ReplicaSet。它是控制器生成的实现细节,不应手工设置,也不应在应用逻辑中依赖其具体值。
2.2 哪些修改会触发新的 ReplicaSet
Deployment 的核心判断对象是 Pod 模板,也就是 .spec.template。
例如修改镜像:
kubectl set image deployment/web web=nginx:1.28
或者修改环境变量、端口、探针、资源限制、卷挂载等 Pod 模板字段,都会使模板发生变化。Deployment Controller 会:
- 计算新的 Pod 模板;
- 创建一个新的 ReplicaSet;
- 为新的 ReplicaSet 设置期望副本数;
- 逐步缩减旧 ReplicaSet;
- 等待新 Pod 达到可用状态;
- 最终让旧 ReplicaSet 的副本数降为零。
相反,单纯修改 Deployment 的副本数通常不会创建新的 Pod 模板,因此不会产生新的发布版本。例如:
kubectl scale deployment/web --replicas=6
这会调整当前 ReplicaSet 或多个现有 ReplicaSet 的副本数,但不是一次新的模板发布。不要把“Deployment 对象发生了修改”和“发生了新的 Deployment revision”混为一谈。
Deployment 的 selector 在创建后不可变。若需要改变应用选择范围,通常应创建新的 Deployment,而不是试图修改已有 Deployment 的 selector。
三、滚动更新:两个 ReplicaSet 之间如何切换
3.1 RollingUpdate 的目标
RollingUpdate 是 Deployment 的默认策略。它不要求先删除所有旧 Pod,而是允许新旧版本在一段时间内同时存在。
滚动更新要同时满足两个约束:
- 新旧 Pod 的总数不能超过允许的上限;
- 可用 Pod 数不能低于允许的下限。
假设:
N是 Deployment 的期望副本数;S是maxSurge;U是maxUnavailable;A是当前可用 Pod 数量。
滚动更新期间,近似需要满足:
以及:
其中 maxSurge 控制发布时可以额外创建多少 Pod,maxUnavailable 控制相对于期望副本数最多可以少多少个可用 Pod。
这两个参数可以使用整数或百分比。百分比计算时:
maxSurge向上取整;maxUnavailable向下取整。
3.2 完整算例
假设:
replicas: 10
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 25%
maxUnavailable: 25%
计算结果为:
因此:
- 新旧 Pod 总数最多为
13; - 可用 Pod 数量通常不应低于
8。
一个可能的过程是:
| 阶段 | 旧 ReplicaSet | 新 ReplicaSet | 总 Pod | 可用 Pod |
|---|---|---|---|---|
| 初始 | 10 | 0 | 10 | 10 |
| 创建新 Pod | 10 | 3 | 13 | 10 |
| 新 Pod Ready | 7 | 3 | 10 | 10 |
| 再创建新 Pod | 7 | 6 | 13 | 10 |
| 新 Pod Ready | 4 | 6 | 10 | 10 |
| 最终完成 | 0 | 10 | 10 | 10 |
实际过程不一定严格按该表推进,因为控制器是异步工作的,Pod 调度、镜像拉取、探针检查和终止过程都可能改变中间状态。
maxSurge: 1、maxUnavailable: 0 常用于要求发布期间保持全部可用副本的场景:
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
对于四个副本,这通常意味着先创建一个新 Pod,等它满足可用条件后,再删除一个旧 Pod。代价是发布速度较慢,并且集群需要额外容纳至少一个 Pod。
maxSurge: 0、maxUnavailable: 1 则倾向于先删除旧 Pod,再创建新 Pod,从而避免额外容量,但会降低发布期间的可用副本数。maxSurge 与 maxUnavailable 不能同时为零,否则没有任何合法的替换动作。
3.3 “Ready”不等于“可用”
Deployment 的滚动更新不能只看 Pod 是否已经创建。Pod 需要通过 readiness probe,且满足 minReadySeconds,才会计入 availableReplicas。
例如:
minReadySeconds: 30
表示 Pod 必须连续处于 Ready 状态至少 30 秒,才被 Deployment 视为可用。若 Pod 短暂 Ready 后又变为 NotReady,连续计时会失效。
这会影响两个地方:
- 控制器何时可以继续缩减旧 ReplicaSet;
- Deployment 是否能够在
progressDeadlineSeconds内完成发布。
因此,一个容器“进程已经启动”并不代表滚动更新可以继续。启动成功但 readiness probe 始终失败的 Pod,会使新 ReplicaSet存在,却不产生足够的可用副本。
3.4 Recreate 与 RollingUpdate 的区别
Deployment 还支持:
strategy:
type: Recreate
Recreate 会先删除旧版本 Pod,再创建新版本 Pod。它不提供滚动期间的新旧版本并存,也不保证业务无中断。
如果应用不能同时运行两个版本,例如多个版本会争用不兼容的独占资源,Recreate 可能比 RollingUpdate 更合适。但这是一种明确的停机取舍,而不是滚动发布。
四、Revision:Deployment 的发布历史编号
4.1 Revision 是什么
Deployment revision 是 Deployment Controller 为 Pod 模板发布生成的历史编号。相关信息通常可以在 ReplicaSet 注解中看到:
kubectl get rs -l app=web \
-o custom-columns=NAME:.metadata.name,REVISION:.metadata.annotations.deployment\\.kubernetes\\.io/revision,DESIRED:.spec.replicas
输出可能类似:
NAME REVISION DESIRED
web-7d8f6c9b7 1 0
web-6f9d8c7b4 2 4
通常,每次 Pod 模板发生发布性变化,Deployment 会创建新的 ReplicaSet,并分配新的 revision。旧 ReplicaSet 被缩容到零,但仍可能保留,用于查看历史和回滚。
revision 不是 Pod 版本字符串,也不是镜像标签。它是 Deployment 控制器维护的发布历史序号。revision: 2 本身并不能说明运行的是哪个镜像,必须查看对应 ReplicaSet 的 Pod 模板。
kubectl rollout history deployment/web
kubectl rollout history deployment/web --revision=2
为了让历史更容易理解,可以在修改模板前记录变更原因:
kubectl annotate deployment/web \
kubernetes.io/change-cause="upgrade nginx from 1.27 to 1.28" \
--overwrite
kubectl set image deployment/web web=nginx:1.28
kubernetes.io/change-cause 是用于展示变更原因的注解,不是 Kubernetes Controller 用于决定发布内容的字段。没有该注解时,历史中的 CHANGE-CAUSE 可能为空。不要手工修改 deployment.kubernetes.io/revision,该注解由控制器管理。
4.2 Revision 与 ReplicaSet 的关系
每个由 Deployment 管理的 Pod 模板通常对应一个 ReplicaSet。Deployment 会根据模板哈希识别哪个 ReplicaSet是当前模板、哪个是旧模板。
发布过程中的状态可以简化为:
Deployment revision 1
└── ReplicaSet A: 4 个旧 Pod
修改 spec.template
Deployment revision 2
├── ReplicaSet A: 逐步缩容
└── ReplicaSet B: 逐步扩容
当 revision 2 完成后,ReplicaSet A 通常保留但副本数为零,ReplicaSet B 持有全部副本。
需要注意的是,revision 序号通常不会因为旧历史被删除而重新使用。历史清理后,仍然存在的 revision 号可能不连续。
4.3 revisionHistoryLimit 的边界
默认情况下,Deployment 会保留有限数量的旧 ReplicaSet。可以显式设置:
spec:
revisionHistoryLimit: 5
该字段控制旧 ReplicaSet 的保留数量,不包括当前正在使用的 ReplicaSet。设置得太小会减少可回滚的历史;设置得太大则会保留更多 ReplicaSet 对象和模板元数据。
回滚依赖旧 ReplicaSet 中保存的 Pod 模板。如果对应 ReplicaSet 已被清理,就不能再通过 Deployment 的历史机制恢复该版本。因此,revision 历史不是无限可靠的制品仓库,镜像本身仍应使用可追溯、不会被重新指向其他内容的版本标识,例如固定版本或 digest。
五、暂停与继续:停止控制器推进发布
5.1 暂停的语义
可以暂停 Deployment:
kubectl rollout pause deployment/web
也可以直接设置:
kubectl patch deployment web \
--type=merge \
-p '{"spec":{"paused":true}}'
暂停后,Deployment Controller 不再推进新的滚动更新。它不会继续按原计划扩容新 ReplicaSet或缩容旧 ReplicaSet。
检查状态:
kubectl get deployment web -o jsonpath='{.spec.paused}{"\n"}'
输出:
true
暂停常用于将多个配置修改合并为一次发布。例如:
kubectl rollout pause deployment/web
kubectl set image deployment/web web=nginx:1.28
kubectl set resources deployment/web \
--limits=cpu=500m,memory=256Mi \
--requests=cpu=100m,memory=128Mi
kubectl rollout resume deployment/web
暂停期间的修改不会立即推进发布。恢复后,控制器根据恢复时的最终 Pod 模板进行协调。这样可以避免连续修改镜像、环境变量和资源配置时产生多次中间发布。
5.2 暂停不是回滚,也不是冻结 Pod
暂停不会:
- 删除已经创建的 Pod;
- 将新版本自动恢复成旧版本;
- 阻止 Pod 因节点故障、探针失败或其他原因被替换;
- 阻止 ReplicaSet 维持其自身的副本数;
- 阻止用户直接修改其他 Kubernetes 对象。
它暂停的是 Deployment 发布协调过程,而不是整个集群或所有下层控制器。
如果 Deployment 正处于发布中间阶段,暂停后可能同时保留新旧两个 ReplicaSet。恢复时,控制器会从当前状态继续收敛。
kubectl rollout resume deployment/web
kubectl rollout status deployment/web --timeout=5m
暂停时不应把 ProgressDeadlineExceeded 简单解释为“暂停导致发布失败”。发布进度计时与暂停状态存在特殊处理,诊断时应同时检查 .spec.paused、Deployment conditions 和 Pod 状态。
六、回滚:恢复 Pod 模板,而不是倒转整个系统
6.1 查看和执行回滚
查看历史:
kubectl rollout history deployment/web
回滚到上一个版本:
kubectl rollout undo deployment/web
回滚到指定 revision:
kubectl rollout undo deployment/web --to-revision=1
观察回滚:
kubectl rollout status deployment/web --timeout=5m
kubectl get rs -l app=web
kubectl get pods -l app=web
回滚的本质不是把 Deployment 对象的 revision 指针移动回旧编号,而是把旧 ReplicaSet 中保存的 Pod 模板重新设置为当前 Deployment 模板。这个动作会触发一次新的发布,因此可能产生新的 revision。
例如:
revision 1: nginx:1.27
revision 2: nginx:1.28
执行 undo 到 revision 1
revision 3: nginx:1.27
此时当前版本是 revision 3,但其 Pod 模板内容与 revision 1 相同。revision 3 不是 revision 1 被“重新激活”,而是一次新的模板变更。
6.2 回滚不会恢复所有 Deployment 字段
Deployment 的回滚主要针对 Pod 模板。不能假设以下内容也会自动恢复:
- Service 的 selector 和端口;
- ConfigMap 或 Secret 的当前内容;
- 数据库 schema;
- 外部系统配置;
- PersistentVolume 中已经写入的数据;
- 镜像仓库中被重新覆盖的同名 tag;
- 与 Deployment 分开管理的 HPA、PDB、Ingress 或网关规则。
例如,Pod 模板中引用了:
envFrom:
- configMapRef:
name: web-config
如果 web-config 的内容已经变更,Deployment 回滚到旧 revision 并不会把 ConfigMap 内容自动恢复。旧模板引用的仍然是当前的 ConfigMap 对象。
同样,回滚一个应用版本不会撤销数据库迁移。不可逆数据库变更是自动回滚最常见的边界之一。
6.3 回滚前后的验证
回滚前应先确认历史中的模板:
kubectl rollout history deployment/web --revision=1
如果需要查看精确字段:
kubectl get rs web-旧版本名称 -o yaml
回滚后至少验证:
kubectl get deployment web
kubectl get rs -l app=web
kubectl get pods -l app=web -o wide
kubectl describe deployment web
重点查看:
updatedReplicas是否达到期望值;availableReplicas是否达到期望值;- Pod 是否处于 Ready;
- ReplicaSet 是否报告
FailedCreate; - Deployment conditions 是否出现
ProgressDeadlineExceeded; - 新旧 Pod 的镜像、环境变量和探针是否符合预期。
七、发布失败:Deployment 如何表示失败
Deployment 的失败通常不是一个单一错误,而是多个层次的状态。
7.1 Pod 创建失败
如果 ReplicaSet 无法创建 Pod,常见原因包括:
- 资源配额不足;
- 节点没有可调度资源;
- 镜像不存在或无法拉取;
- ServiceAccount、Secret 或 ConfigMap 不存在;
- Pod 安全策略、准入策略或 webhook 拒绝请求;
- 节点选择器、亲和性或拓扑约束没有可行节点。
诊断命令:
kubectl describe deployment web
kubectl get rs -l app=web
kubectl describe rs <new-replicaset-name>
kubectl get events --sort-by=.lastTimestamp
若是 Pod 已创建但没有运行:
kubectl get pods -l app=web
kubectl describe pod <pod-name>
ReplicaSet 可能出现类似条件:
conditions:
- type: ReplicaFailure
status: "True"
reason: FailedCreate
这表示底层 ReplicaSet 创建 Pod 失败。此时 Deployment 的滚动算法本身可能没有问题,故障发生在 Pod 创建、调度或准入阶段。
7.2 Pod 创建成功但不可用
另一类常见情况是 Pod 对象存在,但无法成为可用副本:
NAME READY STATUS RESTARTS
web-xxxx-yyyy 0/1 ImagePullBackOff 0
web-xxxx-zzzz 0/1 Running 5
可能原因包括:
- 镜像拉取失败;
- 容器不断崩溃;
- readiness probe 路径、端口或协议错误;
- 应用启动时间超过预期;
- 应用启动后无法连接依赖服务;
- 容器监听地址或端口配置错误。
应分别检查:
kubectl describe pod <pod-name>
kubectl logs <pod-name> -c web
kubectl logs <pod-name> -c web --previous
--previous 用于查看上一次已经退出的容器实例日志,适合诊断 CrashLoopBackOff。
如果 readiness probe 一直失败,Pod 可能显示 Running,但不会进入 availableReplicas。Deployment 会因此无法继续完成发布。
7.3 ProgressDeadlineExceeded:发布进度超时
Deployment 可以配置:
spec:
progressDeadlineSeconds: 120
如果在指定时间内没有观察到足够的发布进展,Deployment 会在 conditions 中报告:
- type: Progressing
status: "False"
reason: ProgressDeadlineExceeded
查询:
kubectl get deployment web -o yaml
kubectl describe deployment web
“没有进展”可能表示:
- 新 ReplicaSet 没有成功创建 Pod;
- 新 Pod 没有变为 Ready;
- 新 Pod 没有满足
minReadySeconds; - 副本数一直无法达到目标;
- 调度、镜像拉取或准入过程长期阻塞。
progressDeadlineSeconds 是失败检测阈值,不是自动修复策略。Kubernetes Deployment Controller 默认不会因为该条件自动执行回滚。回滚需要由操作者、发布系统或更高层控制器明确执行。
可以先修复原因,再等待控制器继续推进。例如修正镜像:
kubectl set image deployment/web web=nginx:1.28.1
kubectl rollout status deployment/web --timeout=5m
也可以显式回滚:
kubectl rollout undo deployment/web
kubectl rollout status deployment/web --timeout=5m
如果 Deployment 处于暂停状态,先恢复:
kubectl rollout resume deployment/web
7.4 kubectl rollout status 的含义
执行:
kubectl rollout status deployment/web --timeout=5m
该命令等待 Deployment 达到完成条件。成功时会输出类似:
deployment "web" successfully rolled out
它主要观察 Deployment 的发布状态,不等于完整的业务验证。命令成功只能说明 Kubernetes 认为期望副本已经更新并可用,不能证明:
- HTTP 业务逻辑正确;
- 数据库迁移安全;
- 所有请求都成功;
- 新版本性能满足要求;
- Service、Ingress 或外部流量配置正确。
生产发布通常还需要业务探测、指标检查和错误率验证。
八、滚动更新中的并发和竞态
Deployment Controller 是异步控制器。kubectl set image、kubectl scale 或 kubectl apply 提交的是新的期望状态,而不是直接执行一串同步操作。
如果在发布尚未完成时连续修改模板:
kubectl set image deployment/web web=nginx:1.28
kubectl set env deployment/web FEATURE_X=true
kubectl set image deployment/web web=nginx:1.29
控制器可能观察到多个连续期望状态,并创建或保留多个 ReplicaSet。最终状态通常会收敛到最后一次模板,但中间 ReplicaSet 是否已经创建、是否已经产生 Pod,取决于控制器处理速度和 API Server 中各次更新的时序。
暂停可以把多个修改合并:
kubectl rollout pause deployment/web
kubectl set image deployment/web web=nginx:1.29
kubectl set env deployment/web FEATURE_X=true
kubectl rollout resume deployment/web
发布系统还应避免多个操作者或流水线同时修改同一个 Deployment。即使 Kubernetes 最终能收敛,发布历史、变更原因和回滚目标也会变得难以解释。
九、失败发布的一个完整诊断流程
假设执行:
kubectl set image deployment/web web=example.invalid/web:v2
kubectl rollout status deployment/web --timeout=2m
可能得到等待超时。可以按以下因果路径排查。
第一步:确认 Deployment 期望状态
kubectl get deployment web -o wide
kubectl describe deployment web
检查:
desired、updated、ready、available副本数;- 新 ReplicaSet 的名称;
Progressing和Availableconditions;- Events 中是否有失败信息。
第二步:确认新 ReplicaSet 是否产生
kubectl get rs -l app=web
如果新 ReplicaSet 存在但 DESIRED 为零,可能是发布已经被新的模板变化取代,或 Deployment 处于其他中间状态。应结合 Deployment 的完整 YAML 和事件判断。
如果新 ReplicaSet 的期望副本数大于零但 Pod 数量很少,继续查看:
kubectl describe rs <new-replicaset-name>
第三步:确认 Pod 卡在哪个阶段
kubectl get pods -l app=web -o wide
kubectl describe pod <new-pod-name>
例如:
Pending:优先检查调度、资源、亲和性和配额;ImagePullBackOff:检查镜像地址、凭据和仓库可达性;CrashLoopBackOff:检查容器日志和启动参数;Running但0/1 Ready:检查 readiness probe 和应用依赖。
第四步:选择修复或回滚
如果新版本只是镜像地址写错,可以修正模板:
kubectl set image deployment/web web=example.com/web:v2
kubectl rollout status deployment/web --timeout=5m
如果新版本本身不应继续推进,则执行:
kubectl rollout undo deployment/web
kubectl rollout status deployment/web --timeout=5m
回滚后仍要检查旧版本是否真的可用,因为失败可能来自共享依赖、节点故障、Secret 缺失或数据库状态,而不是新镜像本身。
十、Deployment、Service 和流量切换不是同一个机制
Deployment 负责 Pod 模板和 ReplicaSet 的发布;Service 负责通过 selector 选择后端 Pod并提供稳定的访问入口。
如果 Service selector 只选择:
selector:
app: web
那么滚动更新期间,新旧 Pod 通常都会进入 Service 的后端集合。流量会在符合 selector 且通过就绪条件的 Pod 之间分配。Deployment 的 maxSurge 和 maxUnavailable 控制的是副本和可用性,不是精确的流量百分比。
因此,下面这些策略不能仅靠普通 Deployment 自动实现:
- 让 5% 流量进入新版本;
- 按用户、地域或请求头分流;
- 两套完整环境之间切换;
- 新旧版本同时存在但由网关按权重路由。
Canary 通常需要额外的 Service、Ingress、Gateway、服务网格或发布控制器;Blue-Green 通常需要两套独立工作负载和一次明确的流量入口切换。Deployment 的 RollingUpdate 是副本逐步替换,不等同于 Canary 或 Blue-Green。
十一、PodDisruptionBudget 不会替代 Deployment 的滚动策略
PodDisruptionBudget(PDB)用于限制自愿中断造成的可用性损失,例如节点排空时的 Eviction。它描述的是在某类中断期间需要保持多少 Pod 可用。
PDB 不应被当作 Deployment 滚动更新参数的替代品:
maxUnavailable是 Deployment 发布协调的一部分;- PDB 主要约束自愿中断;
- PDB 不会自动把错误版本回滚;
- PDB 不能保证应用级请求一定成功;
- PDB 不等价于“永远保持 N 个 Pod”。
工作负载控制器为了完成滚动更新而删除旧 Pod 时,不应简单假设 PDB 会像流量闸门一样精确限制删除行为。发布可用性主要由 Deployment strategy、readiness、minReadySeconds 和副本数共同决定;节点维护等自愿中断则需要单独评估 PDB。
十二、常见误解和边界
12.1 删除 Pod 不会回滚版本
kubectl delete pod <pod-name>
这只会删除一个运行实例。ReplicaSet 会重新创建符合当前模板的 Pod,Deployment 不会因为 Pod 被删除而恢复历史版本。
12.2 回滚镜像不等于回滚应用
Deployment 回滚的是 Pod 模板。它不会自动撤销数据库迁移、外部配置变更、消息消费副作用或已经写入的数据。
12.3 revision 不等于镜像版本
revision 4 可能使用 nginx:1.27,revision 5 也可能在回滚后再次使用 nginx:1.27。应查看 ReplicaSet 模板或 Pod 实际镜像,而不是仅凭 revision 数字判断版本。
12.4 发布成功不等于业务成功
Deployment 只知道 Pod 是否按 Kubernetes 的条件变为可用。错误的业务响应、性能下降、依赖服务异常和数据兼容问题仍需要应用指标、日志和端到端探测发现。
12.5 镜像 tag 可能使回滚结果不确定
如果多个发布都使用 app:latest,或者镜像仓库允许覆盖同名 tag,那么历史 ReplicaSet 保存的是相同字符串,而不是镜像构建内容。回滚后重新拉取的内容可能已经变化。
更可追溯的方式是使用不可变 tag 或 digest:
image: registry.example.com/web@sha256:<digest>
digest 必须替换为实际镜像摘要,不能使用占位字符串。
十三、一个可验证的发布实验
可以用以下命令观察完整生命周期:
kubectl create deployment web --image=nginx:1.27
kubectl scale deployment web --replicas=3
kubectl get deployment web
kubectl get rs -l app=web
kubectl get pods -l app=app # 此处通常不会匹配
kubectl create deployment web 默认会使用 app=web 标签,因此最后一条命令的 selector 写错了,不会匹配这些 Pod。这是一个有意保留的反例:查询 selector 必须与实际标签一致。
正确查询:
kubectl get pods -l app=web
记录变更原因并更新镜像:
kubectl annotate deployment/web \
kubernetes.io/change-cause="upgrade to nginx 1.28" \
--overwrite
kubectl set image deployment/web web=nginx:1.28
kubectl rollout status deployment/web --timeout=5m
观察 ReplicaSet:
kubectl get rs -l app=web
通常会看到一个旧 ReplicaSet 副本数逐步降为零,以及一个新 ReplicaSet逐步达到三个副本。
暂停一次发布:
kubectl rollout pause deployment/web
kubectl set image deployment/web web=nginx:1.29
kubectl get rs -l app=web
此时不要期待新版本立即完成。恢复:
kubectl rollout resume deployment/web
kubectl rollout status deployment/web --timeout=5m
查看历史并回滚:
kubectl rollout history deployment/web
kubectl rollout undo deployment/web
kubectl rollout status deployment/web --timeout=5m
这个实验可以验证四个关键事实:
- Deployment 通过 ReplicaSet 间接管理 Pod;
- Pod 模板变化会产生新的 ReplicaSet;
- 暂停会停止 Deployment 的发布推进;
- 回滚会恢复旧模板内容,但会形成一次新的发布过程。
Deployment 的可靠性不在于它保证每次发布都成功,而在于它把期望副本数、版本模板、替换策略、历史模板和进度状态明确建模出来。理解 Deployment,必须同时理解 ReplicaSet 如何维持副本、滚动更新如何在可用性约束下推进、revision 如何保存模板历史,以及失败时哪些状态会停止、哪些状态仍会继续自愈。
系列导航与关联阅读
- 系列入口:Kubernetes 完整学习路线:从 Pod 与控制面到安全、运维和 Operator
- 上一篇:Init、Sidecar 与 Ephemeral Container:启动顺序、共享和调试边界
- 下一篇:StatefulSet:稳定身份、顺序、PVC、更新和分布式系统边界
- 延伸:Kubernetes 发布策略:Rolling、Canary、Blue-Green、流量和回滚
- 延伸:PodDisruptionBudget 与可用性:自愿中断、Eviction、滚动和误区
官方资料
本文依据 Kubernetes、CNCF 与相关项目官方文档重新梳理;正文和生产清单由 WR BLOG 编写。

评论
0 条讨论