返回首页

2.2 Deployment 与 ReplicaSet:无状态应用管理

如果说 Pod 解决的是“怎么运行一个实例”,那么 Deployment 解决的就是“怎么稳定地运行一组实例,并且安全地升级它们”。在真实业务里,我们几乎不会手动维护单个 Pod,而是让 Deployment 负责声明目标状态,再由它背后的 ReplicaSet 去确保 Pod 副本数量始终符合预期。::cite[386]::cite[387]

这一章的重点,是把 Deployment 和 ReplicaSet 的职责边界讲清楚:为什么要有这两个对象、滚动更新是如何发生的、什么时候能回滚、什么时候该暂停发布,以及做版本发布时有哪些容易踩坑的地方。::cite[386]::cite[387]

2.2.1 为什么需要 ReplicaSet 与 Deployment

先看 ReplicaSet:保证“应该有几个 Pod”

ReplicaSet 的核心职责很单纯:维持指定数量的 Pod 副本持续存在。如果有 Pod 挂掉了,它会补;如果你把副本数调大,它会创建;如果调小,它会删除多余的实例。::cite[387]

这解决了“副本数量稳定性”的问题,但 ReplicaSet 本身并不擅长发布管理。官方文档也明确指出:ReplicaSet 不直接支持滚动更新。::cite[387]

再看 Deployment:保证“怎么升级这组 Pod”

Deployment 在 ReplicaSet 之上再封装了一层,提供的是 声明式更新能力。你只需要说清楚“我希望应用最终变成什么样子”,Deployment 控制器就会按受控节奏,创建新的 ReplicaSet、缩容旧的 ReplicaSet,并把整个过程编排起来。::cite[386]

可以这样理解它们的关系:

对象 主要职责
Pod 运行单个实例
ReplicaSet 保证副本数
Deployment 管理版本发布、滚动更新、回滚、暂停与恢复

为什么业务里通常直接写 Deployment?

因为绝大多数无状态应用都需要这几件事:

  • 副本数控制
  • 发布升级
  • 失败回滚
  • 观察发布状态
  • 清理旧版本历史

这些都是 Deployment 的长项,所以官方也建议:通常直接管理 Deployment,而不要手动管理由 Deployment 创建出来的 ReplicaSet。::cite[386]

2.2.2 Deployment 的核心工作机制

Deployment 的本质是:

  1. 你提交一个目标状态。
  2. Deployment 控制器比较“当前状态”和“目标状态”。
  3. 它创建一个新的 ReplicaSet。
  4. 再按照发布策略,逐步扩新、逐步缩旧。::cite[386]

一个最小可用的 Deployment

先从最基础的 YAML 开始:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: web-deployment
  labels:
    app: web
spec:
  replicas: 3
  selector:
    matchLabels:
      app: web
  template:
    metadata:
      labels:
        app: web
    spec:
      containers:
        - name: nginx
          image: nginx:1.27
          ports:
            - containerPort: 80

这个配置表达了三个意思:

  • 想要 3 个副本。
  • 由标签 app: web 选中并管理这些 Pod。
  • Pod 模板里跑的是 nginx:1.27。::cite[386]

实操步骤

  1. 创建 Deployment。
kubectl apply -f web-deployment.yaml
  1. 查看 Deployment。
kubectl get deployments
  1. 查看 ReplicaSet。
kubectl get rs
  1. 查看 Pod。
kubectl get pods --show-labels

观察重点:Deployment 会自动创建一个 ReplicaSet,再由 ReplicaSet 创建并维持 Pod。::cite[386]

2.2.3 Deployment 的副本控制与滚动更新

副本控制:replicas

最直观的能力就是副本数控制。你可以通过修改 spec.replicas 来扩缩容。Deployment 不会直接一个个管理 Pod,而是调整底层 ReplicaSet 的期望副本数。::cite[386]

例如,把副本改成 5:

kubectl scale deployment/web-deployment --replicas=5

什么操作会触发滚动更新?

只有 Pod 模板发生变化时,Deployment 才会触发新的 rollout。

也就是说,像下面这些修改会触发发布:

  • 修改镜像版本
  • 修改容器命令
  • 修改环境变量
  • 修改资源限制
  • 修改挂载卷

但单纯修改 replicas 不会产生新的版本修订。::cite[386]

滚动更新是怎么做的?

Deployment 默认的发布策略是 RollingUpdate。它会在“新旧版本并存”的情况下逐步替换:

  • 一边拉起新 Pod
  • 一边下掉旧 Pod
  • 始终尽量保证服务可用性::cite[386]

官方文档给出的默认行为是:

  • maxUnavailable: 25%
  • maxSurge: 25%

这意味着:

  • 更新期间最多允许 25% 的副本临时不可用
  • 更新期间最多允许额外多创建 25% 的副本::cite[386]

一个更完整的滚动更新示例

apiVersion: apps/v1
kind: Deployment
metadata:
  name: web-deployment
  labels:
    app: web
spec:
  replicas: 4
  revisionHistoryLimit: 10
  progressDeadlineSeconds: 600
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 1
  selector:
    matchLabels:
      app: web
  template:
    metadata:
      labels:
        app: web
    spec:
      containers:
        - name: nginx
          image: nginx:1.27
          ports:
            - containerPort: 80
          readinessProbe:
            httpGet:
              path: /
              port: 80
            initialDelaySeconds: 3
            periodSeconds: 5
          livenessProbe:
            httpGet:
              path: /
              port: 80
            initialDelaySeconds: 10
            periodSeconds: 10
          resources:
            requests:
              cpu: 100m
              memory: 128Mi
            limits:
              cpu: 500m
              memory: 256Mi

更新镜像版本

kubectl set image deployment/web-deployment nginx=nginx:1.28

查看发布状态:

kubectl rollout status deployment/web-deployment

查看新旧 ReplicaSet 变化:

kubectl get rs

观察重点:Deployment 会新建一个 ReplicaSet,并逐步把副本从旧 ReplicaSet 挪到新 ReplicaSet。::cite[386]

2.2.4 回滚、暂停与恢复发布

线上发布一定会出错,所以 Deployment 的价值不只是“能发布”,更是“出错时能不能有序恢复”。::cite[386]

1)查看发布历史

kubectl rollout history deployment/web-deployment

如果想看某个 revision 的具体内容:

kubectl rollout history deployment/web-deployment --revision=2

Deployment 会为每次模板变更创建 revision。只要是 Pod 模板变化,都会形成可回滚的修订版本。::cite[386]

2)回滚到上一个版本

kubectl rollout undo deployment/web-deployment

3)回滚到指定版本

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

这在镜像写错、配置错误、资源设置不合理时非常好用。官方示例就展示了因为镜像名拼错导致发布卡住,再通过回滚恢复服务的过程。::cite[386]

4)暂停发布

当你准备连续改多个字段时,不希望每改一次都触发一次 rollout,可以先暂停:

kubectl rollout pause deployment/web-deployment

然后你可以继续做多次变更,例如:

kubectl set image deployment/web-deployment nginx=nginx:1.28
kubectl set resources deployment/web-deployment -c=nginx --limits=cpu=300m,memory=512Mi

5)恢复发布

kubectl rollout resume deployment/web-deployment

恢复后,之前积累的变更会一起进入新的 rollout。::cite[386]

一个重要注意点

暂停中的 Deployment 不能回滚,必须先恢复。::cite[386]

2.2.5 版本发布策略与常见注意事项

发布策略 1:RollingUpdate(默认、最常用)

这是最适合大多数无状态应用的方式。优点是:

  • 发布过程平滑
  • 能尽量保持服务可用
  • 适合持续交付::cite[386]

发布策略 2:Recreate(简单粗暴)

如果业务允许短暂中断,也可以用 Recreate

strategy:
  type: Recreate

这种方式会先停旧 Pod,再起新 Pod。适用于对版本并存敏感、但能接受瞬时不可用的场景。

金丝雀 / 蓝绿 发布如何做?

严格来说,Deployment 本身没有一个 type: Canarytype: BlueGreen 的原生字段。常见做法是:

  • 金丝雀发布:创建两个 Deployment,分别承载稳定版本与少量新版本流量。
  • 蓝绿发布:准备两套完整环境,通过 Service 或 Ingress 切流。::cite[386]

所以你可以说:Deployment 是这些高级发布模式的基础构件,但不是全部自动化方案本身。

2.2.6 常见踩坑点

1)selector 与模板标签不一致

Deployment 的 spec.selector.matchLabels 必须能匹配 spec.template.metadata.labels。不一致时,控制器就无法正确识别自己该管哪些 Pod。::cite[386]

2)不要与其他控制器使用重叠选择器

如果两个控制器都能选中同一批 Pod,行为会变得不可预测。官方明确提醒不要让不同 Deployment 或 StatefulSet 的选择器重叠。::cite[386]

3)apps/v1 下 selector 不可变

apps/v1 中,Deployment 的 selector 创建后不可修改。也就是说,标签规划最好在一开始就想清楚。::cite[386]

4)Readiness Probe 缺失会让发布看起来“成功”,但流量体验可能很差

Deployment 的滚动更新是否平稳,和 Pod 是否真正 Ready 密切相关。如果没有合理的 readiness 检查,新 Pod 虽然进程起来了,但业务可能还没准备好接请求。

5)发布时资源峰值会暂时升高

因为 maxSurge 允许额外起 Pod,所以 rollout 期间实际 Pod 总数可能超过目标副本数;再叠加老 Pod 的优雅终止时间,短时资源占用往往比平时高。::cite[386]

6)不要手动改 Deployment 管理下的 ReplicaSet

手动缩放或编辑由 Deployment 托管的 ReplicaSet,很容易让控制器状态变复杂。一般情况下,直接改 Deployment 即可。::cite[386]

2.2.7 一套推荐的无状态发布模板

下面是一份更贴近生产习惯的模板:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: api-deployment
  labels:
    app: api
spec:
  replicas: 3
  revisionHistoryLimit: 5
  progressDeadlineSeconds: 600
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 0
  selector:
    matchLabels:
      app: api
  template:
    metadata:
      labels:
        app: api
    spec:
      containers:
        - name: api
          image: nginx:1.27
          ports:
            - containerPort: 80
          readinessProbe:
            httpGet:
              path: /
              port: 80
            initialDelaySeconds: 3
            periodSeconds: 5
            failureThreshold: 3
          livenessProbe:
            httpGet:
              path: /
              port: 80
            initialDelaySeconds: 10
            periodSeconds: 10
            failureThreshold: 3
          resources:
            requests:
              cpu: 100m
              memory: 128Mi
            limits:
              cpu: 500m
              memory: 512Mi

这个模板的思路是:

  • maxUnavailable: 0:尽量避免更新时掉副本。
  • maxSurge: 1:控制额外资源开销。
  • 配好 readiness / liveness:让 Deployment 知道“什么时候真的可以接流量”。
  • 控制 revisionHistoryLimit:保留回滚历史,但不要无限增长。

2.2.8 本章小结

这一章你要真正记住的是:

  1. ReplicaSet 负责保副本,Deployment 负责做发布。
  2. 改 Pod 模板才会触发 rollout,改单纯副本数不会。
  3. 滚动更新本质上是新旧 ReplicaSet 的受控切换。
  4. 回滚、暂停、恢复,是 Deployment 在生产环境里最有价值的能力。
  5. 发布是否平稳,不只看 Deployment,还要看探针、标签、资源与容量。

当你日常管理无状态应用时,推荐默认思路是:

  • 用 Deployment 管理服务
  • 用 Service 提供稳定访问入口
  • 用 Probe 保证流量只进到健康实例
  • kubectl rollout 系列命令管理发布生命周期

📝 版权声明:本文为原创技术博客,转载请注明出处。

如文章中存在错误或不准确之处,欢迎在评论区指正,感谢您的阅读与支持!

上一篇

9.4 节点异常处理

下一篇

9.2 常见 Pod 异常排查