跳转至

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 Deployment 的期望状态经过 Reconcile 后,在 API 中形成 Deployment、ReplicaSet 和 Pod

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。
examples/chapter-01/12-vllm/02-deployment.yaml
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-demo Deployment,并记录 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 = activePods - desiredReplicas

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 每次创建都会变化,因此使用占位值表示。

ReplicaSet 与 Pod 的 OwnerReference
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 的完整状态:

kubectl get deployment vllm-demo -n default -o yaml

一个副本已经完成部署时,关键字段如下。ReplicaSet 名称中的 hash 每次修改 Pod template 后都可能变化。

Deployment/vllm-demo 状态节选
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 协调结果:

examples/chapter-01/04-controller/deployment.yaml
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

从仓库根目录运行验证脚本:

./examples/chapter-01/04-controller/verify.sh

输出中的 [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 同时给出了验证条件与清理命令。

相关资料