整体架构
HAMi 定位为 Kubernetes 的 GPU 资源控制平面,通过 Webhook、调度器扩展、设备插件和监控四大组件,构建了零侵入的 GPU 感知资源管理体系。
架构设计理念
HAMi 的架构设计遵循三个核心原则:
1.遵循 Kubernetes 扩展机制
HAMi 不修改任何 Kubernetes 核心代码,完全通过标准扩展点接入:
- AdmissionWebhook:利用 Mutating Admission Webhook 拦截 Pod 创建请求
- SchedulerExtender:通过 HTTP 接口扩展参与调度决策
- DevicePlugin:基于 Kubernetes Device Plugin API 注册自定义资源
- CRD + Informer:使用自定义资源描述设备状态
这意味着 HAMi 可以在任何标准 Kubernetes 集群上运行,无需定制构建。
2.四组件模型
HAMi 采用经典的四组件架构:
| 组件 | 运行形态 | 职责 |
|---|---|---|
| Mutating Admission Webhook | Deployment | 准入控制与 Pod 注入 |
| Scheduler Extender | Deployment(多副本) | GPU 感知调度决策 |
| Device Plugin | DaemonSet | 设备发现、注册与分配 |
| Monitor | DaemonSet | 指标采集与暴露 |
3.零侵入式设计
HAMi 对用户工作负载零侵入:
- 用户只需在 Pod 的
resources中声明 GPU 资源需求 - 不需要修改应用代码或容器镜像
- 不需要安装额外的运行时依赖
- 资源隔离通过底层库注入实现,对应用透明
整体架构图
架构要点解读:
- Webhook 和 Scheduler Extender 以 Deployment 形式运行在控制平面,参与 Pod 的准入和调度决策
- Device Plugin 和 Monitor 以 DaemonSet 形式运行在每个 GPU 节点,与硬件直接交互
- 各组件通过 Kubernetes 注解(Annotation) 协调工作,而非直接 RPC 调用
四大核心组件概述
Mutating Admission Webhook:准入控制与注入
Webhook 在 Pod 创建阶段拦截请求,执行以下操作:
# Webhook 处理逻辑概览
operations:
- 资源类型转换(limits → requests)
- 添加调度注解(标记 GPU 类型、资源需求)
- 注入环境变量(CUDA_VISIBLE_DEVICES 等)
- 设置 schedulerName 为 HAMi 调度器
- 配额检查与资源合法性校验Webhook 的关键在于“零侵入”,用户只需声明资源需求,Webhook 自动完成所有适配工作。
Scheduler Extender:Filter/Score/Bind 三段式调度
HAMi Scheduler Extender 通过 HTTP 接口扩展 Kubernetes 原生调度器:
Device Plugin:设备注册、发现、分配
Device Plugin 在每个 GPU 节点上以 DaemonSet 形式运行:
- 启动时通过 NVML 或厂商 SDK 发现物理 GPU,根据配置计算可分配资源,注册到 Kubelet
- 运行时通过
ListAndWatch持续上报设备状态,响应Allocate请求完成设备分配 - 异常时检测设备故障,标记为 Unhealthy,通知 Kubelet 触发 Pod 迁移
Monitor:指标采集与暴露
Monitor 同样以 DaemonSet 形式运行,负责:
- 采集每个 Pod 的 GPU 显存使用量、算力利用率
- 以 Prometheus 指标格式暴露
- 支持通过 ServiceMonitor 自动接入 Prometheus
端到端数据流
以下是一个 Pod 从提交到运行的完整数据流:
步骤详解
步骤 1:用户提交 Pod
用户提交一个带有 GPU 资源声明的 Pod:
apiVersion: v1
kind: Pod
metadata:
name: ai-inference
spec:
containers:
- name: inference
image: tensorflow/serving:latest
resources:
limits:
nvidia.com/gpu: 2 # 申请 2 个 GPU 设备
nvidia.com/gpumem: 4000 # 申请 4000 MB 显存步骤 2:Webhook 拦截与注入
API Server 在准入控制阶段将 Pod 发送给 HAMi Webhook。Webhook 完成以下处理:
- 解析容器中的
nvidia.com/gpu、nvidia.com/gpumem等自定义资源 - 将
limits复制到requests(Kubernetes 要求二者一致) - 添加调度注解,如设备类型、核心策略
- 将
schedulerName设置为 HAMi 调度器名称
步骤 3:调度器 Filter
kube-scheduler 通过 HTTP 将候选节点列表发送给 HAMi Scheduler Extender 的 /filter 端点。调度器检查每个节点是否满足 GPU 资源需求,过滤掉不满足的节点。
步骤 4:调度器 Score
kube-scheduler 调用 /score 端点,HAMi 调度器根据配置的策略(binpack/spread/topology-aware)对可行节点打分。
步骤 5:调度器 Bind
kube-scheduler 调用 /bind 端点,HAMi 调度器执行绑定操作:
- 获取节点级锁,防止并发冲突
- 计算具体的设备分配方案
- 将分配结果写入 Pod 注解
- 调用 Kubernetes API 执行绑定
- 释放锁
步骤 6:DevicePlugin 分配
Kubelet 在创建容器之前,通过 gRPC 调用 Device Plugin 的 Allocate 方法。Device Plugin 读取 Pod 注解中的分配信息,准备容器运行时环境(设备路径、环境变量、挂载点)。
步骤 7:容器启动
Kubelet 使用 Device Plugin 返回的配置创建容器,容器内应用通过标准 CUDA 接口使用 GPU。
步骤 8:Monitor 采集
Monitor 持续采集 GPU 使用指标,以 Prometheus 格式暴露供监控系统消费。
注解协议
HAMi 各组件之间通过 Kubernetes 注解(Annotation)传递信息,这是组件间解耦的核心机制。
调度器写入分配注解
调度器在 Bind 阶段将分配决策写入 Pod 注解:
metadata:
annotations:
# 调度器写入的分配注解
hami.io/bind-node: "gpu-node-01"
hami.io/device-bind-phase: "allocating"
hami.io/device-allocated: |
[
{
"type": "NVIDIA",
"uuid": "GPU-abc123-def456",
"memory": 4000,
"cores": 30,
"numa": 0
}
]设备插件读取注解完成分配
Device Plugin 在 Allocate 阶段读取这些注解:
// 伪代码:Device Plugin 读取分配注解
func (p *Plugin) Allocate(ctx context.Context, req *pluginapi.AllocateRequest) (*pluginapi.AllocateResponse, error) {
// 从 Pod 注解中读取调度器的分配决策
allocation := ParseAllocationAnnotation(pod.Annotations["hami.io/device-allocated"])
for _, device := range allocation {
// 准备设备路径
devicePaths = append(devicePaths, device.DevicePath)
// 准备环境变量
envs["CUDA_VISIBLE_DEVICES"] = device.UUID
}
return &AllocateResponse{
Envs: envs,
Mounts: mounts,
Devices: devices,
}, nil
}注解编码/解码规范
| 注解键 | 写入方 | 读取方 | 格式 |
|---|---|---|---|
hami.io/device-allocated | Scheduler | Device Plugin | JSON 数组 |
hami.io/device-bind-phase | Scheduler | Monitor | 字符串(allocating/allocated) |
hami.io/bind-node | Scheduler | Monitor | 节点名称 |
hami.io/gpu-type | Webhook | Scheduler | 设备类型标识 |
hami.io/node-scheduler-policy | Webhook | Scheduler | 调度策略名称 |
hami.io/core-policy | Webhook | Scheduler | 核心分配策略 |
注解值中的 JSON 使用紧凑编码,不包含换行和多余空格,以避免 Kubernetes Annotation 大小限制(262144 字节)问题。
高可用架构
生产环境中 HAMi 的每个组件都需要考虑高可用:
多副本调度器 + 领导者选举
调度器通过 Kubernetes Lease 对象实现领导者选举:
- 同一时刻只有一个 Scheduler 副本处理调度请求
- Leader 通过定期续约 Lease 保持活跃状态
- Leader 失效后,Standby 副本在秒级内接管
# 高可用调度器配置
scheduler:
replicas: 3
leaderElect:
enabled: true
leaseDuration: 15s
renewDeadline: 10s
retryPeriod: 2s
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchLabels:
app: hami-scheduler
topologyKey: kubernetes.io/hostname节点级锁保障并发一致性
在 Bind 阶段,即使只有一个 Leader 调度器在运行,也可能同时处理多个 Pod 的绑定请求。HAMi 使用节点级锁保障同一节点上的资源分配不发生冲突:
// 伪代码:节点级锁机制
func (s *Scheduler) Bind(nodeName string, pod *v1.Pod) error {
// 获取节点级互斥锁
lock := s.lockManager.Acquire(nodeName)
defer lock.Release()
// 读取节点当前资源使用
nodeUsage := s.cache.GetNodeUsage(nodeName)
// 检查资源是否仍然充足
if !nodeUsage.Fit(pod) {
return ErrInsufficientResources
}
// 计算分配方案并写入注解
allocation := s.computeAllocation(nodeUsage, pod)
patchPodAnnotation(pod, allocation)
// 执行绑定
return s.client.CoreV1().Pods(pod.Namespace).Bind(ctx, bind, metav1.CreateOptions{})
}DaemonSet 设备插件
Device Plugin 和 Monitor 以 DaemonSet 形式部署,天然具备节点级高可用:
- 每个 GPU 节点运行一个 Device Plugin 副本
- Pod 异常退出后 Kubernetes 自动重启
- 单节点故障不影响其他节点的设备分配
# DaemonSet 部署示意
devicePlugin:
enabled: true
updateStrategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: "10%"
tolerations:
- key: "nvidia.com/gpu"
operator: "Exists"
effect: "NoSchedule"Webhook 高可用
Webhook 通常部署 2 个以上副本,配合 Service 进行负载均衡:
webhook:
replicas: 2
service:
type: ClusterIP
port: 443
affinity:
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchLabels:
app: hami-webhook
topologyKey: kubernetes.io/hostnameWebhook 失效时的保护机制:
- 配置
failurePolicy: Fail确保准入失败时拒绝请求(安全优先) - 如需更高的可用性,可配置
failurePolicy: Ignore(跳过准入检查,但可能影响调度正确性)
小结
本章从全局视角介绍了 HAMi 的整体架构:
- 设计理念遵循 Kubernetes 扩展机制、四组件模型、零侵入式设计
- 四大组件 Webhook(准入注入)、SchedulerExtender(调度决策)、Device Plugin(设备管理)、Monitor(指标采集)
- 端到端数据流从 Pod 提交到容器运行的完整链路
- 注解协议组件间通过 Kubernetes Annotation 解耦协作
- 高可用架构多副本调度器 + 领导者选举、节点级锁、DaemonSet 自愈
在接下来的章节中,我们将逐一深入每个组件的实现细节,从调度器开始。