HAMI 面试题
36 道题- 分类
- AI 与大模型
- 子分类
- infra
- 题目数
- 36 道
1 HAMI 的核心定位是什么?它与 GPU Operator、Volcano、Time-Slicing 分别在什么层面解决问题?
答案:
HAMI 是 GPU 共享与虚拟化中间件,在 Kubernetes 中实现单 GPU 被多个容器共享,并提供显存与算力维度的隔离。HAMI 位于设备插件层与调度层之间,通过 LD_PRELOAD 注入 CUDA 库拦截 API 调用实现资源限制,同时以 Scheduler Extender 方式参与 Pod 调度决策。
分层对比:
| 组件 | 作用层面 | 核心能力 | 典型场景 |
|---|---|---|---|
| HAMI | CUDA API 拦截 + 调度扩展 | 显存/算力隔离,单卡共享 | 多推理服务共享 GPU |
| GPU Operator | 驱动与组件生命周期管理 | NVIDIA 驱动、container-toolkit、device-plugin 自动化部署 | GPU 节点初始化与运维 |
| Volcano | 批量调度与任务编排 | Gang Scheduling、Queue、公平调度 | 分布式训练任务调度 |
| Time-Slicing | GPU 时分复用 | 基于时间片的 GPU 共享 | 轻量级 GPU 复用 |
HAMI 与 GPU Operator 互补:GPU Operator 管理 GPU 基础软件栈,HAMI 在此之上提供细粒度资源共享。HAMI 与 Volcano 可协同:Volcano 负责任务排队与 Gang Scheduling,HAMI 负责单卡内的资源切分与隔离。HAMI 与 Time-Slicing 的本质区别:HAMI 提供显存硬隔离 + 算力软隔离,Time-Slicing 仅做时间片轮转不提供资源保证。
2 HAMI 的显存隔离机制是如何实现的?
答案:
HAMI 通过 LD_PRELOAD 机制在容器启动时注入自定义 CUDA 库(libvgpu.so),劫持 CUDA 显存分配相关 API(cudaMalloc、cudaFree、cuMemAlloc 等),在应用请求 GPU 显存时检查是否超过容器声明的显存限制,若超限则返回分配失败,从而实现显存的硬隔离。
实现链路:
graph TD
A[Container 启动] --> B["LD_PRELOAD=libvgpu.so"]
B --> C["拦截 cudaMalloc(size)"]
C --> D["检查 current_used + size > hami.io/gpu-memory"]
D -->|是| E["返回 cudaErrorMemoryAllocation - OOM"]
D -->|否| F["透传到真实 CUDA driver 分配"]
关键配置文件(device-plugin 侧):
# HAMI device plugin 配置
devices:
nvidia:
enabled: true
resourceCountName: hami.io/gpu
resourceMemoryName: hami.io/gpu-memory
resourceMemoryPercentageName: hami.io/gpu-memory-percentage
resourceCoreName: hami.io/gpu-core
Pod 侧声明:
resources:
limits:
hami.io/gpu: 1 # 请求 1 张 GPU 卡
hami.io/gpu-memory: 4 # 限制显存 4GB
hami.io/gpu-core: 30 # 限制 30% 算力
显存限制在进程启动即生效,libvgpu.so 维护每个进程的显存用量计数器,实时拦截并校验每次显存分配请求。
3 HAMI 的算力隔离机制是如何实现的?
答案:
HAMI 的算力隔离基于 GPU 时间片控制,通过在 libvgpu.so 中劫持 CUDA Kernel Launch API(cudaLaunchKernel、cuLaunchKernel 等),在 Kernel 执行前插入 sleep 或忙等待来控制 GPU 核心利用率占比。
控制模型:
时间窗口(默认 1 秒)
├── 有效计算时间 = 窗口 × (core_percentage / 100)
└── 强制空闲时间 = 窗口 × (1 - core_percentage / 100)
CPU 利用率控制实现:
// libvgpu.so 中的伪代码逻辑
void intercept_kernel_launch() {
uint64_t elapsed = now() - window_start;
uint64_t allowed_time = time_window * core_percent / 100;
if (elapsed >= time_window) {
// 新窗口开启
window_start = now();
busy_time = 0;
}
if (busy_time >= allowed_time) {
// 超额,休眠至下一个窗口
usleep((time_window - elapsed) * 1000);
window_start = now();
busy_time = 0;
}
real_kernel_launch();
busy_time += kernel_exec_time;
}
关键参数:
| 参数 | 默认值 | 说明 |
|---|---|---|
time_window | 1000ms | 算力统计窗口 |
core_percent | 100 | 容器声明的算力百分比 |
utilization_penalty | true | 是否启用超限惩罚 |
算力隔离为软限制(soft limit),不影响 Kernel 正确性,仅限制吞吐。在 GPU 空闲时容器可能获得超过声明的算力,繁忙时则严格限制在声明区间内。
4 HAMI 的设备插件是如何向 kubelet 注册并上报虚拟化 GPU 资源的?
答案:
HAMI device plugin 实现了 Kubernetes Device Plugin API(v1beta1),在 GPU 节点上以 DaemonSet 形式运行,向 kubelet 的 /var/lib/kubelet/device-plugins/ 目录注册 Unix Socket,并通过 ListAndWatch gRPC 接口持续上报虚拟化后的 GPU 资源。
注册流程:
graph TD
A[HAMI Device Plugin Pod] --> B["1. 发现物理 GPU - nvml"]
B --> B1["读取 GPU 型号、显存总量、SM 数量"]
A --> C["2. 注册 Device Plugin Socket"]
C --> C1["/var/lib/kubelet/device-plugins/hami.sock"]
A --> D["3. kubelet 调用 ListAndWatch"]
D --> D1["返回虚拟化 Devices 列表"]
D1 --> D1a["hami.io/gpu - GPU 卡数"]
D1 --> D1b["hami.io/gpu-memory - 显存 MB"]
D1 --> D1c["hami.io/gpu-core - 算力百分比"]
D1 --> D1d["hami.io/gpu-memory-percentage"]
A --> E["4. kubelet 注册为 Extended Resource"]
E --> E1["Node.Status.Capacity: hami.io/gpu-memory: 16384"]
A --> F["5. Allocate 回调"]
F --> F1["容器创建前,挂载 libvgpu.so 并通过环境变量注入配置"]
上报资源示例:
# kubectl describe node gpu-node-01
Capacity:
hami.io/gpu: 8
hami.io/gpu-core: 800
hami.io/gpu-memory: 131072 # 单位 MiB,8×16GB
hami.io/gpu-memory-percentage: 800
nvidia.com/gpu: 8
Allocatable:
hami.io/gpu: 8
hami.io/gpu-core: 800
hami.io/gpu-memory: 126848
hami.io/gpu-memory-percentage: 800
每个物理 GPU 被抽象为 100 个算力单位(hami.io/gpu-core: 100)和其全部显存(hami.io/gpu-memory)。
5 HAMI-scheduler 的调度扩展原理是什么?
答案:
HAMI-scheduler 以 Kubernetes Scheduler Extender 模式运行,部署为独立 Service,在 Kube-scheduler 配置中注册为外部扩展器,对 GPU 资源的 Pod 执行 Filter 和 Prioritize 阶段的二次调度逻辑。
架构流程:
graph TD
A[Kube-scheduler] --> B["默认调度阶段 - Filter / Score / Reserve"]
A --> C["遇到 hami.io/* 资源的 Pod"]
C --> D["HTTP POST → HAMI-scheduler Service"]
D --> E["Filter: 检查节点 hami 资源是否满足 Pod 请求"]
E --> E1["细粒度到单卡级别:该卡上的 gpu-memory 和 gpu-core 剩余量是否满足 Pod 声明的 limit"]
D --> F["Prioritize: 优先将 Pod 调度到已有分配的 GPU 卡上"]
F --> F1["减少 GPU 碎片化"]
A --> G["Bind: 调度成功的 Pod 绑定到节点"]
Kube-scheduler 配置示例:
apiVersion: kubescheduler.config.k8s.io/v1beta3
kind: KubeSchedulerConfiguration
extenders:
- urlPrefix: "http://hami-scheduler.kube-system:31993/scheduler"
filterVerb: filter
prioritizeVerb: prioritize
weight: 20
enableHTTPS: false
managedResources:
- name: hami.io/gpu
- name: hami.io/gpu-memory
- name: hami.io/gpu-core
ignorable: false
HAMI-scheduler 维护集群内每张 GPU 卡的实时分配状态(分配了哪些容器、剩余显存与算力),在 Filter 阶段排除无法满足 Pod 资源需求的节点,在 Prioritize 阶段通过装箱策略优先填满已部分使用的 GPU 卡。
6 HAMI 的 GPU 共享模式是如何让多个容器共享同一张 GPU 的?
答案:
HAMI 的核心共享模式是将一张物理 GPU 拆分为多个虚拟 GPU(vGPU),每个 vGPU 拥有独立的显存限额与算力限额,通过环境变量和 LD_PRELOAD 注入的方式在容器内强制执行限制,从而允许多个容器同时使用同一张 GPU。
共享模型示意:
graph TD
A["Physical GPU: NVIDIA A100 80GB"] --> B["Container A: hami.io/gpu-memory: 30GB | hami.io/gpu-core: 40"]
A --> C["Container B: hami.io/gpu-memory: 25GB | hami.io/gpu-core: 35"]
A --> D["Container C: hami.io/gpu-memory: 20GB | hami.io/gpu-core: 25"]
B --> B1["Pod: inference-service-v1"]
C --> C1["Pod: inference-service-v2"]
D --> D1["Pod: dev-notebook"]
Pod 声明示例:
apiVersion: v1
kind: Pod
metadata:
name: gpu-shared-pod
spec:
containers:
- name: app
image: nvidia/cuda:12.1.0-runtime-ubuntu22.04
resources:
limits:
hami.io/gpu: 1
hami.io/gpu-memory: 8 # 8GB 显存
hami.io/gpu-core: 25 # 25% 算力
Device Plugin 在 Allocate 阶段的行为:
{
"envs": {
"LD_PRELOAD": "/usr/local/hami/libvgpu.so",
"CUDA_DEVICE_MEMORY_LIMIT": "8589934592",
"CUDA_DEVICE_SM_LIMIT": "25",
"CUDA_DEVICE_MEMORY_SHARED_CACHE": "/tmp/hami-cache"
},
"mounts": [
{
"containerPath": "/usr/local/hami/libvgpu.so",
"hostPath": "/usr/local/hami/libvgpu.so"
}
]
}
每个容器看到的仍然是完整的物理 GPU(nvidia-smi 显示全部显存),但 libvgpu.so 在 API 层面拦截并限制实际可用资源,每个容器的限制彼此独立、互不干扰。
7 HAMI 的 Helm 安装包含哪些核心组件与参数?
答案:
HAMI 通过 Helm Chart 部署,核心组件包括:HAMI Device Plugin(DaemonSet)、HAMI Scheduler(Deployment)、HAMI Webhook(可选,用于自动注入环境变量)。
核心 Helm 参数:
helm repo add hami-charts https://project-hami.github.io/HAMi/charts
helm install hami hami-charts/hami \
--namespace kube-system \
--set devicePlugin.image.tag=v2.4.0 \
--set scheduler.kubeScheduler.image.tag=v2.4.0 \
--set devicePlugin.devices.nvidia.resourceCountName=hami.io/gpu \
--set devicePlugin.devices.nvidia.resourceMemoryName=hami.io/gpu-memory \
--set devicePlugin.devices.nvidia.resourceCoreName=hami.io/gpu-core \
--set scheduler.defaultMem=0 \
--set scheduler.defaultCores=0 \
--set scheduler.defaultGPUNum=1
核心组件清单:
| 组件 | 类型 | 功能 |
|---|---|---|
| hami-device-plugin | DaemonSet | 注册 GPU 资源、上报虚拟化设备、挂载 libvgpu.so |
| hami-scheduler | Deployment | Scheduler Extender,细粒度 GPU 资源调度 |
| hami-webhook | Deployment(可选) | Mutating Webhook,自动注入 GPU 环境变量 |
| hami-metrics | Sidecar(可选) | 暴露 GPU 使用指标给 Prometheus |
关键配置参数说明:
# values.yaml 核心配置
devicePlugin:
devices:
nvidia:
enabled: true
resourceCountName: hami.io/gpu
resourceMemoryName: hami.io/gpu-memory
resourceCoreName: hami.io/gpu-core
resourceMemoryPercentageName: hami.io/gpu-memory-percentage
nodeSelector: {} # GPU 节点选择器
tolerations: [] # 容忍策略
scheduler:
enabled: true
schedulerName: hami-scheduler
defaultMem: 0 # 默认显存限制(0=容器未声明时不限制)
defaultCores: 0 # 默认算力限制
defaultGPUNum: 1 # 默认 GPU 数量
8 HAMI 如何实现 GPU 显存限制的精确控制?
答案:
HAMI 显存限制依赖于 CUDA 显存分配 API 的全量拦截。libvgpu.so 在进程初始化时读取环境变量 CUDA_DEVICE_MEMORY_LIMIT(单位为字节),在每次 cudaMalloc / cuMemAlloc / cudaMallocManaged 等显存分配调用时执行限额校验。
拦截点覆盖:
| CUDA API | 拦截方式 | 限制策略 |
|---|---|---|
cudaMalloc | LD_PRELOAD 符号覆盖 | 总分配量 + 本次请求 <= limit |
cudaMallocManaged | LD_PRELOAD 符号覆盖 | 同上 |
cuMemAlloc | LD_PRELOAD 符号覆盖 | 同上 |
cudaMallocPitch | LD_PRELOAD 符号覆盖 | 同上 |
cudaMalloc3D | LD_PRELOAD 符号覆盖 | 同上 |
cudaFree | LD_PRELOAD 符号覆盖 | 更新已用量计数器 |
显存限制精度:
# 容器内环境变量(由 device plugin Allocate 阶段注入)
CUDA_DEVICE_MEMORY_LIMIT=8589934592 # 8GB = 8 × 1024³
# 应用程序视角
# cudaMalloc(4GB) → 成功,已用 4GB / 8GB
# cudaMalloc(5GB) → 失败,返回 cudaErrorMemoryAllocation
# cudaFree(4GB) → 成功,已用 0GB / 8GB
HAMI 的显存限制为 硬限制,应用程序在超限时收到的错误码与 GPU 物理 OOM 一致(cudaErrorMemoryAllocation),应用层可按标准 CUDA 错误处理逻辑响应。libvgpu.so 不在显存层面做预留或超分(overcommit),声明的限制即为硬上限。
9 HAMI 的算力利用率限制如何配置?
答案:
HAMI 算力限制通过 hami.io/gpu-core 资源声明,每个物理 GPU 被抽象为 100 个算力单位。例如声明 hami.io/gpu-core: 30 即限制该容器占用 GPU 总 SM 时间的 30%。
Pod 配置:
apiVersion: v1
kind: Pod
metadata:
name: gpu-limited-pod
spec:
containers:
- name: training
image: nvidia/cuda:12.1.0-devel-ubuntu22.04
resources:
limits:
hami.io/gpu: 1
hami.io/gpu-memory: 16 # 16GB 显存
hami.io/gpu-core: 50 # 50% 算力
算力配置策略对比:
| 配置方式 | 场景 | 示例 |
|---|---|---|
| 固定百分比 | 明确知道工作负载的算力需求 | hami.io/gpu-core: 30 |
| 不限制 | 开发测试环境 | 不声明 gpu-core |
| 动态配置 | HAMI Webhook 自动注入 | 按 namespace annotation 注入默认值 |
| 优先级继承 | 生产环境 | 高优先级 Pod 获取更高算力上限 |
运行时行为:
算力限制 = 30(30% GPU SM 时间)
时间轴(每 1000ms 窗口):
[==30% 有效计算==|______70% 空闲______|==30% 有效计算==|...]
在 GPU 空闲时,容器可能获得超过 30% 的实际算力(soft limit 特性);当同一 GPU 上的其他容器也有负载时,libvgpu.so 的节流机制会将容器 SM 使用率限制在 30% 以内。
10 HAMI 与 nvidia-device-plugin 如何兼容工作?
答案:
HAMI 与 nvidia-device-plugin(NVIDIA 官方设备插件)共存于同一集群,HAMI 通过配置仅接管 GPU 节点的虚拟化资源上报,nvidia-device-plugin 仍可继续上报整卡资源(nvidia.com/gpu),两者通过不同的 Extended Resource 名称区分。
共存架构:
graph TD
A["Node: GPU Node"] --> B["nvidia-device-plugin"]
A --> C["HAMI device-plugin"]
B --> B1["上报: nvidia.com/gpu - 整卡,用于非共享场景"]
C --> C1["上报: hami.io/gpu, hami.io/gpu-memory, hami.io/gpu-core - 虚拟化,用于共享场景"]
HARM 配置文件中的共存设置:
# HAMI values.yaml
devicePlugin:
devices:
nvidia:
enabled: true
# 关键:不与 nvidia-device-plugin 争夺 nvidia.com/gpu
resourceCountName: hami.io/gpu
# 保留原始 nvidia.com/gpu 资源给非 HAMI 的 Pod
disableNvidiaResource: false
使用场景分流:
| Pod 类型 | 资源声明 | 插件处理 |
|---|---|---|
| 整卡独占 Pod | nvidia.com/gpu: 1 | nvidia-device-plugin |
| GPU 共享 Pod | hami.io/gpu: 1 + hami.io/gpu-memory: 8 | HAMI device-plugin |
注意事项:
HAMI device plugin 不应接管 nvidia.com/gpu 资源的注册(通过 disableNvidiaResource 或资源名配置控制),避免与 nvidia-device-plugin 冲突。在同一节点上,物理 GPU 的整卡资源与虚拟化资源互斥:分配给 HAMI Pod 的 GPU 卡上的 nvidia.com/gpu 资源应从节点 Capacity 中扣除对应数量。
11 HAMI 对 CUDA 应用的兼容性如何?哪些 CUDA 特性可能受限?
答案:
HAMI 通过 LD_PRELOAD 劫持 CUDA 运行时 API,兼容主流 CUDA 版本(CUDA 10.0 - 12.x),涵盖 PyTorch、TensorFlow、ONNX Runtime、TensorRT 等主流深度学习框架。限制主要出现在直接调用 CUDA Driver API 的场景或涉及 GPU 间通信的高级特性。
兼容性矩阵:
| 特性 | 兼容状态 | 说明 |
|---|---|---|
| cudaMalloc / cudaFree | 完全兼容 | 核心显存分配 API |
| cudaLaunchKernel | 完全兼容 | 核心 Kernel 启动 API |
| cudaMemcpy (Host-Device) | 完全兼容 | 不拦截,透传 |
| cuBLAS / cuDNN | 完全兼容 | 基于上述 API,间接兼容 |
| NCCL(单卡) | 兼容 | 不涉及 P2P / RDMA 时 |
| NCCL(多卡跨节点) | 兼容 | 通信不走 HAMI |
| NCCL(同节点 P2P) | 受限 | 共享 GPU 场景下 P2P 受限 |
| CUDA IPC | 不支持 | 共享 GPU 下进程间显存共享受限 |
| MPS(Multi-Process Service) | 不支持 | 与 HAMI 共享模式冲突 |
| CUDA Graph | 部分兼容 | 静态图捕获时算力控制可能不精确 |
| GPU Direct RDMA | 依赖硬件 | HAMI 不介入,取决于 InfiniBand 配置 |
框架兼容性验证命令:
# 验证 PyTorch 兼容性
kubectl run torch-test --image=pytorch/pytorch:2.0.1-cuda11.7-cudnn8-runtime \
--restart=Never --rm -it -- \
python -c "import torch; print(torch.cuda.is_available()); print(torch.cuda.get_device_name(0))"
# 验证 TensorFlow 兼容性
kubectl run tf-test --image=tensorflow/tensorflow:2.13.0-gpu \
--restart=Never --rm -it -- \
python -c "import tensorflow as tf; print(tf.config.list_physical_devices('GPU'))"
典型受限场景(CUDA IPC):
涉及 cudaIpcGetMemHandle / cudaIpcOpenMemHandle 的程序在同一 GPU 的多个 HAMI 容器间无法共享显存指针,因为每个容器的虚拟地址空间被 libvgpu.so 隔离。
12 HAMI 的资源上报格式有哪些?各自含义是什么?
答案:
HAMI 定义了一套 Extended Resource 命名体系,以 hami.io/ 为前缀,覆盖 GPU 卡数、显存、算力三个维度。
资源类型总览:
| 资源名 | 单位 | 含义 | 示例值 |
|---|---|---|---|
hami.io/gpu | 张 | 虚拟 GPU 卡数 | 1 |
hami.io/gpu-memory | MiB | 显存限额 | 8192(8GB) |
hami.io/gpu-core | % | 算力百分比(0-100/卡) | 30 |
hami.io/gpu-memory-percentage | % | 显存百分比 | 25 |
节点 Capacity 视角:
物理 GPU: 8 × NVIDIA A100 80GB
节点上报:
hami.io/gpu: 800 # 8 张 × 100 = 800 虚拟卡
hami.io/gpu-memory: 655360 # 8 × 80GB = 640GB = 655360 MiB
hami.io/gpu-core: 800 # 8 × 100 = 800 算力单位
hami.io/gpu-memory-percentage: 800 # 8 × 100 = 800 百分比单位
Pod 层面声明:
# 声明方式一:显存绝对值
resources:
limits:
hami.io/gpu: 1
hami.io/gpu-memory: 4096 # 4GB
# 声明方式二:显存百分比
resources:
limits:
hami.io/gpu: 1
hami.io/gpu-memory-percentage: 25 # 25% 的总显存
# 声明方式三:算力限制
resources:
limits:
hami.io/gpu: 1
hami.io/gpu-core: 50 # 50% 算力
hami.io/gpu-memory 和 hami.io/gpu-memory-percentage 两种声明方式二选一,device plugin 在 Allocate 阶段将其统一转换为字节数注入环境变量。hami.io/gpu-core 和 hami.io/gpu-memory 的 Sum 必须与 hami.io/gpu 保持逻辑一致(每张虚拟 GPU 卡 = 100 算力 + 全部物理卡显存)。
13 HAMI 如何与 Volcano 调度器集成?
答案:
HAMI 与 Volcano 的集成通过 Volcano 的 Device Plugin 机制 + HAMI-scheduler 作为 Volcano 的调度插件实现。Volcano 负责批量任务编排与队列管理,HAMI 负责 GPU 资源的细粒度分配。
集成架构:
graph TD
A[Volcano Scheduler] --> B["Session - 调度会话"]
B --> B1["Queue / JobOrder / TaskOrder 排序"]
B --> B2["Gang Scheduling 检查"]
B --> C["HAMI Device Plugin - Volcano 侧"]
C --> D["调用 HAMI-scheduler API"]
D --> D1["查询节点 GPU 卡剩余资源 - 显存 + 算力"]
D --> D2["决策 Pod 分配到哪张物理 GPU"]
A --> E["Bind: 分配到具体节点"]
Volcano Job 声明 HAMI 资源:
apiVersion: batch.volcano.sh/v1alpha1
kind: Job
metadata:
name: distributed-training
spec:
minAvailable: 4
schedulerName: volcano
queue: gpu-queue
tasks:
- replicas: 4
name: worker
template:
spec:
containers:
- name: pytorch
image: pytorch/pytorch:2.0.1-cuda11.7-cudnn8-devel
resources:
limits:
hami.io/gpu: 1
hami.io/gpu-memory: 16
hami.io/gpu-core: 50
关键配置:
# HAMI 启用 Volcano 集成
scheduler:
enabled: true
volcanoEnabled: true
# Scheduler Extender URL 同时注册到 Kube-scheduler 和 Volcano
集成后 Volcano 的 Gang Scheduling 确保分布式训练的所有 Worker 同时获得 GPU 资源,HAMI 在此基础上确保每个 Worker 获得指定显存与算力。
14 HAMI 如何处理 GPU 拓扑感知调度?
答案:
HAMI 的拓扑感知调度基于 NVML Topology API 获取节点 GPU 拓扑信息(PCIe 拓扑、NVLink 连接关系),在调度决策中优先将需要高带宽通信的 Pod 调度到 NVLink 直接连接的 GPU 上。
拓扑感知调度策略:
graph TD
A["Node: 8 × A100"] --> B["NVSwitch 互联拓扑"]
B --> B1["GPU0 ←──NVLink 600GB/s──→ GPU1"]
B --> B2["GPU0 ←──NVLink 600GB/s──→ GPU2"]
B --> B3["GPU0 ←── PCIe Switch ──→ GPU4 - 较低带宽"]
A --> C["HAMI 调度决策"]
C --> C1["任务请求 2 卡 → 优先分配 GPU0 + GPU1 - NVLink 直连"]
C --> C2["任务请求 2 卡 → 若无 NVLink 组合,退而求其次分配 PCIe Switch 内 GPU"]
Pod 声明拓扑偏好:
apiVersion: v1
kind: Pod
metadata:
name: nvlink-training
annotations:
hami.io/gpu-topology-policy: "nvlink-first" # 优先 NVLink 拓扑
spec:
containers:
- name: training
resources:
limits:
hami.io/gpu: 2
hami.io/gpu-memory: 32
拓扑策略选项:
| 策略 | 行为 |
|---|---|
nvlink-first | 优先选择 NVLink 直连的 GPU 对 |
nvlink-only | 仅选择 NVLink 直连的 GPU(无满足条件的节点时调度失败) |
balanced | 均匀分布到不同 NUMA / PCIe 域(避免竞争) |
none | 不做拓扑感知(默认) |
拓扑信息由 device plugin 在启动时通过 NVML 采集并上报给 scheduler,scheduler 维护 GPU 拓扑图(graph),在 Filter 阶段根据拓扑策略过滤节点,在 Score 阶段为满足偏好拓扑的节点赋予更高分数。
15 HAMI 提供哪些监控指标?如何与 Prometheus 集成?
答案:
HAMI 通过内嵌的 metrics sidecar 暴露 GPU 使用率、显存用量、算力占用等指标,格式兼容 Prometheus text format,可由 Prometheus Operator 的 ServiceMonitor 或 PodMonitor 自动抓取。
核心指标:
| 指标名 | 类型 | 含义 | 标签 |
|---|---|---|---|
hami_gpu_memory_used_bytes | Gauge | 容器 GPU 显存使用量 | pod, namespace, gpu_id |
hami_gpu_memory_limit_bytes | Gauge | 容器 GPU 显存限额 | pod, namespace, gpu_id |
hami_gpu_core_utilization | Gauge | GPU 算力利用率(%) | pod, namespace, gpu_id |
hami_gpu_core_limit | Gauge | 容器算力限额 | pod, namespace, gpu_id |
hami_gpu_utilization | Gauge | 物理 GPU 总利用率 | gpu_id, node |
hami_gpu_memory_total_bytes | Gauge | 物理 GPU 总显存 | gpu_id, node |
hami_gpu_shared_count | Gauge | 物理 GPU 上共享容器数 | gpu_id, node |
hami_gpu_oom_events_total | Counter | 显存 OOM 事件数 | pod, namespace, gpu_id |
Prometheus 集成配置:
# PodMonitor 示例
apiVersion: monitoring.coreos.com/v1
kind: PodMonitor
metadata:
name: hami-metrics
namespace: kube-system
spec:
selector:
matchLabels:
app: hami-device-plugin
podMetricsEndpoints:
- port: metrics
interval: 30s
path: /metrics
常用 PromQL 查询:
# 每张物理 GPU 上的容器数
hami_gpu_shared_count
# 容器显存使用率
hami_gpu_memory_used_bytes / hami_gpu_memory_limit_bytes * 100
# 物理 GPU 总显存使用率
sum by (gpu_id) (hami_gpu_memory_used_bytes) / hami_gpu_memory_total_bytes * 100
# GPU 算力超限检测(容器算力利用率超过其限额 120%)
hami_gpu_core_utilization > hami_gpu_core_limit * 1.2
Grafana 仪表盘可按 GPU 维度(总览)、Pod 维度(单容器视角)、节点维度(集群视图)三种层级组织面板,分别监控碎片化程度、单容器 SLA 和集群利用率趋势。
16 HAMI 在生产环境中有哪些最佳实践?
答案:
资源粒度控制:
# 推荐的最小资源粒度(A100 80GB)
# 显存: 4GB 起,以 2GB 为步长
# 算力: 10% 起,以 5% 为步长
resources:
limits:
hami.io/gpu: 1
hami.io/gpu-memory: 8 # 8GB
hami.io/gpu-core: 20 # 20%
节点分组与 GPU 型号选择:
# 使用 nodeSelector 将不同型号 GPU 分组
apiVersion: v1
kind: Pod
spec:
nodeSelector:
hami.io/gpu-model: "a100-80gb"
containers:
- resources:
limits:
hami.io/gpu: 1
设置默认资源限制(防止未声明的 Pod 占用整卡):
# HAMI Helm values
scheduler:
defaultMem: 4096 # 默认显存 4GB
defaultCores: 10 # 默认算力 10%
defaultGPUNum: 1
资源超分配监控告警:
# Prometheus 告警规则
groups:
- name: hami
rules:
- alert: HAMIGPUOversubscribed
expr: hami_gpu_shared_count > 8
for: 5m
labels:
severity: warning
annotations:
summary: "GPU {{ $labels.gpu_id }} 共享容器数超过 8"
- alert: HAMIGPUMemoryHigh
expr: sum by (gpu_id) (hami_gpu_memory_used_bytes) / hami_gpu_memory_total_bytes > 0.95
for: 1m
labels:
severity: critical
annotations:
summary: "GPU {{ $labels.gpu_id }} 显存使用率超过 95%"
网卡与 NUMA 亲和性:
# 训练任务绑定高速网卡所在 NUMA 域的 GPU
spec:
containers:
- resources:
limits:
hami.io/gpu: 4
volumeMounts:
- name: nvidia-peermem
mountPath: /dev/infiniband
命名空间级别资源配额:
apiVersion: v1
kind: ResourceQuota
metadata:
name: gpu-quota
namespace: ml-team
spec:
hard:
hami.io/gpu-memory: 160 # 160GB 总显存
hami.io/gpu-core: 200 # 200% 总算力(2 张卡)
17 HAMI vs MIG vs Time-Slicing 三者如何对比?各自的适用场景是什么?
答案:
| 维度 | HAMI | NVIDIA MIG | Time-Slicing |
|---|---|---|---|
| 隔离级别 | 显存硬隔离 + 算力软隔离 | 显存/算力/缓存/带宽全硬隔离 | 无隔离(仅时分复用) |
| GPU 要求 | 所有 NVIDIA GPU | A100 / A30 / H100 等 MIG 支持型号 | 所有 NVIDIA GPU |
| 显存保护 | 是(cudaMalloc 限制) | 是(硬件分区) | 否(无限制,互相干扰) |
| 算力保护 | 部分(时间片节流) | 是(SM 硬件隔离) | 否 |
| 故障隔离 | 部分(OOM 不影响其他容器) | 是(MIG 实例完全独立) | 否 |
| 分片粒度数 | 理论无上限 | 最多 7 个 MIG 实例(A100) | 受 GPU 上下文限制 |
| 配置复杂度 | 中(Helm 安装 + Pod 声明) | 高(GPU 模式切换 + 重启) | 低(device-plugin 配置) |
| 动态调整 | 支持(Pod 重建) | 不支持(需重置 GPU) | 不支持 |
| 性能开销 | 低(LD_PRELOAD 开销) | 无(硬件原生) | 无(原生) |
适用场景决策树:
graph TD
A["是否需要硬件级安全隔离 - 多租户?"] -->|是| B[MIG]
B --> B1["适用:SaaS 平台提供 GPU 服务、严格多租户隔离"]
A -->|否| C["是否需要显存硬限制?"]
C -->|是| D[HAMI]
D --> D1["适用:推理服务共享 GPU、开发环境 GPU 复用"]
C -->|否| E["Time-Slicing"]
E --> E1["适用:开发测试、非关键批处理、GPU 空闲时段复用"]
HAMI 的独特优势在于不依赖特定 GPU 硬件型号,在 T4 / V100 / A10 等不支持 MIG 的 GPU 上也能实现显存隔离与算力软隔离,覆盖了 MIG 无法覆盖的 GPU 型号范围。
18 HAMI 如何通过节点亲和性选择特定型号的 GPU?
答案:
HAMI 通过 Kubernetes 原生 nodeSelector / nodeAffinity 机制结合 device plugin 上报的 GPU 型号标签,实现 Pod 对特定 GPU 型号的调度亲和性。
节点标签自动注册:
HAMI device plugin 启动时通过 NVML 读取 GPU 型号,自动为节点打上标签:
# HAMI 自动添加的节点标签
hami.io/gpu-model=NVIDIA-A100-SXM4-80GB
hami.io/gpu-memory-total=81920 # MiB
hami.io/gpu-count=8
hami.io/gpu-type=tesla
nvidia.com/gpu.product=NVIDIA-A100-SXM4-80GB
Pod 按 GPU 型号选择:
# 仅调度到 A100 GPU 节点
apiVersion: v1
kind: Pod
spec:
nodeSelector:
hami.io/gpu-model: "NVIDIA-A100-SXM4-80GB"
containers:
- resources:
limits:
hami.io/gpu: 1
# 排除特定型号(In 操作符的逆向)
apiVersion: v1
kind: Pod
spec:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: hami.io/gpu-model
operator: NotIn
values:
- "NVIDIA-T4"
- "NVIDIA-Tesla-K80"
containers:
- resources:
limits:
hami.io/gpu: 1
虚拟 GPU 类型的亲和性:
# 部署时通过 Helm 设置虚拟 GPU 类型标签
devicePlugin:
nodeLabels:
gpu-tier: "premium" # A100/H100 节点
gpu-tier: "standard" # V100/T4 节点
# Pod 选择
spec:
nodeSelector:
gpu-tier: "premium"
19 HAMI 如何处理显存 OOM 与超限场景?
答案:
HAMI 在显存超限时,通过 libvgpu.so 在 cudaMalloc 调用处拦截并返回 cudaErrorMemoryAllocation(错误码 2),应用层按标准 CUDA OOM 方式处理,不会导致内核态 GPU 异常或节点 crash。
OOM 处理流程:
graph TD
A["应用调用 cudaMalloc(size)"] --> B["libvgpu.so 检查: current_usage + size > limit ?"]
B -->|是| C["返回 cudaErrorMemoryAllocation"]
C --> C1["PyTorch: 抛出 RuntimeError - CUDA out of memory"]
C --> C2["TensorFlow: 抛出 ResourceExhaustedError"]
C --> C3["自定义 CUDA: 检查 cudaMalloc 返回值"]
B -->|否| D["透传到显卡驱动正常分配"]
与其他容器的隔离性:
HAMI 的 OOM 仅影响发起超限分配的容器自身,不会波及同一物理 GPU 上的其他容器。因为每个容器的 libvgpu.so 实例独立维护各自的显存使用计数器。但如果应用绕过 cudaMalloc(例如通过 CUDA Driver API 直接操作显存),则 HAMI 无法拦截,可能导致物理 GPU 级别的 OOM,影响同卡其他容器。
OOM 监控告警:
# HAMI 记录 OOM 事件的 counter 指标
rate(hami_gpu_oom_events_total[5m]) > 0
# Pod 显存使用接近上限
hami_gpu_memory_used_bytes / hami_gpu_memory_limit_bytes > 0.95
Pod 层面 OOM 表现:
# 容器内 nvidia-smi 显示(HAMI 修改过的 nvidia-smi)
+-----------------------------------------------------------------------------+
| Processes: |
| GPU GI CI PID Type Process name GPU Memory |
| ID ID Usage |
|=============================================================================|
| 0 N/A N/A 12345 C python 4096MiB |
| (limit: 8192MiB) |
+-----------------------------------------------------------------------------+
20 HAMI 支持哪些容器运行时?
答案:
HAMI 对容器运行时的依赖主要体现在 LD_PRELOAD 挂载和环境变量注入两个环节,理论上兼容所有符合 OCI 规范的容器运行时。
兼容性矩阵:
| 运行时 | 兼容状态 | 说明 |
|---|---|---|
| containerd | 完全兼容 | 推荐运行时,生产环境首选 |
| cri-o | 兼容 | Kubernetes 1.24+ 推荐 |
| Docker(dockershim) | 兼容 | Kubernetes 1.23 及以下版本 |
| nvidia-container-runtime | 完全兼容 | HAMI 与此运行时协同工作 |
containerd 配置要点:
# /etc/containerd/config.toml
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc]
runtime_type = "io.containerd.runc.v2"
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.nvidia]
runtime_type = "io.containerd.runc.v2"
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.nvidia.options]
BinaryName = "/usr/bin/nvidia-container-runtime"
HAMI 不依赖特定运行时的特有功能。libvgpu.so 的挂载通过 device plugin 的 Allocate 响应中的 mount 字段指定 hostPath,标准 OCI runtime spec 支持此挂载方式。环境变量注入同样通过 device plugin Allocate 响应的 envs 字段,由 kubelet 传递给容器运行时。
21 HAMI 如何进行版本升级?有哪些兼容性注意事项?
答案:
HAMI 升级涉及 device plugin(DaemonSet)、scheduler(Deployment) 和 libvgpu.so 三个组件,需按顺序升级以保持兼容性。
升级步骤:
# 1. 先升级 scheduler(无状态,可滚动更新)
kubectl set image deployment/hami-scheduler \
scheduler=projecthami/hami:v2.4.0 \
-n kube-system
# 2. 升级 device plugin DaemonSet(逐个节点)
kubectl set image daemonset/hami-device-plugin \
device-plugin=projecthami/hami:v2.4.0 \
-n kube-system
# 3. 确认 libvgpu.so 版本与 device plugin 匹配
# device plugin 容器内自带的 libvgpu.so 会自动挂载到宿主机路径
kubectl rollout status daemonset/hami-device-plugin -n kube-system
兼容性保障规则:
| 升级路径 | 兼容性 | 注意事项 |
|---|---|---|
| v2.3.x → v2.4.x | 兼容 | 先升 scheduler,再升 device plugin |
| v2.2.x → v2.4.x | 需评估 | 检查 CRD 变更和资源名变更 |
| libvgpu.so 版本 | 必须与 device plugin 一致 | 不同版本混合导致 CUDA API 拦截行为不一致 |
| CUDA 版本 | v2.4 支持 CUDA 11.0-12.x | 升级前验证目标 CUDA 版本 |
| Kubernetes 版本 | 支持 1.20 - 1.29 | 检查 API 弃用情况 |
回滚策略:
# 先回滚 device plugin
helm rollback hami -n kube-system
# 已运行的 Pod 继续使用旧版 libvgpu.so,不受影响
# 新建 Pod 将使用回滚后的 libvgpu.so
升级期间的行为:
- 已运行的 Pod 不受 scheduler 升级影响(scheduler 仅参与调度阶段)
- device plugin DaemonSet 滚动更新期间,该节点上已运行的 Pod 不受影响
- 新 Pod 在 device plugin 升级期间不可调度到对应节点(Allocate 不可用)
22 HAMI 如何实现多租户 GPU 资源隔离?
答案:
HAMI 的多租户隔离由 Namespace ResourceQuota + HAMI Webhook 注入默认限制 + 显存硬隔离 三层构成。
租户隔离架构:
graph TD
A[Cluster] --> B["tenant-a - namespace"]
A --> C["tenant-b - namespace"]
B --> B1["ResourceQuota: hami.io/gpu-memory=160GB, hami.io/gpu-core=400"]
B --> B2["Pod 声明 gpu-memory 后,由 Webhook 补全 gpu-core 默认值"]
C --> C1["ResourceQuota: hami.io/gpu-memory=80GB, hami.io/gpu-core=200"]
C --> C2["同上"]
ResourceQuota 配置:
apiVersion: v1
kind: ResourceQuota
metadata:
name: gpu-tenant-a-quota
namespace: tenant-a
spec:
hard:
requests.hami.io/gpu: 4
limits.hami.io/gpu: 4
limits.hami.io/gpu-memory: 160Gi
limits.hami.io/gpu-core: 400
limits.hami.io/gpu-memory-percentage: 400
apiVersion: v1
kind: LimitRange
metadata:
name: gpu-limit-range
namespace: tenant-a
spec:
limits:
- type: Container
default:
hami.io/gpu: 1
hami.io/gpu-memory: 8Gi
hami.io/gpu-core: 20
defaultRequest:
hami.io/gpu: 1
hami.io/gpu-memory: 4Gi
hami.io/gpu-core: 10
max:
hami.io/gpu-memory: 40Gi
hami.io/gpu-core: 80
min:
hami.io/gpu-memory: 2Gi
hami.io/gpu-core: 5
显存层面的硬隔离保证:
即使两个租户的 Pod 被调度到同一张物理 GPU 上,各自的 libvgpu.so 实例独立拦截 CUDa 显存分配调用,租户 A 的 Pod 无论如何无法使用超过其声明的 gpu-memory,也永远无法访问租户 B 的显存空间。
注意事项:
HAMI 不提供 GPU 数据清理(类似 MIG 的硬件隔离 + 显存归零),同一张物理 GPU 上的上一个容器释放显存后,残留数据可能对后续调度到同卡的容器构成信息泄露风险。对高安全等级的多租户场景,结合 MIG 或配合 NVIDIA Confidential Computing 特性使用。
23 HAMI 对训练工作负载有什么支持能力?
答案:
HAMI 对训练工作负载的核心价值在于 多卡训练场景下的 GPU 共享与碎片化消除,支持 PyTorch DDP、Horovod、DeepSpeed 等分布式训练框架。
训练工作负载典型配置:
apiVersion: v1
kind: Pod
metadata:
name: ddp-training-worker-0
annotations:
hami.io/gpu-topology-policy: "nvlink-first"
spec:
hostNetwork: true # NCCL 通信使用主机网络
containers:
- name: training
image: pytorch/pytorch:2.1.0-cuda12.1-cudnn8-devel
env:
- name: NCCL_DEBUG
value: "INFO"
- name: NCCL_SOCKET_IFNAME
value: "eth0"
- name: MASTER_ADDR
value: "ddp-training-master"
resources:
limits:
hami.io/gpu: 1
hami.io/gpu-memory: 32 # 32GB 显存
hami.io/gpu-core: 80 # 80% 算力(训练需要高算力)
支持特性矩阵:
| 训练特性 | 支持状态 | 说明 |
|---|---|---|
| 单节点多卡训练 | 支持 | 通过拓扑感知分配 NVLink 直连 GPU |
| 多节点分布式训练 | 支持 | NCCL 通信依赖 HostNetwork |
| Gradient Accumulation | 支持 | 不涉及 GPU 特性 |
| Mixed Precision(AMP) | 支持 | 显存和算力限制均正常生效 |
| Gradient Checkpointing | 支持 | 显存换计算,显存限制下有效 |
| DeepSpeed ZeRO | 部分支持 | ZeRO Stage 3 需大显存,HARM 共享模式下受限 |
| CUDA Graph(静态图加速) | 部分支持 | 图捕获期间算力控制精度下降 |
训练优化建议:
# 训练 Pod 建议 gpu-core 不低于 50
# 训练 Pod 建议 gpu-memory 设置为模型占用 + 20% 余量
# 建议使用 hostNetwork 减少 NCCL 通信延迟
# 建议配合 Volcano 使用 Gang Scheduling 保证 All-or-Nothing
24 HAMI 对推理工作负载有什么优化?
答案:
HAMI 对推理工作负载的核心优化点在于 高密度 GPU 共享与显存优先分配策略。推理服务通常显存需求大、算力需求低,HAMI 的细粒度显存 + 低算力配置可大幅提升 GPU 利用率。
推理服务典型配置:
# LLM 推理(70B 模型,INT4 量化,约需 35GB 显存)
apiVersion: apps/v1
kind: Deployment
metadata:
name: llama-inference
spec:
replicas: 2
template:
spec:
containers:
- name: vllm
image: vllm/vllm-openai:latest
args:
- --model=/models/llama-70b-int4
- --tensor-parallel-size=1
- --max-model-len=4096
resources:
limits:
hami.io/gpu: 1
hami.io/gpu-memory: 40 # 40GB 显存
hami.io/gpu-core: 15 # 15% 算力(推理算力需求低)
env:
- name: CUDA_DEVICE_MEMORY_LIMIT
value: "42949672960" # 40GB,由 HAMI 注入
推理场景优化策略:
| 策略 | 配置 | 效果 |
|---|---|---|
| 高密度部署 | gpu-core=10, gpu-memory=模型显存+2GB | 单 A100 80G 可承载 2 个 70B INT4 或 4 个 13B FP16 |
| 显存优先调度 | scheduler 装箱策略优先使用显存富余的 GPU | 减少显存碎片 |
| 空闲时 burst | 算力软限制,GPU 空闲时自动获得更多算力 | 冷启动和突发请求有更好延迟 |
| HPA 联动 | 基于 GPU 显存或请求延迟触发扩缩容 | 弹性匹配推理流量 |
推理场景典型共享密度:
graph TD
A["A100 80GB GPU"] --> B["推理实例 1: 40GB 显存, 15% 算力 - LLaMA-70B INT4"]
A --> C["推理实例 2: 20GB 显存, 10% 算力 - SDXL 图像生成"]
A --> D["推理实例 3: 16GB 显存, 10% 算力 - Whisper-large-v3"]
总计: 76GB / 80GB 显存, 35% / 100% 算力
25 HAMI 常见故障有哪些?如何诊断排查?
答案:
故障现象一:Pod 调度卡在 Pending 状态
# 排查步骤
kubectl describe pod <pod-name>
# 查看 Events,常见原因:
# 1. "0/10 nodes are available: Insufficient hami.io/gpu-memory"
# → GPU 显存不足,HAMIscheduler Filter 阶段排除所有节点
# 2. "0/10 nodes are available: Insufficient hami.io/gpu-core"
# → GPU 算力不足
# 检查节点资源
kubectl get nodes -o custom-columns=NAME:.metadata.name,\
GPU-MEMORY:.status.allocatable.hami\.io/gpu-memory,\
GPU-CORE:.status.allocatable.hami\.io/gpu-core
# 检查 scheduler 日志
kubectl logs -n kube-system deployment/hami-scheduler --tail=50
故障现象二:容器内 nvidia-smi 无输出或报错
# 原因:libvgpu.so 未正确挂载或 LD_PRELOAD 未生效
# 验证:
kubectl exec <pod> -- env | grep LD_PRELOAD
# 预期输出: LD_PRELOAD=/usr/local/hami/libvgpu.so
kubectl exec <pod> -- ls -la /usr/local/hami/libvgpu.so
# 预期:文件存在
# 如果 libvgpu.so 缺失,检查 device plugin Pod 日志
kubectl logs -n kube-system daemonset/hami-device-plugin --tail=100
故障现象三:应用报 CUDA OOM 但实际显存低于 limit
# 原因:CUDA context 开销 + cuDNN/cuBLAS 内部缓存
# libvgpu.so 统计的是应用通过 cudaMalloc 显式分配的显存
# CUDA context 和框架内部缓存不计入 limit 但会消耗物理显存
# 缓解措施:
# 1. 给 limit 预留 10-15% 余量
# 2. 设置框架减少缓存
# PyTorch: export PYTORCH_CUDA_ALLOC_CONF=max_split_size_mb:128
# TensorFlow: tf.config.experimental.set_memory_growth(gpu, True)
故障现象四:device plugin 反复重启
# 检查 nvidia-driver 版本
kubectl logs -n kube-system daemonset/hami-device-plugin | grep -i "driver\|nvidia\|nvml"
# NVML 初始化失败的常见原因:
# 1. nvidia-driver 未安装或版本过低(要求 >= 450.80.02)
# 2. /dev/nvidia* 设备文件缺失
# 3. nvidia-container-toolkit 未正确配置
# 验证节点基础 GPU 环境
kubectl run gpu-test --image=nvidia/cuda:12.1.0-base-ubuntu22.04 \
--restart=Never --rm -it --overrides='{"spec":{"nodeName":"<gpu-node>"}}' \
-- nvidia-smi
故障诊断汇总检查表:
| 检查项 | 命令 | 预期结果 |
|---|---|---|
| device plugin 状态 | kubectl get ds -n kube-system hami-device-plugin | READY |
| scheduler 状态 | kubectl get deploy -n kube-system hami-scheduler | READY |
| 节点资源上报 | kubectl describe node <node> | hami.io/* 字段存在 |
| libvgpu.so 挂载 | kubectl exec <pod> -- ls /usr/local/hami/ | libvgpu.so |
| LD_PRELOAD | kubectl exec <pod> -- env | LD_PRELOAD 设置正确 |
| GPU 驱动 | kubectl exec <pod> -- nvidia-smi | 正常输出 |
26 HAMI 中 Pod 优先级与抢占对 GPU 资源的影响是怎样的?
答案:
HAMI 的 Pod 优先级与抢占机制遵循 Kubernetes 原生 PriorityClass + Preemption 框架,结合 HAMI-scheduler 的 GPU 状态感知,在高优先级 Pod 无法调度时触发低优先级 Pod 驱逐,释放 GPU 资源。
PriorityClass 定义:
apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
name: gpu-training-high
value: 10000
globalDefault: false
description: "高优先级分布式训练任务"
apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
name: gpu-inference-medium
value: 5000
globalDefault: false
description: "中等优先级推理服务"
apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
name: gpu-dev-low
value: 1000
globalDefault: false
description: "低优先级开发测试"
HAMI 抢占决策流程:
graph TD
A["高优先级 Pod 进入调度队列"] --> B["Kube-scheduler Filter 阶段通过"]
A --> C["HAMI-scheduler Filter: 检查节点 GPU 卡剩余资源"]
C --> D["有足够资源"]
D --> D1["正常调度"]
C --> E["资源不足"]
E --> F["进入 Preemption 评估"]
F --> G["评估同节点上低优先级 Pod 占用的 GPU 资源"]
G --> H["被抢占 Pod 释放的 gpu-memory + gpu-core 是否满足需求?"]
H -->|是| I["驱逐低优先级 Pod → 调度高优先级 Pod"]
H -->|否| J["继续评估下一个候选节点"]
F --> K["无满足条件的节点 → Pending"]
GPU 资源释放时间:
# 抢占后 GPU 资源释放存在延迟
# 1. Pod 驱逐(terminationGracePeriodSeconds,默认 30s)
# 2. 容器停止 → libvgpu.so 卸载 → 显存清理(CUDA context 销毁)
# 3. HAMI device plugin 感知并上报更新后的 GPU 状态
# (ListAndWatch 默认健康检查间隔,取决于 kubelet 轮询周期)
# 总延迟:通常 30s - 60s
HARM 抢占注意事项:
显存清理依赖应用进程正常退出时的 CUDA context 销毁。如果被抢占的应用进程在收到 SIGTERM 后没有正常执行 cudaFree 或 cuCtxDestroy,物理显存可能短期内不会完全释放(CUDA driver 层面的延迟回收)。对显存敏感的场景,建议 terminationGracePeriodSeconds 设置为 60 秒以上,给应用足够时间优雅关闭。
27 HAMI GPU 共享场景下的性能干扰(Noisy Neighbor)如何评估与控制?
答案:
HAMI 共享模式下,同一物理 GPU 上的容器共享 GPU 显存带宽、L2 Cache 和 DRAM 带宽等资源,形成性能干扰(Noisy Neighbor),主要来源于显存带宽争抢和计算单元竞争。
干扰来源分析:
GPU 共享资源竞争模型:
容器 A 容器 B
│ │
├─ SM (计算单元) ├─ SM (计算单元)
│ HAMI 算力节流控制 │ HAMI 算力节流控制
│ │
├─ L2 Cache ──争抢──── L2 Cache ← 无隔离
│ │
├─ HBM 带宽 ──争抢─── HBM 带宽 ← 无隔离
│ │
└─ PCIe/NVLink ─争抢─ PCIe/NVLink ← 部分隔离(拓扑感知)
干扰量化评估方法:
# 1. 单容器独占 GPU 时跑基准测试
kubectl run benchmark-standalone --image=nvidia/cuda:12.1.0-devel-ubuntu22.04 \
--overrides='{"spec":{"nodeSelector":{"hami.io/gpu-model":"NVIDIA-A100-SXM4-80GB"}}}' \
-- /workspace/benchmark --iterations=1000
# 2. 共享 GPU 场景下同时运行两个容器
kubectl run benchmark-shared-a --resources='limits={hami.io/gpu=1,hami.io/gpu-memory=40,hami.io/gpu-core=50}' \
-- /workspace/benchmark --iterations=1000 &
kubectl run benchmark-shared-b --resources='limits={hami.io/gpu=1,hami.io/gpu-memory=40,hami.io/gpu-core=50}' \
-- /workspace/benchmark --iterations=1000 &
# 3. 对比吞吐量下降比例:干扰系数 = (throughput_shared - throughput_standalone) / throughput_standalone
控制与缓解策略:
| 策略 | 方法 | 效果 |
|---|---|---|
| 算力上限控制 | 设置合理的 gpu-core 上限 | 直接限制计算单元竞争 |
| 同质化调度 | 将相似工作负载调度到同一 GPU | 减少异构负载干扰 |
| 反亲和调度 | 延迟敏感服务使用 PodAntiAffinity | 避免核心服务受干扰 |
| GPU 独占标记 | 关键服务声明 gpu-core=100 | 独占模式消除干扰 |
| MIG 辅助 | 对 A100/H100 结合 MIG 硬件隔离 | 从硬件层面消除共享资源争抢 |
Prometheus 监控干扰指标:
# 同一 GPU 上容器间算力利用率标准差(高值 = 不均匀 = 存在干扰)
stddev by (gpu_id) (hami_gpu_core_utilization)
# 隔离效率 = 单容器算力利用率 / 声明算力占比
# 值 < 1 表示存在性能干扰
hami_gpu_core_utilization / hami_gpu_core_limit
28 HAMI 有哪些安全配置?
答案:
HAMI 的安全配置涉及 Pod Security、RBAC 控制 GPU 资源访问、LD_PRELOAD 安全风险管控 三个层面。
Pod Security 配置:
apiVersion: v1
kind: Pod
metadata:
name: gpu-secure-pod
spec:
securityContext:
runAsNonRoot: true
runAsUser: 1000
seccompProfile:
type: RuntimeDefault
containers:
- name: app
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop:
- ALL
readOnlyRootFilesystem: false # CUDA 需要 tmp 写入
resources:
limits:
hami.io/gpu: 1
hami.io/gpu-memory: 8
RBAC 限制 GPU 资源使用:
# 仅允许特定 ServiceAccount 使用 HAMI GPU 资源
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: hami-gpu-user
rules:
- apiGroups: [""]
resources: ["pods"]
verbs: ["create"]
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingWebhookConfiguration
metadata:
name: gpu-resource-validator
webhooks:
- name: gpu-validator.k8s.io
rules:
- operations: ["CREATE", "UPDATE"]
apiGroups: [""]
apiVersions: ["v1"]
resources: ["pods"]
clientConfig:
service:
name: gpu-validator
namespace: kube-system
LD_PRELOAD 安全防护:
# device plugin 部署时限制权限
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: hami-device-plugin
spec:
template:
spec:
containers:
- name: device-plugin
securityContext:
readOnlyRootFilesystem: true
allowPrivilegeEscalation: false
volumeMounts:
- name: lib-dir
mountPath: /usr/local/hami
readOnly: true # libvgpu.so 只读挂载
volumes:
- name: lib-dir
hostPath:
path: /usr/local/hami
type: DirectoryOrCreate
GPU 显存数据安全注意事项:
HAMI 不执行 GPU 显存归零操作。容器释放显存后,物理显存中的数据残留对下一次分配到同 GPU 的容器构成潜在信息泄露风险。高安全场景需配合以下措施:
# 1. 只在独占 GPU 上运行敏感工作负载(声明 gpu-core=100)
# 2. 在应用退出前执行显存清零(cudaMemset)
# 3. 评估结合 MIG 硬件隔离 + NVIDIA Confidential Computing
29 HAMI 的监控与日志诊断体系如何搭建?
答案:
HAMI 的监控诊断体系由三层构成:HAMI 内置 metrics(GPU 使用指标)、HAMI 组件日志(device plugin + scheduler 运行日志)、Prometheus + Grafana + Loki(指标集中采集、可视化、日志聚合)。
架构总览:
graph TD
subgraph grafana["Grafana 仪表盘"]
G1["GPU 集群总览 - 利用率/碎片/共享密度"]
G2["单 GPU 卡详情 - 显存分配图/算力分布"]
G3["Pod 级别 GPU 监控 - 显存使用曲线/算力曲线"]
G4["HAMI 组件健康状态 - device plugin / scheduler"]
end
subgraph prom["Prometheus - metrics"]
P1[metrics 采集]
end
subgraph loki["Loki - logs"]
L1[logs 聚合]
end
subgraph hami["HAMI 组件"]
H1["device-plugin - metrics + logs"]
H2["scheduler - metrics + logs"]
H3["libvgpu.so - Pod 内 metrics"]
end
prom --> grafana
loki --> grafana
hami --> prom
hami --> loki
关键告警规则:
# prometheus-rules.yaml
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
name: hami-alerts
namespace: kube-system
spec:
groups:
- name: hami.gpu
rules:
- alert: HAMIGPUMemoryHighUsage
expr: (hami_gpu_memory_used_bytes / hami_gpu_memory_total_bytes) > 0.95
for: 5m
labels:
severity: critical
annotations:
summary: "GPU 显存使用率 > 95%"
- alert: HAMIGPUOOMFrequency
expr: rate(hami_gpu_oom_events_total[10m]) > 0.1
for: 5m
labels:
severity: warning
annotations:
summary: "GPU OOM 事件频率异常"
- alert: HAMIDevicePluginDown
expr: up{job="hami-device-plugin"} == 0
for: 3m
labels:
severity: critical
- alert: HAMISchedulerDown
expr: up{job="hami-scheduler"} == 0
for: 3m
labels:
severity: critical
- alert: HAMIGPUHighDensity
expr: hami_gpu_shared_count > 10
for: 10m
labels:
severity: warning
annotations:
summary: "GPU 共享容器数超过 10,Noisy Neighbor 风险"
- alert: HAMIGPUMemoryFragmentation
expr: |
(sum by (gpu_id) (hami_gpu_memory_limit_bytes) - sum by (gpu_id) (hami_gpu_memory_used_bytes))
/ hami_gpu_memory_total_bytes < 0.05
and hami_gpu_shared_count < 3
for: 5m
labels:
severity: info
annotations:
summary: "GPU 显存碎片化(空闲少但容器少 = 显存碎片)"
组件日志诊断命令:
# device plugin 日志
kubectl logs -n kube-system -l app=hami-device-plugin --tail=100 \
| grep -E "ERROR|WARN|FATAL"
# scheduler 日志
kubectl logs -n kube-system deployment/hami-scheduler --tail=100 \
| grep -E "filter|allocate|error"
# 特定 Pod 的 GPU 事件(从 device plugin 日志关联)
kubectl logs -n kube-system -l app=hami-device-plugin \
| grep "<pod-uid>"
# Loki 日志查询(HAMI GPU OOM 事件)
{app="hami-device-plugin"} |= "OOM"
30 HAMI 与 GPU Operator 如何协同部署?
答案:
HAMI 与 GPU Operator 的协同部署方案:GPU Operator 负责自动化安装和维护 NVIDIA 驱动、nvidia-container-toolkit、nvidia-device-plugin 等基础组件;HAMI 在此基础之上部署,接管 GPU 的虚拟化资源共享能力。
部署顺序与分工:
graph TD
A["步骤 1: GPU Operator - NVIDIA 官方 Helm Chart"]
A --> A1["NVIDIA Driver - DaemonSet,自动编译安装"]
A --> A2["nvidia-container-toolkit - DaemonSet"]
A --> A3["nvidia-device-plugin - DaemonSet,上报 nvidia.com/gpu"]
A --> A4["dcgm-exporter - DaemonSet,GPU 指标采集"]
A --> A5["GPU Feature Discovery - 节点标签自动注册"]
B["步骤 2: HAMI - 4Paradigm Helm Chart"]
B --> B1["HAMI device plugin - DaemonSet,上报 hami.io/* 资源"]
B --> B2["HAMI scheduler - Deployment,Scheduler Extender"]
B --> B3["libvgpu.so - 挂载到 /usr/local/hami/"]
C["步骤 3: Prometheus + Grafana - 可选"]
C --> C1["统一采集 DCGM + HAMI 指标"]
协同部署配置:
# 1. 安装 GPU Operator
helm repo add nvidia https://helm.ngc.nvidia.com/nvidia
helm install gpu-operator nvidia/gpu-operator \
--namespace gpu-operator \
--create-namespace \
--set driver.enabled=true \
--set toolkit.enabled=true \
--set devicePlugin.enabled=true \
--set dcgmExporter.enabled=true
# 2. 安装 HAMI
helm repo add hami https://project-hami.github.io/HAMi/charts
helm install hami hami/hami \
--namespace kube-system \
--set devicePlugin.devices.nvidia.resourceCountName=hami.io/gpu \
--set devicePlugin.devices.nvidia.resourceMemoryName=hami.io/gpu-memory \
--set devicePlugin.devices.nvidia.resourceCoreName=hami.io/gpu-core \
--set scheduler.kubeScheduler.image.tag=v2.4.0 \
--set devicePlugin.nodeSelector."nvidia\.com/gpu\.present"="true"
组件职责划分:
| 组件 | 部署方 | 资源名 | 场景 |
|---|---|---|---|
| nvidia-device-plugin | GPU Operator | nvidia.com/gpu | 整卡独占 Pod |
| HAMI device plugin | HAMI | hami.io/gpu / hami.io/gpu-memory / hami.io/gpu-core | GPU 共享 Pod |
| nvidia-container-toolkit | GPU Operator | 通用 | 所有 GPU 容器的基础运行时支持 |
| dcgm-exporter | GPU Operator | DCGM 指标 | GPU 硬件层级监控 |
| HAMI metrics | HAMI | HAMI 指标 | vGPU 层级监控 |
节点初始化流程(GPU Operator + HAMI 完成后的节点状态):
graph TD
A["GPU Node 最终状态"] --> B["NVIDIA Driver: 535.xx"]
A --> C["nvidia-container-toolkit: running"]
A --> D["GPU Feature Discovery labels: 自动注册"]
A --> E["nvidia.com/gpu: capacity=8, allocatable=8"]
A --> F["hami.io/gpu: capacity=800, allocatable=800"]
A --> G["hami.io/gpu-memory: capacity=655360Mi, allocatable=640000Mi"]
A --> H["hami.io/gpu-core: capacity=800, allocatable=800"]
A --> I["DCGM metrics: 可用 - Prometheus 采集"]
A --> J["HAMI metrics: 可用 - Prometheus 采集"]
升级注意事项:
- GPU Operator 升级驱动可能触发节点重启,HAMI device plugin 需容忍节点 NotReady
- HAMI libvgpu.so 升级不影响已运行的 Pod,新启动的 Pod 自动使用新版
- DCGM exporter 和 HAMI metrics 指标互补:DCGM 提供硬件层级(温度/功耗/ECC),HAM 提供 vGPU 层级(容器级别显存/算力)
- GPU Operator 与 HAMI 需统一 CUDA 驱动大版本(例如都为 CUDA 12.x 驱动系列)
31 HAMI 在生产环境中的 CUDA 兼容性矩阵如何验证?Driver / Runtime / 框架版本如何联动?
答案:
HAMI 通过 LD_PRELOAD 劫持 CUDA Driver API,强依赖 libvgpu.so 与目标 CUDA Runtime 之间的符号兼容。任何 Driver 升级、Runtime 升级或上层框架(PyTorch / TensorFlow / vLLM / TensorRT-LLM)版本变更,都可能让拦截路径上的符号偏移、调用约定、内存布局发生变化,导致显存或算力限制失效甚至进程崩溃。
三层版本矩阵:
| 层级 | 关键组件 | 升级影响 | 验证手段 |
|---|---|---|---|
| Driver 层 | NVIDIA Driver(535.x / 550.x / 570.x) | 改变 nvml 接口、CUDA Driver API 行为 | nvidia-smi -q、DCGM Validator |
| Runtime 层 | CUDA Runtime(11.8 / 12.2 / 12.4)、nvidia-container-toolkit | 改变 cuMemAlloc 等符号实现,libvgpu.so 必须重新编译对齐 | nvcc --version、ldd 检查 libvgpu.so 链接 |
| 框架层 | PyTorch(2.x)、vLLM(0.5+)、Triton、TensorRT-LLM | 框架内部对显存池的激进复用可能绕过 LD_PRELOAD 拦截 | 单 Pod 显存压测 + HAMI 指标对比 |
必须覆盖的最小测试集:
# matrix-test-pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: hami-cuda-matrix-test
spec:
containers:
- name: pytorch-test
image: pytorch/pytorch:2.3.0-cuda12.1-cudnn8-runtime
command: ["python3", "-c", "import torch; x=torch.randn(1024,1024,device='cuda'); print(x.sum())"]
resources:
limits:
hami.io/gpu: 1
hami.io/gpu-memory: 4096 # 4GB 限制
hami.io/gpu-core: 30
- name: vllm-test
image: vllm/vllm-openai:v0.5.4
command: ["sleep", "3600"]
resources:
limits:
hami.io/gpu: 1
hami.io/gpu-memory: 8192 # 8GB
hami.io/gpu-core: 50
- name: triton-test
image: nvcr.io/nvidia/tritonserver:24.06-py3
command: ["sleep", "3600"]
resources:
limits:
hami.io/gpu: 1
hami.io/gpu-memory: 6144
hami.io/gpu-core: 40
验证三步法:
- 符号对齐检查:
docker run --rm <image> ldd $(find / -name 'libvgpu.so' | head -1) | grep cuda确认libvgpu.so链接到目标 CUDA Runtime - 分配边界压测:在 HAMI 限制 4GB 显存的 Pod 中执行
torch.zeros(5*1024*1024*1024 // 4, dtype=torch.float32, device='cuda')(恰好 5GB),观察是否正确返回 OOM 而非绕过限制 - HAMI 指标对比:
hami_device_memory_used/hami_device_core_used应与容器内nvidia-smi报告的物理使用量有合理的"账本差"(限制越严格差越大)
已知兼容性雷区:
- vLLM:使用 PagedAttention 自管 KV Cache,会预先向 CUDA Driver 申请大块显存,可能在 HAMI 尚未完成劫持前就触发 Driver 层 OOM
- TensorRT-LLM:深度依赖 Driver 私有 API,部分构建路径会跳过
cuMemAlloc直接调用底层接口,HAMI 无法拦截 - PyTorch + CUDA Graph:CUDA Graph 回放阶段不重新调用分配 API,HAMI 看不到流量,建议在 CUDA Graph 场景下额外预留 20% 配额
- 多 CUDA 库并存:
/usr/local/cuda/lib64存在多个libcudart.so版本时,libvgpu.so 加载顺序会影响拦截目标,必须用LD_DEBUG=libs确认
CI 流水线建议:
# 升级 GPU Operator 或 HAMI 前的预检
hami-matrix-validate \
--driver 535.183.06 \
--runtime 12.4.1 \
--frameworks pytorch==2.3.0,vllm==0.5.4,triton==2.42.0 \
--gpu-memory-limits 4096,8192,12288 \
--pass-criteria "no_oom_bypass,metrics_consistent,symbols_resolved"
32 HAMI 场景下监控指标体系如何设计?物理账本与虚拟账本为何会不一致?
答案:
HAMI 部署后,集群内同时存在两套账本:物理账本(DCGM / nvidia-smi,反映物理 GPU 真实使用)与虚拟账本(HAMI metrics,反映 vGPU 配额与容器级使用)。两套账本的视角不同、生产中必须并列采集,否则会出现"物理卡空闲却调度失败"的诡异故障。
物理账本指标(DCGM Exporter,>=gpu-operator 部署):
| 指标 | 类型 | 用途 |
|---|---|---|
DCGM_FI_DEV_GPU_UTIL | Gauge | 单卡 SM 利用率(%) |
DCGM_FI_DEV_FB_USED / DCGM_FI_DEV_FB_FREE | Gauge | 物理显存使用 / 空闲(MiB) |
DCGM_FI_DEV_FB_TOTAL | Gauge | 物理显存总量 |
DCGM_FI_DEV_POWER_USAGE | Gauge | 功耗(W) |
DCGM_FI_DEV_ECC_DBE_AGGREGATE | Counter | 双位 ECC 错误累计次数 |
DCGM_FI_DEV_GPU_TEMP | Gauge | 核心温度 |
虚拟账本指标(HAMI Exporter,端口 :9394 默认):
| 指标 | 用途 |
|---|---|
hami_device_memory_total / hami_device_memory_used | 单卡物理显存总量与已分配给 vGPU 的累计值 |
hami_device_memory_percentage | 已用百分比(用于超分判定) |
hami_device_core_total / hami_device_core_used | 算力配额累计值 |
hami_job_nvidia_allocated_memory_bytes | 单 Pod 实际使用(vGPU 视角) |
hami_job_nvidia_core_utilization | 单 Pod 算力实际占用 |
hami_scheduler_pod_scheduling_duration_seconds | 调度耗时直方图 |
hami_scheduler_allocation_failures_total | 调度失败累计 |
账本不一致的典型场景:
# 场景 1:物理空闲但虚拟配额耗尽,Pod Pending
( DCGM_FI_DEV_FB_FREE{node="gpu-01"} > 50*1024*1024*1024 ) # 物理 > 50GB
and on(node) (
sum by(node) (hami_device_memory_total - hami_device_memory_used) < 10*1024*1024*1024
) # 虚拟剩余 < 10GB
# 场景 2:配额未满但物理 OOM(超分导致)
hami_device_memory_percentage > 90
and DCGM_FI_DEV_FB_USED / DCGM_FI_DEV_FB_TOTAL > 0.95
# 场景 3:虚拟指标显示算力 30% 占用,物理显示 95%
hami_job_nvidia_core_utilization{pod="infer-7d"} < 30
and DCGM_FI_DEV_GPU_UTIL{node="gpu-01"} > 90
# 根因:另一租户的 Pod 抢占算力时间片,HAMI 视角只看到自己配额
告警规则建议:
# prometheus-alerts.yaml
groups:
- name: hami-vgpu
rules:
- alert: VGPUSchedulingFragmentation
expr: hami_scheduler_allocation_failures_total > 5
for: 10m
labels: { severity: warning }
annotations:
summary: "HAMI 调度失败激增,GPU 出现碎片化"
- alert: VGPUQuotaImbalance
expr: |
(hami_device_memory_total - hami_device_memory_used)
/ hami_device_memory_total > 0.3
for: 30m
annotations:
summary: "节点 {{ $labels.node }} vGPU 配额闲置 > 30%,可能配额切分过细"
- alert: VGPUOOMHotspot
expr: increase(hami_job_nvidia_oom_total[1h]) > 0
annotations:
summary: "Pod {{ $labels.pod }} 触发 vGPU 显存 OOM,建议扩配额"
Grafana 双面板布局:
- 左面板(物理视角):DCGM 标准 dashboard,6 卡矩阵,温度/功耗/ECC 优先
- 右面板(虚拟视角):HAMI 自定义 dashboard,Pod 维度的显存/算力时间序列、调度失败堆栈
为什么不只盯物理卡: 物理空闲时 Pod 仍可能 Pending(HAMI 配额已分完);物理打满时 Pod 仍可能正常(HAMI 配额未满)。两套账本必须双写双查才能反映真实租户体验。
33 HAMI 与 MIG、MPS 在多租户场景下的组合策略是什么?为什么单一方案无法满足生产需求?
答案:
GPU 共享方案没有"银弹"——MIG 强隔离但粒度固定,MPS 高吞吐但无租户边界,HAMI 灵活但软件隔离。三者在故障域、硬件依赖、运维成本上各有取舍,生产集群通常按业务分级组合部署。
单方案无法覆盖的根因:
| 维度 | MIG | MPS | HAMI |
|---|---|---|---|
| 隔离强度 | 硬件级(独立 SM/缓存/显存) | 进程级(共享 SM) | 软件级(API 拦截) |
| 故障传播 | 不传播 | 同卡进程可互相干扰 | 驱动异常 / ECC 错误会传播 |
| 粒度灵活性 | 固定 profile(如 1g.5gb) | 不可配额 | 自定义(显存 + 算力) |
| 硬件依赖 | 仅 A100/A30/H100 | 大多数 NVIDIA 卡 | 任意支持 CUDA 的卡 |
| 租户公平性 | 强 | 弱(无抢占) | 中(可配额) |
| 运维复杂度 | 高(profile 规划) | 低 | 中(CUDA 兼容性测试) |
四象限组合策略:
| 业务类型 | 推荐方案 | 关键考量 | 资源池划分 |
|---|---|---|---|
| 在线核心推理(SLA > 99.9%) | MIG | 故障隔离 + 显存确定性 + 实例数稳定 | 独立 MIG profile 池,禁用 HAMI |
| 离线训练 / 大模型微调 | HAMI + Volcano | 显存可超分、算力可压缩、单卡多实验 | HAMI 配额池 + Volcano 队列 |
| 单租户高吞吐推理 | MPS | 上下文共享减少切换、最大化吞吐 | 专用节点 + 容器数受限 |
| 开发调试 / Notebook | HAMI | 灵活配额、随时销毁、容错容忍 | 大共享池 + 短时配额 |
| 云厂商托管 | 商业方案(qGPU/cGPU) | 平台级整合 + 计费 + 多租户 | 厂商抽象层 |
典型生产拓扑示例:
graph TB
subgraph "GPU 节点池 A(A100 × 8,核心推理)"
A1["MIG 1g.10gb × 7 实例"]
A2["专属 1 实例做模型缓存"]
end
subgraph "GPU 节点池 B(A100 × 8,训练 + 实验)"
B1["HAMI device-plugin"]
B2["Volcano scheduler"]
B3["实验 Pod 配额:4GB × 8"]
end
subgraph "GPU 节点池 C(L40S × 4,开发)"
C1["HAMI 大共享池"]
C2["Notebook 短时配额"]
end
subgraph "MIG/HAMI 共存节点(Danger)"
X["⚠️ 不推荐混部:device-plugin 互相冲突"]
end
关键工程约束:
- 同卡不混用 MIG + HAMI:nvidia-device-plugin 与 HAMI device-plugin 会竞争
nvidia.com/gpu注册,必须在节点池层面物理隔离 - MIG 节点不跑 HAMI Pod:MIG 实例化后整卡被切分,HAMI 看不到底层物理 GPU
- HAMI 节点需关闭 MIG:
nvidia-smi -mig 0持久化,否则 HAMI 注册的虚拟资源会与 MIG profile 冲突 - 共享池默认不超显存:HAMI 配额总数 ≤ 物理显存 × 1.0;高密度实验可超分到 1.5–2.0,但需要单 Pod OOM 监控
多租户配额管理建议:
# namespace 级别 GPU 配额(配合 ResourceQuota)
apiVersion: v1
kind: ResourceQuota
metadata:
name: tenant-a-gpu-quota
namespace: tenant-a
spec:
hard:
requests.hami.io/gpu: "8"
requests.hami.io/gpu-memory: "65536" # 64GB
requests.hami.io/gpu-core: "500"
# 单租户内部 HAMI 配额细分
apiVersion: v1
kind: LimitRange
metadata:
name: tenant-a-gpu-limit
namespace: tenant-a
spec:
limits:
- type: Container
default:
hami.io/gpu: "1"
hami.io/gpu-memory: "8192"
hami.io/gpu-core: "50"
max:
hami.io/gpu-memory: "16384"
hami.io/gpu-core: "80"
34 HAMI 的动态算力分配能力现状如何?为什么显存仍偏静态配额?
答案:
HAMI 当前阶段算力可动态调、显存仍以静态配额为主——这是由显存分配机制本身的特性决定的,而非 HAMI 实现上的缺陷。理解这一点对于设计潮汐负载(推理峰谷、Notebook 空闲、训练数据加载间隙)下的资源策略至关重要。
算力动态调整的现状:
sequenceDiagram
participant Prom as Prometheus
participant Ctl as 控制平面(CRD/Operator)
participant Pod as vGPU Pod
participant Lib as libvgpu.so
Note over Prom,Lib: 调整前:core=30%
Prom->>Ctl: hami_job_nvidia_core_utilization < 5% 持续 5min
Ctl->>Pod: 调低 core 到 10%(更新 HAMI CRD / Annotation)
Pod->>Lib: 重新加载配置(无需重启)
Lib->>Lib: 时间片窗口按新百分比执行
Note over Pod,Lib: 调整后:core=10%,立即生效
HAMI 算力限制基于时间片 + sleep 注入,核心百分比只是一个比率参数,运行时改变该比率不需要重建 CUDA Context,所以可以做到Pod 不重启的动态调整。
显存为何不能同样动态:
| 原因 | 说明 |
|---|---|
| 框架预分配 | PyTorch / vLLM 启动时调用 cudaMalloc 一次性申请 KV Cache 池(可能数十 GB),运行中不会主动释放给其他进程 |
| CUDA Context 单次绑定 | libvgpu.so 在 cuMemAlloc 入口记账,但若框架把 Context 切到子进程,记账会丢失 |
| 碎片化风险 | 动态回收显存后再分配,物理地址可能不连续,触发 CUDA 内部 coalesce,性能抖动 |
| 驱动层未知 | 显存释放需要驱动层 cuMemFree 与物理 page return 两步,软件层无法跨进程协调 |
| 安全边界 | 跨 Pod 调整显存配额需要重新校验请求方权限,开销与复杂度远超算力调整 |
当前可落地的折中方案:
# 方案 1:算力动态 + 显存静态(HAMI 当前推荐)
resources:
limits:
hami.io/gpu: 1
hami.io/gpu-memory: 8192 # 静态 8GB
hami.io/gpu-core: 30 # 可被控制面动态调至 10–60%
# 方案 2:Pod 级别 HPA + GPU 利用率
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: infer-hpa
spec:
scaleTargetRef: { apiVersion: apps/v1, kind: Deployment, name: infer }
metrics:
- type: Pods
pods:
metric: { name: hami_job_nvidia_core_utilization }
target: { type: AverageValue, averageValue: "60" }
- type: Resource
resource: { name: cpu, target: { type: Utilization, averageUtilization: 70 } }
# 方案 3:VPA 推荐模式(仅看,不自动 apply)
apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
metadata:
name: infer-vpa
spec:
targetRef: { apiVersion: apps/v1, kind: Deployment, name: infer }
updatePolicy: { updateMode: "Off" } # 只生成 recommendation
resourcePolicy: { containerPolicies: [{ containerName: infer, maxAllowed: { hami.io/gpu-memory: 16Gi } }] }
未来演进方向:
- GPU Pool 化:把节点级物理显存池化,跨节点显存共享(类似网络虚拟化的 overlay)
- 远程 GPU:通过 RDMA / NVLink-over-Fabric 把远程 GPU 当本地设备,调度时延与带宽是新瓶颈
- 算力时间片市场:在多租户集群中按 QPS / 延迟 SLO 自动拍卖算力份额
- CUDA Graph 兼容:解决 vLLM 等框架 CUDA Graph 模式下 HAMI 看不到流量的问题(v0.4+ 已在演进)
生产建议:
- 短期:算力走动态(Prometheus 触发 CRD 更新),显存走静态配额 + HPA 副本数
- 中期:等待 HAMI v0.4+ 显存超分回收机制(仍在实验阶段)
- 长期:评估 GPU Pool / 远程 GPU 方案,但延迟、带宽、故障域会显著复杂化
35 "调度成功 ≠ 运行时限制成功" 在 HAMI 排查中如何落地?标准排查链路是什么?
答案:
HAMI 的"虚拟化"由三层协同完成:调度层(scheduler extender)→ 分配层(device-plugin + 环境变量)→ 运行时层(libvgpu.so 拦截)。任一环节断裂,Pod 都可能"调度成功 + 启动成功",但实际显存/算力不受限。生产环境必须把"调度成功 ≠ 限制成功"作为强约束建立标准排查链路。
标准排查链路(4 步验证):
# 步骤 1:调度结果验证(确认 HAMI-scheduler 真的分配了虚拟资源)
kubectl get pod vgpu-test -o jsonpath='{.metadata.annotations}' | jq
# 期望看到 hami.io/gpu-* 系列 annotation:
# hami.io/gpu-node: "gpu-01"
# hami.io/gpu-assigned: "0"
# hami.io/gpu-memory: "4096"
# hami.io/gpu-core: "30"
# 步骤 2:环境变量注入验证(device-plugin 是否把限制传给容器)
kubectl exec vgpu-test -- env | grep -E "CUDA|GPU|VGPU"
# 期望看到:
# CUDA_VISIBLE_DEVICES=0
# GPU_MEMORY_LIMIT=4096
# GPU_CORE_LIMIT=30
# LD_PRELOAD=/usr/local/vgpu/libvgpu.so
# 步骤 3:libvgpu.so 加载验证(容器内是否真的预加载了拦截库)
kubectl exec vgpu-test -- cat /proc/1/maps | grep libvgpu
# 期望看到 libvgpu.so 被加载到进程地址空间
# 失败现象:maps 中无 libvgpu → LD_PRELOAD 未生效 / 路径错误 / 库与 CUDA 版本不匹配
# 步骤 4:行为验证(业务进程是否真的被限制)
kubectl exec vgpu-test -- python3 -c "
import torch
try:
x = torch.zeros(5 * 1024 * 1024 * 1024 // 4, dtype=torch.float32, device='cuda')
print('FAIL: 分配 5GB 未被拦截')
except RuntimeError as e:
print(f'PASS: OOM 触发 → {e}')
"
故障定位决策树:
graph TD
A[Pod Running 但限制失效] --> B{annotation 存在?}
B -->|否| C["调度问题
查 hami-scheduler 日志"]
B -->|是| D{环境变量注入?}
D -->|否| E["device-plugin 问题
查 hami-device-plugin 日志
检查 socket 注册"]
D -->|是| F{libvgpu.so 加载?}
F -->|否| G["运行时问题
检查 LD_PRELOAD 路径
CUDA Runtime 兼容
ldd 验证符号链接"]
F -->|是| H{行为验证?}
H -->|未拦截| I["业务侧问题
CUDA Graph / 驱动私有 API
需 vLLM 等单独适配"]
H -->|已拦截| J["系统正常
记录基线指标"]
按层级的常见根因:
| 层级 | 现象 | 根因 | 修复手段 |
|---|---|---|---|
| 调度层 | Pod Pending,event 显示 0/N nodes are available: insufficient hami.io/gpu-memory | 节点配额已用尽或 scheduler extender 误判 | 查 hami-scheduler 日志、检查 hami.io/gpu-memory 上报值 |
| 调度层 | Pod Pending,但物理显存空闲 | 虚拟账本与物理账本不一致(见 Q32) | 检查超分比例,必要时扩容或重平衡 |
| 分配层 | Pod Running,但 kubectl exec ... env | grep GPU 为空 | device-plugin 未接管该 GPU 节点 | 查 hami-device-plugin DaemonSet 日志、确认 socket 路径 |
| 分配层 | 分配了但 LD_PRELOAD 路径不存在 | 容器镜像未安装 libvgpu.so,或路径写错 | Helm values 中检查 mountPath、基础镜像需包含 libvgpu |
| 运行时层 | libvgpu.so 已加载但 cuMemAlloc 未被拦截 | CUDA 版本不兼容,符号解析失败 | LD_DEBUG=libs 查看加载顺序,重新编译 libvgpu 匹配 Runtime |
| 运行时层 | 限制生效但 OOM 过早 | HAMI 配额 < 框架实际最小需求 | 提升配额或对 vLLM / TensorRT-LLM 单独压测(见 Q31) |
生产落地建议:
- HAMI 安装后立即跑 smoke test:上面 4 步验证脚本封装为 Helm hook 或 Job,作为部署完成的硬性 gate
- HAMI 升级时强制跑矩阵测试:见 Q31 的 CI 流水线
- 关键业务 Pod 加 readinessProbe 检测环境变量:避免"调度成功 + 限制失效"的 Pod 进入流量
- 常态化采集对比指标:物理
DCGM_FI_DEV_GPU_UTILvs 虚拟hami_job_nvidia_core_utilization的偏差作为健康度指标
36 HAMI 与 vLLM / TensorRT-LLM 等激进显存管理框架的兼容性如何评估?为何需要单独压测?
答案:
vLLM、TensorRT-LLM、Triton 等现代推理框架深度优化显存使用:PagedAttention、Continuous Batching、KV Cache 复用、In-flight batching 等技术使它们对显存的占用模式与传统 PyTorch / TF 完全不同。HAMI 的 LD_PRELOAD 拦截模型假设的是"应用按需 cuMemAlloc“的朴素模式,对这些框架的适配需要单独压测而非默认信任。
激进框架对 HAMI 的挑战:
graph LR
A[vLLM 启动] --> B["一次性预分配 KV Cache 池
可能 10–40GB"]
B --> C{libvgpu 拦截?}
C -->|是| D["记账 + 真实分配"]
C -->|否| E["绕过 HAMI
直接命中 Driver OOM"]
C -->|部分| F["前 N 次拦截
后续使用 CUDA Graph 回放
拦截失效"]
G[推理请求] --> H["Continuous Batching"]
H --> I["KV Cache 按页分配
vLLM 自管 PagedAttention"]
I --> J["页大小 = 4MB / 16MB
与 HAMI 配额粒度不一致"]
J --> K[显存抖动 + OOM 误判]
按框架的兼容性矩阵:
| 框架 | HAMI 显存限制 | HAMI 算力限制 | 主要风险 | 建议 |
|---|---|---|---|---|
| vLLM | 生效但 OOM 边界激进 | 生效 | PagedAttention + CUDA Graph 绕过拦截;预分配大池触发 Driver OOM | 单 Pod 单独压测,gpu-memory-utilization 与 HAMI 配额双重限定 |
| TensorRT-LLM | 部分生效 | 生效 | 深度调用 Driver 私有 API;构建阶段可能绕过 | 关闭部分 aggressive 优化,单独压测 |
| Triton | 生效 | 生效 | 多 model ensemble 共享显存时配额切分复杂 | 启用 Triton’s memory pool + HAMI 双层配额 |
| TGI | 生效 | 生效 | Flash Attention 内核路径多变 | 使用官方推荐 CUDA 镜像版本 |
| PyTorch (原生) | 完全生效 | 完全生效 | 朴素模式,HAMI 默认适配 | 默认信任 |
vLLM 单独压测脚本:
# vllm_hami_stress.py
import os
import time
import requests
from vllm import LLM, SamplingParams
# 1. 启动 vLLM,显存由 HAMI 限制为 8GB
os.environ["GPU_MEMORY_LIMIT"] = "8192" # 与 K8s limits 一致
llm = LLM(
model="meta-llama/Llama-2-7b-hf",
gpu_memory_utilization=0.85, # vLLM 视角占 HAMI 配额的 85%
max_model_len=4096,
enforce_eager=False, # CUDA Graph 启用 — 关键测试点
)
# 2. 阶梯压力测试
sampling = SamplingParams(max_tokens=128, temperature=0.7)
prompts = ["Explain GPU virtualization in detail: "] * 64
for batch in [1, 4, 8, 16, 32, 64]:
start = time.time()
outputs = llm.generate(prompts[:batch], sampling)
duration = time.time() - start
print(f"batch={batch} duration={duration:.2f}s "
f"tokens={sum(len(o.outputs[0].token_ids) for o in outputs)}")
# 3. 采集 HAMI 视角指标
import subprocess
metrics = subprocess.check_output([
"curl", "-s", "http://hami-monitor:9394/metrics"
]).decode()
print("HAMI metrics for vllm pod:")
print([l for l in metrics.split("\n") if "hami_job_nvidia_memory" in l])
HAMI 视角的告警阈值:
# vLLM Pod 显存使用逼近配额时提前告警
hami_job_nvidia_allocated_memory_bytes{pod=~"vllm-.*"}
/ on(pod) hami_job_nvidia_memory_limit_bytes > 0.85
# vLLM 触发 Driver OOM 而非 HAMI OOM 的特征
increase(kernel_driver_oom_total[5m]) > 0
and hami_scheduler_allocation_failures_total == 0
# 含义:HAMI 配额充足,但 Driver 层报 OOM → libvgpu 未拦截成功
# CUDA Graph 模式下 HAMI 失效的间接信号
hami_job_nvidia_core_utilization{pod=~"vllm-.*"} < 5
and DCGM_FI_DEV_GPU_UTIL{node=~".*"} > 80
# 含义:HAMI 看到 5% 算力,但物理卡跑满 → 实际绕过了算力限制
生产部署模式:
vLLM on HAMI 推荐配置:
resources: limits: hami.io/gpu: 1 hami.io/gpu-memory: 12288 # 12GB,预留 20% 给 CUDA Graph + HAMI 簿记 hami.io/gpu-core: 60 # 不要超过 70%,留 40% 给框架开销 env: - name: VLLM_GPU_MEMORY_UTILIZATION value: "0.80" # vLLM 内部再用 80% 上限 - name: VLLM_ENFORCE_EAGER value: "false" # 启用 CUDA Graph,但要接受 HAMI 部分失效不推荐组合:vLLM + HAMI + MIG(HAMI 拦截在 MIG 实例上行为未充分测试)
替代方案:vLLM 高密度部署优先用 Time-Slicing(NVIDIA 官方)+ 简单配额管理,而非 HAMI
为何不能"装上即用”:
LD_PRELOAD 拦截是协约式 API 兼容而非硬件级强制。激进框架绕过协约的路径包括:
- CUDA Driver 私有 API(cuLaunchKernelEx、cuMemAllocPitch)
- 持久化 CUDA Context 跨进程
- 预分配 + 自管(bypassing
cudaFree) - CUDA Graph 编译时固化
这些都需要框架 + HAMI 版本联合验证,单方面升级任一方都可能让之前的测试失效。