05 | 控制器模式与 Deployment:编排的本质

系列专栏《深入剖析 Kubernetes》· 基于张磊极客时间专栏思想整理 · 适合 CSDN 发布

05 | 控制器模式与 Deployment:编排的本质

上一篇我们讲了 Pod 是最小调度单元。但 Pod 自己不会"保持"几个副本、不会滚动更新、不会回滚。这些"让应用按期望运行"的能力,全部来自 K8s 的控制器模式(Controller Pattern)

这篇文章,我们先抓住 K8s 最提纲挈领的一个思想,再落地到最常用的Deployment


一、控制器模式:K8s 的"灵魂"

Kubernetes 整个编排系统的核心,可以浓缩成一句话:

声明式 API + 调谐循环(Reconcile Loop)

你不是下命令"去启动 3 个 Pod"(那是命令式);而是声明一个期望状态(Desired State):“我要 3 个 Pod 在跑”。剩下的交给一个叫controller的死循环,它不断对比"实际状态"和"期望状态",发现差距就动手弥合。

用户 (kubectl apply) | 声明期望状态: replicas=3 v +-------------+ watch +----------------------+ | Etcd | <-------------- | Controller | | (实际/期望 | | (调谐循环) | | 状态存储) | -------------> | | +-------------+ 读取实际状态 | if 实际 != 期望: | | 创建/删除 Pod | +----------------------+ ^ | | v +----------- 反复 reconcile -------+ 直到 实际==期望

这就是所谓的level-triggered(基于水平触发的调谐):控制器不在乎"发生了什么事件",只在乎"当前状态对不对"。网络抖动、进程崩溃、节点宕机,只要最终状态不对,它就会持续纠偏。这正是 K8s "自愈"能力的来源。


二、Deployment → ReplicaSet → Pod 控制链

Deployment是用户最常用的工作负载,但它不直接管 Pod。它的真实结构是三层委托:

Deployment (声明:副本数、镜像版本、更新策略) | 管理 v ReplicaSet (保证"某一版本"恰好有 N 个 Pod) | 管理 v Pod (真正跑容器的实例) 滚动更新时: Deployment 创建 新的 ReplicaSet (v2) 旧 ReplicaSet (v1) 缩容 -> 0 新 ReplicaSet (v2) 扩容 -> N 两个 RS 短暂共存,实现"不停机更新"

为什么要中间加一层ReplicaSet?因为ReplicaSet(RS)的职责是保证某个固定 Pod 模板的副本数。当你改镜像版本触发滚动更新,Deployment 会新建一个 RS 来管理新版本 Pod,旧 RS 保留下来——这正好就是"回滚"的基石(旧 RS 还在,随时可拉回来)。


三、副本与水平扩展

一个最精简的 Deployment:

apiVersion:apps/v1kind:Deploymentmetadata:name:nginxspec:replicas:3# 期望副本数,RS 会维持恰好 3 个 Podselector:matchLabels:app:nginxtemplate:# Pod 模板(Pod 怎么造,写在这里)metadata:labels:app:nginxspec:containers:-name:nginximage:nginx:1.25resources:requests:{cpu:"100m",memory:"128Mi"}limits:{cpu:"200m",memory:"256Mi"}

参数解释

  • replicas:声明式副本数。改这个值就是扩缩容(当然生产用 HPA 更优雅,见第 12 篇)。
  • selector.matchLabels:RS 靠这个标签筛出"归我管的 Pod",务必与template.labels一致。
  • resources.requests/limits:调度依据与资源上限,避免单 Pod 吃满 Node。

扩容只是改个数字:kubectl scale deployment nginx --replicas=5


四、滚动更新与回滚

Deployment 默认用RollingUpdate(滚动更新),分批替换旧 Pod,保证服务不中断。

spec:strategy:type:RollingUpdaterollingUpdate:maxSurge:1# 最多比期望多 1 个 Pod(更新时临时超出)maxUnavailable:0# 更新期间最多 0 个 Pod 不可用(保证容量)

参数解释

  • maxSurge:滚动中额外能创建的 Pod 数(可为百分比,如25%)。1表示先起 1 个新 Pod 再下 1 个旧的。
  • maxUnavailable:滚动中允许不可用的 Pod 数。0表示全程不丢容量(代价是临时多占用资源)。

触发更新(改镜像即可):

kubectlsetimage deployment/nginxnginx=nginx:1.26 kubectl rollout status deployment/nginx# 观察滚动进度# 查看历史版本kubectl rollouthistorydeployment/nginx# 回滚到上一版kubectl rollout undo deployment/nginx# 回滚到指定版本kubectl rollout undo deployment/nginx --to-revision=2

回滚之所以快,是因为旧ReplicaSet从未被删除,只是副本数被置 0。undo 本质上是把旧 RS 拉回前台。


五、和经典 PaaS 的"作业副本"对比

传统 PaaS(如 Cloud Foundry / Heroku)也能跑多副本、也能滚动发布,区别在哪?

维度经典 PaaSK8s Deployment
抽象以"应用"为中心以"资源对象 + 控制器"为中心
发布平台托管,黑盒RS 版本化,可精确回滚
扩展通常只能扩副本任意对象可自定义控制器扩展
灵活性受限(平台给什么用什么)可编写自定义控制器/CRD(见第 08 篇)

经典 PaaS 把"副本管理"做成了一个写死的功能点;而 K8s 把它抽象成通用机制(控制器 + 声明式 API),于是你不仅能用 Deployment,还能用同样的模式造出 StatefulSet、DaemonSet,甚至自己的 Operator。这就是 K8s “可扩展” 与 “声明式” 的真正威力。


小结 / 核心要点

  • 控制器模式 = 声明式 API + Reconcile 循环:你描述"想要什么",系统持续纠偏到"实际=期望"。
  • Deployment 不直接管 Pod,而是Deployment → ReplicaSet → Pod三层委托。
  • ReplicaSet 保留旧版本是实现快速回滚的关键。
  • 滚动更新靠maxSurge/maxUnavailable控制"多建几个、允许停几个",实现不停机发布。
  • K8s 把副本管理做成通用机制,这是它远比传统 PaaS 灵活、可扩展的根本原因。