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)也能跑多副本、也能滚动发布,区别在哪?
| 维度 | 经典 PaaS | K8s Deployment |
|---|---|---|
| 抽象 | 以"应用"为中心 | 以"资源对象 + 控制器"为中心 |
| 发布 | 平台托管,黑盒 | RS 版本化,可精确回滚 |
| 扩展 | 通常只能扩副本 | 任意对象可自定义控制器扩展 |
| 灵活性 | 受限(平台给什么用什么) | 可编写自定义控制器/CRD(见第 08 篇) |
经典 PaaS 把"副本管理"做成了一个写死的功能点;而 K8s 把它抽象成通用机制(控制器 + 声明式 API),于是你不仅能用 Deployment,还能用同样的模式造出 StatefulSet、DaemonSet,甚至自己的 Operator。这就是 K8s “可扩展” 与 “声明式” 的真正威力。
小结 / 核心要点
- 控制器模式 = 声明式 API + Reconcile 循环:你描述"想要什么",系统持续纠偏到"实际=期望"。
- Deployment 不直接管 Pod,而是
Deployment → ReplicaSet → Pod三层委托。 - ReplicaSet 保留旧版本是实现快速回滚的关键。
- 滚动更新靠
maxSurge/maxUnavailable控制"多建几个、允许停几个",实现不停机发布。 - K8s 把副本管理做成通用机制,这是它远比传统 PaaS 灵活、可扩展的根本原因。
