从云原生走向 AI 原生:一套面向未来的架构方法论 → 阅读《AI 原生基础设施》

整体架构

已发行

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 WebhookDeployment准入控制与 Pod 注入
Scheduler ExtenderDeployment(多副本)GPU 感知调度决策
Device PluginDaemonSet设备发现、注册与分配
MonitorDaemonSet指标采集与暴露

3.零侵入式设计

HAMi 对用户工作负载零侵入:

  • 用户只需在 Pod 的 resources 中声明 GPU 资源需求
  • 不需要修改应用代码或容器镜像
  • 不需要安装额外的运行时依赖
  • 资源隔离通过底层库注入实现,对应用透明

整体架构图

图 1: HAMi 整体架构
图 1: HAMi 整体架构

架构要点解读:

  • 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 原生调度器:

图 2: 调度器三段式流程
图 2: 调度器三段式流程

Device Plugin:设备注册、发现、分配

Device Plugin 在每个 GPU 节点上以 DaemonSet 形式运行:

  • 启动时通过 NVML 或厂商 SDK 发现物理 GPU,根据配置计算可分配资源,注册到 Kubelet
  • 运行时通过ListAndWatch持续上报设备状态,响应 Allocate 请求完成设备分配
  • 异常时检测设备故障,标记为 Unhealthy,通知 Kubelet 触发 Pod 迁移

Monitor:指标采集与暴露

Monitor 同样以 DaemonSet 形式运行,负责:

  • 采集每个 Pod 的 GPU 显存使用量、算力利用率
  • 以 Prometheus 指标格式暴露
  • 支持通过 ServiceMonitor 自动接入 Prometheus

端到端数据流

以下是一个 Pod 从提交到运行的完整数据流:

图 3: 端到端数据流
图 3: 端到端数据流

步骤详解

步骤 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 完成以下处理:

  1. 解析容器中的 nvidia.com/gpunvidia.com/gpumem 等自定义资源
  2. limits 复制到 requests(Kubernetes 要求二者一致)
  3. 添加调度注解,如设备类型、核心策略
  4. 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-allocatedSchedulerDevice PluginJSON 数组
hami.io/device-bind-phaseSchedulerMonitor字符串(allocating/allocated)
hami.io/bind-nodeSchedulerMonitor节点名称
hami.io/gpu-typeWebhookScheduler设备类型标识
hami.io/node-scheduler-policyWebhookScheduler调度策略名称
hami.io/core-policyWebhookScheduler核心分配策略

注解值中的 JSON 使用紧凑编码,不包含换行和多余空格,以避免 Kubernetes Annotation 大小限制(262144 字节)问题。

高可用架构

生产环境中 HAMi 的每个组件都需要考虑高可用:

多副本调度器 + 领导者选举

图 4: 调度器高可用架构
图 4: 调度器高可用架构

调度器通过 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/hostname

Webhook 失效时的保护机制:

  • 配置 failurePolicy: Fail 确保准入失败时拒绝请求(安全优先)
  • 如需更高的可用性,可配置 failurePolicy: Ignore(跳过准入检查,但可能影响调度正确性)

小结

本章从全局视角介绍了 HAMi 的整体架构:

  • 设计理念遵循 Kubernetes 扩展机制、四组件模型、零侵入式设计
  • 四大组件 Webhook(准入注入)、SchedulerExtender(调度决策)、Device Plugin(设备管理)、Monitor(指标采集)
  • 端到端数据流从 Pod 提交到容器运行的完整链路
  • 注解协议组件间通过 Kubernetes Annotation 解耦协作
  • 高可用架构多副本调度器 + 领导者选举、节点级锁、DaemonSet 自愈

在接下来的章节中,我们将逐一深入每个组件的实现细节,从调度器开始。

创建于 2026/06/04 更新于 2026/06/05 2675 字 阅读约 6 分钟