工作原理

深入了解 CastSlice 的 Webhook 架构、Kubernetes 准入控制集成,以及用于改写 GPU 资源请求的 JSON Patch 策略。

架构

CastSlice 是一个 Kubernetes Mutating Admission Webhook。它以标准 Deployment 形式运行在集群内部,并将自身注册到 API Server 的准入控制器流水线中。

当集群中创建任意 Pod 时,API Server 会在持久化之前将请求转发给 CastSlice。CastSlice 检查 Pod 注解——如果已选择启用——则将 GPU 资源请求改写为共享 GPU 资源。改写后的对象被返回,API Server 继续正常完成调度。

准入流程

┌──────────────────── Kubernetes 控制平面 ───────────────────────────┐ │ │ │ kubectl apply │ │ │ │ │ ▼ │ │ API Server ────────── AdmissionReview ──────────▶ CastSlice │ │ │ │ │ Has castops.io/optimize: "true"? │ │ │ No ◀────┤ │ │ pass-through ◀───────┘ │ │ │ Yes ──▶ Mutate │ │ JSON Patch │ │ gpu → gpu-shared│ │ │ │ │ ◀────────────────── AdmissionReview ───────────────┘ │ │ │ (with patches embedded) │ │ ▼ │ │ API Serveretcd │ │ │ └─────────────────────────────────────────────────────────────────────┘
ℹ️

准入 Webhook 是同步内联调用的。CastSlice 必须在 API Server 的 Webhook 超时时间内(默认 10 秒)响应。改写后的 Pod 会被持久化,原始请求则被丢弃。

注解参考

CastSlice 通过 Pod(或 Deployment Pod 模板)上的注解来决定是否执行变更,以及分配多少 GPU 切片。

注解 是否必需 说明
castops.io/optimize "true" 必需 将 Pod 选入 GPU 切片变更。任何非 "true" 的值均视为未设置,Pod 将原样透传。
castops.io/workload-type "training" / "inference" / "batch" / "dev" 可选 根据工作负载类别选择预设切片数。training → 4,inference → 2,batch → 2,dev → 1(缺省值)。
castops.io/slice-ratio 正整数字符串,如 "8" 可选 用显式切片数覆盖工作负载类型预设值,优先级高于 castops.io/workload-type

解析优先级

CastSlice 按以下顺序确定最终切片数:

  1. castops.io/slice-ratio — 显式覆盖,优先级最高
  2. castops.io/workload-type — 预设查找
  3. 默认值 1 — 与 v0.1.0 向后兼容

注解位置很重要

注解必须位于 Pod 自身的 metadata 上。对于 Deployment,这意味着要写在 spec.template.metadata.annotations,而不是 Deployment 顶层的 metadata.annotations

正确位置(Deployment)
spec: template: metadata: annotations: castops.io/optimize: "true" ✓ On the Pod template spec: containers: ...

JSON Patch 策略

触发变更时,CastSlice 返回包含 JSON Patch(RFC 6902)数组的 AdmissionResponse。API Server 在持久化对象之前原子性地应用该 patch。

CastSlice 遍历所有容器类型——initContainerscontainersephemeralContainers。对于每个键为 nvidia.com/gpu 的条目:

  1. 通过 op: remove 移除现有的 nvidia.com/gpu
  2. 通过 op: add 添加解析后切片数的 nvidia.com/gpu-shared 条目
CastSlice 产生的 JSON Patch(workload-type: training → ratio 4)
[ { "op": "remove", "path": "/spec/containers/0/resources/limits/nvidia.com~1gpu" }, { "op": "add", "path": "/spec/containers/0/resources/limits/nvidia.com~1gpu-shared", "value": "4" // 动态值 — v0.1.0 中固定为 "1" } ]
ℹ️

JSON Pointer 编码:键名中的正斜杠(/)在 JSON Patch 路径中按 RFC 6901 编码为 ~1。因此 nvidia.com/gpu 在 patch 路径中变为 nvidia.com~1gpu

该 patch 经 base64 编码后,连同 patchType: JSONPatch 一起放入 AdmissionResponse.Patch 字段返回。

Webhook 配置参考

CastSlice 注册为 MutatingWebhookConfiguration,关键配置如下:

字段原因
rules[].resources ["pods"] 仅拦截 Pod CREATE/UPDATE,忽略所有其他资源类型。
rules[].operations ["CREATE"] 变更发生在 Pod 创建时,已运行的 Pod 不会被修改。
failurePolicy Ignore 若 CastSlice 不可达,Pod 仍会被准入。这确保 CastSlice 宕机不会阻塞工作负载。
admissionReviewVersions ["v1"] 使用稳定的 v1 AdmissionReview API。
sideEffects None Webhook 无副作用,这是 dry-run 兼容性的要求。

通过 cert-manager 管理 TLS

MutatingWebhookConfiguration 要求 API Server 信任 Webhook 的 TLS 证书。CastSlice 使用 cert-manager 的 Certificate 资源获取 CA 签名证书,并在 Webhook 配置上设置 cert-manager.io/inject-ca-from 注解。cert-manager 会自动将 CA bundle 注入 webhooks[].clientConfig.caBundle,并在证书续期时保持更新。

GPU 共享:nvidia.com/gpu-shared

NVIDIA 提供了两种主要机制来让多个 Pod 共享一块物理 GPU:

时间片(Time-Slicing)

NVIDIA GPU Operator(v1.10+)通过定义 GPU 副本数的 ConfigMap 支持时间片。每个副本作为独立的 nvidia.com/gpu 资源暴露。CastSlice 通过写入 nvidia.com/gpu-shared 资源键来配合该机制——你可以通过设备插件配置将其映射到时间片资源池。

多进程服务(MPS)

NVIDIA MPS 允许多个 CUDA 进程并发共享同一 GPU 上下文,隔离性优于时间片。nvidia.com/gpu-shared 资源键用于设备插件配置为暴露 MPS 资源池的集群。

⚠️

CastSlice 仅负责 Kubernetes 资源命名——实际的 GPU 共享由 NVIDIA 设备插件和集群配置提供。部署前请确保 nvidia.com/gpu-shared 已在节点上注册为合法资源。