Controller:从 Deployment 到 Pod¶
default/vllm-demo Deployment 完成持久化后,Deployment Controller 观察到这个新对象并开始 Reconcile。集群中还没有对应的 ReplicaSet,因此它通过 API Server 创建一个 ReplicaSet;ReplicaSet Controller 随后发现期望副本数尚未满足,再创建一个 Pod。两个 Controller 都通过比较期望状态与当前状态决定是否提交修改。Pod 对象创建完成后,Scheduler 开始为它选择节点。
kube-controller-manager¶
kube-controller-manager 运行 Kubernetes 内置 Controller。逻辑上,每个 Controller 都有独立的关注对象和协调逻辑;发布时,这些 Controller 被编译进同一个二进制文件,由一个进程统一启动。Kubernetes 组件将它的职责概括为“运行控制器来实现 Kubernetes API 行为”。
常见的内置 Controller 包括:
| Controller | 关注的对象 | 主要动作 |
|---|---|---|
| Deployment Controller | Deployment、ReplicaSet | 创建或调整 ReplicaSet,执行工作负载更新 |
| ReplicaSet Controller | ReplicaSet、Pod | 创建或删除 Pod,使副本数符合 spec.replicas |
| StatefulSet Controller | StatefulSet、Pod、PVC | 按固定身份和顺序维护有状态 Pod |
| DaemonSet Controller | DaemonSet、Node、Pod | 在符合条件的 Node 上维护 Pod |
| Job Controller | Job、Pod | 根据 Job 的 Pod template 创建任务 Pod,并根据 Pod 结果更新 Job 状态 |
| Node Lifecycle Controller | Node、Pod | 更新节点健康状态并处理节点失联 |
| EndpointSlice Controller | Service、Pod、EndpointSlice | 根据 Service selector 和 Pod 状态维护 EndpointSlice |
| PersistentVolume Controller | PV、PVC | 处理卷声明绑定与回收 |
| Garbage Collector | 带 OwnerReference 的 API 对象 | 根据对象所有权清理附属对象 |
高可用控制面可以同时运行多个 kube-controller-manager 实例。Leader election 选出的实例执行内置 Controller;leader 发生变化后,新实例继续处理 API 中已经持久化的对象。
Reconcile 协调¶
Reconcile 是 Controller 针对一个对象执行的一次状态协调。Controller 读取目标对象及其相关对象,比较期望状态与当前状态,再通过 API Server 创建、更新或删除对象。两者已经一致时,本次 Reconcile 不需要提交修改;对象状态再次变化后,Controller 会重新执行 Reconcile。
下图以 vllm-demo 为例,展示 Controller 如何读取 Deployment 的期望状态、检查 API 中的相关对象,并提交使两者一致的修改。

vllm-demo 的创建过程包含两个 Controller 的 Reconcile。Deployment Controller 处理 Deployment,并创建 ReplicaSet;ReplicaSet 持久化后,ReplicaSet Controller 处理这个新对象,再创建 Pod。每次 API 写入只创建一层对象。
下图展示了两个 Controller 依次创建 ReplicaSet 和 Pod 的过程。
sequenceDiagram
autonumber
participant A as kube-apiserver
participant D as Deployment Controller
participant R as ReplicaSet Controller
A-->>D: Deployment default/vllm-demo
D->>D: Reconcile Deployment
D->>A: POST ReplicaSet vllm-demo-<hash>
A-->>D: ReplicaSet persisted
A-->>R: ReplicaSet vllm-demo-<hash>
R->>R: Reconcile ReplicaSet
R->>A: POST Pod vllm-demo-<hash>-<suffix>
A-->>R: Pod persisted
Deployment Controller 提交 ReplicaSet 后,API Server 对这次创建请求执行认证、授权、准入、资源对象校验和持久化。ReplicaSet Controller 创建 Pod 时再次经过同一套 API 写入流程。两个 Controller 都使用 kube-controller-manager 的客户端凭据访问 API Server。
Deployment Controller¶
Deployment Controller 读取 Deployment 的副本数、selector 和 Pod template,再查找由它管理的 ReplicaSet。它根据 Deployment 的变化选择创建 ReplicaSet、调整副本数或更新 Status。
vllm-demo 中与 Controller 直接相关的字段如下。完整清单保存在 examples/chapter-01/12-vllm/02-deployment.yaml。
- 副本数:
spec.replicas: 1要求工作负载最终保留一个副本。 - Selector:
spec.selector识别 Deployment 对应的工作负载集合,并且必须匹配 Pod template 中的 label。 - Pod template:
spec.template是 ReplicaSet 和 Pod 的配置来源;模板内容变化会产生新的 ReplicaSet。
spec:
replicas: 1
selector:
matchLabels:
app: vllm-demo
template:
metadata:
labels:
app: vllm-demo
ReplicaSet 的创建¶
Deployment Controller 先查找 Pod template 与当前 Deployment 相同的 ReplicaSet。首次创建 vllm-demo 时不存在这样的 ReplicaSet,Controller 根据 Pod template 计算 hash,并构造新的 ReplicaSet。
新 ReplicaSet 包含以下信息:
- 名称由 Deployment 名称和 hash 组成,例如
vllm-demo-6d8f47f7b8。 spec.template复制 Deployment 的完整 Pod template,包括容器、卷和 GPU 申请。- selector 与 Pod template label 增加
pod-template-hash=<hash>。 - OwnerReference 指向
default/vllm-demoDeployment,并记录 Deployment 的 UID。 spec.replicas根据 Deployment 的副本数和更新策略计算。
pod-template-hash 将不同 Pod template 对应的 ReplicaSet 分开。普通更新中,只有 .spec.template 变化才会产生新的 hash 和 ReplicaSet;修改 spec.replicas 只调整现有 ReplicaSet。该标签由 Deployment Controller 维护,不应手动修改。Deployment 官方文档说明了名称和标签的对应关系。
ReplicaSet 构造完成后,Deployment Controller 调用 AppsV1().ReplicaSets(d.Namespace).Create 创建 ReplicaSet。API Server 返回成功后,可以通过 ReplicaSet API 单独查询这个对象;ReplicaSet Controller 随后根据它创建 Pod。
扩缩容与模板更新¶
修改 spec.replicas 时,Deployment Controller 调整现有 ReplicaSet 的期望副本数,再由 ReplicaSet Controller 创建或删除 Pod。例如,把 Deployment 从一个副本扩到三个副本后,ReplicaSet Controller 会补充两个 Pod。RollingUpdate 期间,新旧 ReplicaSet 可以同时处于 active 状态,Deployment Controller 会在它们之间按比例分配扩缩容数量。
修改 .spec.template 时,Deployment Controller 创建带有新 hash 的 ReplicaSet。vllm-demo 没有显式设置 strategy,因此使用默认的 RollingUpdate:新旧 ReplicaSet 的副本数由 Deployment Controller 逐步调整。发布过程还受 maxSurge、maxUnavailable、暂停和回滚配置控制,具体规则见 Deployments。
ReplicaSet Controller¶
ReplicaSet Controller 读取 ReplicaSet 的 spec.replicas 和当前由它管理的 active Pod 数量。两者的差额决定本轮创建还是删除 Pod:
diff < 0 时缺少 Pod,Controller 从 spec.template 复制 PodSpec、label 和 annotation,设置指向 ReplicaSet 的 ControllerRef,再调用 Pod Create API。diff > 0 时副本过多,Controller 选择并删除相应数量的 Pod。
ReplicaSet 创建的 Pod 使用 ReplicaSet 名称作为 generateName 前缀,因此最终名称类似 vllm-demo-6d8f47f7b8-r9x2m。API Server 为 Pod 分配唯一名称和 UID,Pod 的 OwnerReference 则保存 ReplicaSet 的名称与 UID。
用户删除一个 Pod 后,ReplicaSet 的期望副本数仍然是 1。ReplicaSet Controller 观察到 active Pod 数量变为 0,随后创建一个新 Pod。新 Pod 具有新的名称和 UID,Deployment 与 ReplicaSet 保持不变。
OwnerReference 与垃圾回收¶
spec.selector 根据 label 选择对象,ControllerRef 则通过 owner 的名称和 UID 绑定具体的对象实例。同名 Deployment 重新创建后会获得新的 UID,因此不会接管旧 ReplicaSet。
Deployment Controller 创建 ReplicaSet 时写入指向 Deployment 的 ControllerRef;ReplicaSet Controller 创建 Pod 时写入指向 ReplicaSet 的 ControllerRef。OwnerReference 中的 controller: true 表示这条引用是 ControllerRef,blockOwnerDeletion: true 用于前台级联删除。
删除 Deployment 时,Garbage Collector 沿着 OwnerReference 清理 ReplicaSet 和 Pod。默认的后台级联删除先删除 Deployment,再清理附属对象;前台级联删除等待附属对象删除完成,orphan 策略则保留附属对象。
下面的 YAML 展示 vllm-demo ReplicaSet 和 Pod 的完整 spec。名称后缀和 UID 每次创建都会变化,因此使用占位值表示。
apiVersion: apps/v1
kind: ReplicaSet
metadata:
name: vllm-demo-6d8f47f7b8
namespace: default
uid: "<replicaset-uid>"
labels:
app: vllm-demo
pod-template-hash: 6d8f47f7b8
ownerReferences:
- apiVersion: apps/v1
kind: Deployment
name: vllm-demo
uid: "<deployment-uid>"
controller: true
blockOwnerDeletion: true
spec:
replicas: 1
selector:
matchLabels:
app: vllm-demo
pod-template-hash: 6d8f47f7b8
template:
metadata:
labels:
app: vllm-demo
pod-template-hash: 6d8f47f7b8
spec:
runtimeClassName: nvidia
containers:
- name: vllm
image: vllm/vllm-openai:v0.27.1
command: ["vllm", "serve"]
args: ["Qwen/Qwen3-0.6B", "--port", "8000"]
ports:
- containerPort: 8000
resources:
limits:
nvidia.com/gpu: "1"
volumeMounts:
- name: model-cache
mountPath: /root/.cache/huggingface
volumes:
- name: model-cache
persistentVolumeClaim:
claimName: vllm-model-cache
---
apiVersion: v1
kind: Pod
metadata:
name: vllm-demo-6d8f47f7b8-r9x2m
generateName: vllm-demo-6d8f47f7b8-
namespace: default
uid: "<pod-uid>"
labels:
app: vllm-demo
pod-template-hash: 6d8f47f7b8
ownerReferences:
- apiVersion: apps/v1
kind: ReplicaSet
name: vllm-demo-6d8f47f7b8
uid: "<replicaset-uid>"
controller: true
blockOwnerDeletion: true
spec:
runtimeClassName: nvidia
containers:
- name: vllm
image: vllm/vllm-openai:v0.27.1
command: ["vllm", "serve"]
args: ["Qwen/Qwen3-0.6B", "--port", "8000"]
ports:
- containerPort: 8000
resources:
limits:
nvidia.com/gpu: "1"
volumeMounts:
- name: model-cache
mountPath: /root/.cache/huggingface
volumes:
- name: model-cache
persistentVolumeClaim:
claimName: vllm-model-cache
Status:实际状态¶
Deployment 的 spec 记录期望状态,status 记录 Controller 最近观察到的实际状态。声明式部署要求实际状态向 spec 收敛;Deployment Controller 持续 Reconcile,并根据 ReplicaSet 和 Pod 的变化回写 status。
使用下面的命令可以查看 vllm-demo 的完整状态:
一个副本已经完成部署时,关键字段如下。ReplicaSet 名称中的 hash 每次修改 Pod template 后都可能变化。
apiVersion: apps/v1
kind: Deployment
metadata:
name: vllm-demo
namespace: default
generation: 1
spec:
replicas: 1
status:
observedGeneration: 1
replicas: 1
updatedReplicas: 1
readyReplicas: 1
availableReplicas: 1
conditions:
- type: Available
status: "True"
reason: MinimumReplicasAvailable
message: Deployment has minimum availability.
- type: Progressing
status: "True"
reason: NewReplicaSetAvailable
message: ReplicaSet "vllm-demo-6d8f47f7b8" has successfully progressed.
generation与observedGeneration:generation随 Deployment spec 更新而递增;两个值相等表示 Deployment Controller 已处理最新配置,但不表示 Pod 已经可用。replicas与updatedReplicas:replicas是 Deployment 当前管理的非终止 Pod 数量,updatedReplicas是使用最新 Pod template 的 Pod 数量。readyReplicas与availableReplicas:前者统计Ready=True的 Pod,后者统计 Ready 状态持续达到minReadySeconds的 Pod。conditions:Progressing记录发布进度,Available记录最低可用性。Progressing=True既可能表示正在更新,也可能表示已经完成,需要结合reason判断;NewReplicaSetAvailable表示新 ReplicaSet 已完成发布。Deployment 状态列出了完整的 Condition 变化规则。
实验:观察 Controller 协调¶
examples/chapter-01/04-controller 提供不依赖 GPU 的 controller-demo Deployment。实验验证 Deployment Controller 和 ReplicaSet Controller 如何创建对象、建立所有权关系并维持期望副本数,包括扩容、Pod 删除后的副本补齐,以及 Deployment 删除后的级联清理。
配置文件通过无法匹配节点的 nodeSelector 让 Pod 保持 Pending。这是实验的预期结果,用于排除 Scheduler、Kubelet、镜像拉取和容器启动的影响,只观察 API 中的 Controller 协调结果:
apiVersion: apps/v1
kind: Deployment
metadata:
name: controller-demo
spec:
replicas: 2
selector:
matchLabels:
app: controller-demo
template:
metadata:
labels:
app: controller-demo
spec:
# 使用不存在的节点标签,使 Pod 保持 Pending;示例只观察 Controller 对 API 对象的协调结果。
nodeSelector:
controller-demo.example/unschedulable: "true"
containers:
- name: workload
image: registry.k8s.io/pause:3.10
imagePullPolicy: IfNotPresent
从仓库根目录运行验证脚本:
输出中的 [PASS] 来自 API 对象状态断言,[EVIDENCE] 来自 core/v1 Event 的 source.component。Deployment Controller 调整 ReplicaSet 时记录 ScalingReplicaSet,ReplicaSet Controller 创建 Pod 时记录 SuccessfulCreate。最后一个阶段通过 foreground deletion 的结果验证 Garbage Collector 根据 OwnerReference 清理附属对象。
Controller reconciliation verification
--------------------------------------
Context kind-controller-demo
Namespace default
Kubernetes v1.36.1
Kind v0.29.0 go1.24.2 darwin/arm64
[1/4] Initial reconciliation
[PASS] ReplicaSet object controller-demo-c6869b59b owner=Deployment/controller-demo UID=match
[EVIDENCE] Deployment Event source=deployment-controller reason=ScalingReplicaSet
[PASS] Pod objects expected=2 observed=2
[EVIDENCE] Pod creation Events source=replicaset-controller reason=SuccessfulCreate matched=2/2
[PASS] Pod owners 2/2 -> ReplicaSet/controller-demo-c6869b59b UID=match
[PASS] Pod template hash c6869b59b (ReplicaSet and Pods match)
[2/4] Scale reconciliation
[ACTION] Deployment replicas 2 -> 3
[PASS] ReplicaSet replicas expected=3 observed=3
[PASS] Pod objects expected=3 observed=3
[PASS] Created Pod controller-demo-c6869b59b-tl9cm
[EVIDENCE] Deployment Event source=deployment-controller reason=ScalingReplicaSet
[EVIDENCE] Pod creation Event source=replicaset-controller reason=SuccessfulCreate pod=controller-demo-c6869b59b-tl9cm
[3/4] Pod replacement
[ACTION] Deleted Pod controller-demo-c6869b59b-4dcj5
[PASS] Replacement Pod controller-demo-c6869b59b-46hdj
[PASS] Pod objects expected=3 observed=3
[EVIDENCE] Pod creation Event source=replicaset-controller reason=SuccessfulCreate pod=controller-demo-c6869b59b-46hdj
[4/4] Garbage Collector: foreground deletion
[ACTION] Deployment deletion propagation=Foreground
[PASS] OwnerReference chain verified before deletion
[PASS] Dependent objects ReplicaSets=0 Pods=0
State assertions: PASS
Event evidence: PASS
脚本完成级联删除验证后保留 Kind 集群,不保留示例对象。对象名称和 pod-template-hash 每次运行都会变化。需要逐项查看 ReplicaSet 和 Pod 时,可以按 Controller 协调实验 中的手动观察步骤只应用 deployment.yaml;README 同时给出了验证条件与清理命令。