HAMI 面试题
30 道题- 分类
- Kubernetes
- 子分类
- gpu
- 题目数
- 30 道
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 XQOPEN $labels.gpu_id XQCLOSE 共享容器数超过 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 XQOPEN $labels.gpu_id XQCLOSE 显存使用率超过 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 驱动系列)