CFN Cloud
2025-10-04

Kubernetes Deployment 与 ReplicaSet:滚动更新和声明式发布

搞懂 Deployment 与 ReplicaSet 的关系、滚动更新流程,以及发布失败时应如何定位和回滚。

Deployment 是大多数人每天都会碰到的控制器。ReplicaSet 负责把副本数量维持住,Deployment 则负责版本变更、滚动发布和回滚历史。很多发布问题表面上像“应用起不来”,实际上根因常常藏在 Deployment 这条控制链路里。

这两个控制器分别干什么

  • ReplicaSet:维持匹配 Pod 的副本数量。
  • Deployment:创建和更新 ReplicaSet。
  • 滚动更新:让旧版本和新版本按策略交替切换。

日常操作里,你通常应该管理 Deployment,而不是直接去改 ReplicaSet。

为什么还需要 Deployment

只有 ReplicaSet,并不足以支撑日常发布。你还需要:

  • 发布历史
  • 回滚能力
  • 更新进度跟踪
  • 更安全的滚动策略

Deployment 就是在 ReplicaSet 之上补了这一层发布语义。

所以,手动删 Pod 不应该成为发版方式。真正可靠的做法,是修改期望状态,让控制器完成收敛。

最小示例

apiVersion: apps/v1
kind: Deployment
metadata:
  name: api
spec:
  replicas: 3
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 0
  selector:
    matchLabels:
      app: api
  template:
    metadata:
      labels:
        app: api
    spec:
      containers:
        - name: api
          image: nginx:1.25

最重要的一条规则:selector 和 labels 必须对上

Deployment 的 selector 必须和 Pod 模板里的 labels 对齐。如果两者漂移,控制器会接管失败,流量和发布行为都会变得很奇怪。

这也是为什么 Deployment 创建后通常不要再轻易改 selector。

一次 rollout 实际上发生了什么

当你修改 Pod 模板时,Kubernetes 并不是在原地改老的 ReplicaSet,而是:

  1. 创建一个新的 ReplicaSet
  2. 把新 ReplicaSet 扩上去
  3. 把旧 ReplicaSet 缩下来
  4. 一直跟踪状态,直到发布完成

这就是为什么你会看到 rollout history,也解释了为什么老的 ReplicaSet 还会暂时保留下来。

RollingUpdate 和 Recreate 怎么选

RollingUpdate

默认也是最常见的选择。

适合:

  • 新旧版本可以短暂共存
  • 希望发布更平滑
  • 业务不能轻易中断

Recreate

先停旧版本,再起新版本。

只适合:

  • 新旧版本不能同时存在
  • 独占锁、兼容性或启动顺序不允许并行

最值得记住的 rollout 命令

kubectl rollout status deploy/api
kubectl rollout history deploy/api
kubectl rollout undo deploy/api
kubectl rollout pause deploy/api
kubectl rollout resume deploy/api

出问题时,这些命令通常比“现场改配置碰运气”有用得多。

滚动更新参数怎么理解

两个关键字段:

  • maxSurge:发布过程中允许额外多出来多少 Pod
  • maxUnavailable:发布过程中允许少掉多少可用副本

对关键 API 来说,maxUnavailable: 0 往往是更稳的起点。

为什么探针会直接影响 Deployment

Deployment 的安全性很大程度依赖 readiness。如果 readiness 配得不对,Kubernetes 可能:

  • 把坏 Pod 当成好 Pod
  • 把好 Pod 长时间挡在流量之外
  • 让 rollout 一直卡住

很多人以为是“发布失败”,其实根因是探针设计失败。

镜像标签和版本历史

尽量用明确版本号,不要在生产里依赖 latest

版本 tag 固定下来之后,rollout history 才真正有意义。否则你连“这次到底变了什么”都很难说清楚。

requests / limits 也是发布稳定性的一部分

如果没有 requests,调度器缺少有效信号,HPA 也更难做出靠谱判断,节点更容易出现资源争抢。

所以发布策略不只是“镜像怎么切”,也包括“集群有没有能力把新副本稳定放下去”。

Deployment 和 StatefulSet 的边界

适合 Deployment:

  • 副本可互换
  • 不需要稳定身份
  • 存储可丢弃或外置

适合 StatefulSet:

  • 每个副本都有独立身份
  • 每个副本都有独立数据
  • 启动顺序和更新顺序很重要

很多看似“数据库发不稳”的问题,本质上是控制器选错了。

常见发布故障

rollout 卡住

常见原因:

  • readiness 一直不过
  • 镜像拉取失败
  • requests 导致无法调度
  • maxUnavailable 和副本数设置太保守,当前容量兜不住

Pod 活着,但流量不通

这往往不是 Deployment 本身的 bug,而是 Service selector 和 Pod labels 对不上。

旧版本迟迟不下线

可能是 rollout 没真正结束,也可能是 endpoints 还指向旧 Pod,或者 readiness / termination 过慢。

实用排障顺序

kubectl describe deploy api
kubectl get rs
kubectl describe rs <replicaset>
kubectl get pods -l app=api
kubectl describe pod <pod-name>
kubectl logs <pod-name>
kubectl get events --sort-by=.metadata.creationTimestamp

先从 Deployment 往下看,再进入 ReplicaSet、Pod、事件,这样最容易看清发布链路卡在哪一段。

发布前建议检查什么

  • selector 和 labels 是否一致
  • 镜像 tag 是否明确
  • readiness 是否代表真实可用
  • requests / limits 是否合理
  • 回滚命令是否明确
  • Service 是否仍然能正确匹配新 Pod

FAQ

Q: 可以直接改 ReplicaSet 吗? A: 一般不建议。ReplicaSet 是 Deployment 管理出来的发布产物,直接修改容易造成混乱和漂移。

Q: 为什么改了一次 Deployment 就会出现新的 ReplicaSet? A: 因为 Pod 模板变化就意味着一个新的版本边界,Kubernetes 需要新的 ReplicaSet 来承载发布历史和回滚能力。

Q: 为什么 staging 发布正常,生产环境却卡住? A: 生产环境通常资源更紧、探针更严格、流量更真实,Service 依赖也更复杂,所以很多问题会在生产里被放大。

下一步阅读

  • 接着读 kubernetes-quickstart-probes.md
  • 再读 kubernetes-quickstart-service.md
  • 如果你要做更稳妥的发布,继续看 canary 和 rollout 相关 tips

收个尾

Deployment 不只是“多副本容器管理器”,它本质上是你的基础发布控制器。只要把 rollout 状态、ReplicaSet 历史和 readiness 行为看清楚,大量看似玄学的发布故障都会变得可解释。

参考链接