HAMi + Volcano 的 vGPU 使用实践

HAMi 是一个用于管理 Kubernetes 集群中异构 AI 计算设备的开源平台。其前身为 k8s-vGPU-scheduler,可在多个容器和工作负载之间实现设备共享。

Volcano 是基于 Kubernetes 的容器批量计算平台,主要用于高性能计算场景。

本文结合两者的能力进行实践,参考资料:https://project-hami.io/zh/tutorials/labs/volcano-vgpu-gang-queue


前提条件

  1. Kubernetes 已部署,版本 >= 1.16,本文使用 RKE2 v1.35.6+rke2r1
  2. GPU 节点已接入集群
  3. GPU 节点已安装内核驱动和 NVIDIA Container Toolkit
  4. GPU 节点默认 Runtime 已设置为 Nvidia
  5. 集群没有安装 NVIDIA 或 HAMi 的 Device Plugin

内核驱动、NVIDIA Container Toolkit 的安装和 RKE2 默认 Runtime 的配置可参考:

下面的资源值针对 12 GiB GPU 设计:

  • 成功 Gang 申请 2 × 3000 MiB = 6000 MiB
  • 资源不足 Gang 申请 2 × 9000 MiB = 18000 MiB
  • Queue 上限为 6000 MiB
  • Queue 负例申请 9000 MiB,高于 Queue 上限,但低于空闲节点约 12288 MiB 的容量

安装 Volcano

通过官方 Helm Chart 安装:

1
2
3
4
5
helm repo add volcano-sh https://volcano-sh.github.io/helm-charts

helm repo update

helm install volcano volcano-sh/volcano -n volcano-system --create-namespace

开启 Volcano vGPU 调度

编辑 Volcano Scheduler 配置:

1
kubectl -n volcano-system edit configmap volcano-scheduler-configmap

在第二个 Scheduler Tier 中添加如下内容:

1
2
3
4
- name: deviceshare
arguments:
deviceshare.VGPUEnable: true
deviceshare.SchedulePolicy: binpack

重启 Volcano Scheduler:

1
kubectl -n volcano-system rollout restart deployment volcano-scheduler

安装 Volcano Device Plugin

此处安装 v1.12.0 版本:

1
2
kubectl apply -f \
https://raw.githubusercontent.com/Project-HAMi/volcano-vgpu-device-plugin/v1.12.0/volcano-vgpu-device-plugin.yml

确认节点注册资源:

1
2
3
4
export NODE_NAME=gpu-0

kubectl get node "${NODE_NAME}" \
-o custom-columns='NAME:.metadata.name,NUMBER:.status.allocatable.volcano\.sh/vgpu-number,MEMORY:.status.allocatable.volcano\.sh/vgpu-memory,CORES:.status.allocatable.volcano\.sh/vgpu-cores'

示例输出:

1
2
NAME    NUMBER   MEMORY   CORES
gpu-0 10 12288 100

使用场景


验证单个 vGPU Pod

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
kubectl create namespace volcano-demo

cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
name: volcano-vgpu-single
namespace: volcano-demo
spec:
schedulerName: volcano
restartPolicy: Never
containers:
- name: cuda
image: nvidia/cuda:11.0.3-base-ubuntu18.04
imagePullPolicy: IfNotPresent
command:
- bash
- -lc
- |
echo "===== GPU ENV ====="
env | grep -E 'CUDA_DEVICE|NVIDIA_VISIBLE_DEVICES|VGPU|VOLCANO' || true
echo "===== NVIDIA SMI ====="
nvidia-smi
sleep 3600
resources:
limits:
volcano.sh/vgpu-number: 1
volcano.sh/vgpu-memory: 2000
volcano.sh/vgpu-cores: 30
EOF

示例输出:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
root@test-2:~# kubectl exec -n volcano-demo volcano-vgpu-single -- \
env | grep -E 'CUDA_DEVICE|NVIDIA_VISIBLE_DEVICES|VGPU|VOLCANO'
NVIDIA_VISIBLE_DEVICES=GPU-eeab99c3-99a0-e956-3036-41d06990d593
CUDA_DEVICE_MEMORY_LIMIT_0=2000m
CUDA_DEVICE_SM_LIMIT=30
CUDA_DEVICE_MEMORY_SHARED_CACHE=/tmp/vgpu/31191b22-212a-4358-a5d7-8ed49788b37e.cache

root@test-2:~# kubectl exec -n volcano-demo logs volcano-vgpu-single
Tue Jul 14 04:37:39 2026
+-----------------------------------------------------------------------------------------+
| NVIDIA-SMI 595.71.05 Driver Version: 595.71.05 CUDA Version: 13.2 |
+-----------------------------------------+------------------------+----------------------+
| GPU Name Persistence-M | Bus-Id Disp.A | Volatile Uncorr. ECC |
| Fan Temp Perf Pwr:Usage/Cap | Memory-Usage | GPU-Util Compute M. |
| | | MIG M. |
|=========================================+========================+======================|
[HAMI-core Msg(81:140697490560832:libvgpu.c:870)]: Initializing.....
| 0 NVIDIA GeForce RTX 3060 On | 00000000:03:00.0 Off | N/A |
| 0% 33C P8 7W / 170W | 0MiB / 2000MiB | 0% Default |
| | | N/A |
+-----------------------------------------+------------------------+----------------------+

+-----------------------------------------------------------------------------------------+
| Processes: |
| GPU GI CI PID Type Process name GPU Memory |
| ID ID Usage |
|=========================================================================================|
| No running processes found |
+-----------------------------------------------------------------------------------------+
[HAMI-core Msg(81:140697490560832:multiprocess_memory_limit.c:703)]: Cleanup on exit for PID 81
[HAMI-core Msg(81:140697490560832:multiprocess_memory_limit.c:739)]: Exit cleanup complete for PID 81

Volcano 中的 Gang Scheduling

Volcano 中的 Gang Scheduling(成组调度)是一种要么一起调度,要么一个都不调度的策略。

它主要解决分布式计算任务的问题。例如一个训练任务需要 4 Worker,如果集群当前只能调度 2 个 Pod,普通调度器可能先启动这 2 个 Pod,而剩下的 Pod 处于 Pending 状态。这样任务不但无法完成工作,还会一直占用 GPU、CPU 和内存。

Gang Scheduling 可以要求至少满足指定数量后才开始调度:

1
2
spec:
minAvailable: 4

这表示集群能同时容纳这 4 个 Pod 时,Volcano 才会调度它们;否则整个 Job 保持 Pending 状态。


运行双 Worker Gang

通过 VolcanoJob 创建两个 Worker。每个 Worker 申请一个 vGPU 份额、3000 MiB 显存和 30% 算力,因此完整 Gang 总共需要 6000 MiB 和 60% 算力:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
cat <<EOF | kubectl apply -f -
apiVersion: batch.volcano.sh/v1alpha1
kind: Job
metadata:
name: vcjob-vgpu-gang
namespace: volcano-demo
spec:
schedulerName: volcano
queue: default
minAvailable: 2
maxRetry: 1
tasks:
- name: vgpu-worker
replicas: 2
template:
spec:
restartPolicy: Never
containers:
- name: cuda
image: nvidia/cuda:11.0.3-base-ubuntu18.04
imagePullPolicy: IfNotPresent
command:
- bash
- -lc
- |
echo "worker: $(hostname)"
echo "===== GPU ENV ====="
env | grep -E 'CUDA_DEVICE|NVIDIA_VISIBLE_DEVICES|VGPU|VOLCANO' || true
echo "===== NVIDIA SMI ====="
nvidia-smi
sleep 3600
resources:
limits:
volcano.sh/vgpu-number: 1
volcano.sh/vgpu-memory: 3000
volcano.sh/vgpu-cores: 30
EOF

示例输出:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
root@test-2:~# kubectl -n volcano-demo logs vcjob-vgpu-gang-vgpu-worker-0
worker: test-2
===== GPU ENV =====
mesg: ttyname failed: Inappropriate ioctl for device
NVIDIA_VISIBLE_DEVICES=void
CUDA_DEVICE_MEMORY_SHARED_CACHE=/tmp/vgpu/50f68904-2a85-4581-96ba-a04bb36e98cf.cache
CUDA_DEVICE_MEMORY_LIMIT_0=3000m
CUDA_DEVICE_SM_LIMIT=30
===== NVIDIA SMI =====
[HAMI-core Msg(56:140268359665472:libvgpu.c:870)]: Initializing.....
Tue Jul 14 07:43:02 2026
+-----------------------------------------------------------------------------------------+
| NVIDIA-SMI 595.71.05 Driver Version: 595.71.05 CUDA Version: 13.2 |
+-----------------------------------------+------------------------+----------------------+
| GPU Name Persistence-M | Bus-Id Disp.A | Volatile Uncorr. ECC |
| Fan Temp Perf Pwr:Usage/Cap | Memory-Usage | GPU-Util Compute M. |
| | | MIG M. |
|=========================================+========================+======================|
| 0 NVIDIA GeForce RTX 3060 On | 00000000:03:00.0 Off | N/A |
| 0% 33C P8 8W / 170W | 0MiB / 3000MiB | 0% Default |
| | | N/A |
+-----------------------------------------+------------------------+----------------------+

+-----------------------------------------------------------------------------------------+
| Processes: |
| GPU GI CI PID Type Process name GPU Memory |
| ID ID Usage |
|=========================================================================================|
| No running processes found |
+-----------------------------------------------------------------------------------------+
[HAMI-core Msg(56:140268359665472:multiprocess_memory_limit.c:703)]: Cleanup on exit for PID 56
[HAMI-core Msg(56:140268359665472:multiprocess_memory_limit.c:739)]: Exit cleanup complete for PID 56

root@test-2:~# kubectl -n volcano-demo logs vcjob-vgpu-gang-vgpu-worker-1
mesg: ttyname failed: Inappropriate ioctl for device
worker: test-2
===== GPU ENV =====
NVIDIA_VISIBLE_DEVICES=void
CUDA_DEVICE_MEMORY_SHARED_CACHE=/tmp/vgpu/e66172f2-94b9-44dc-842a-1794ea2f1d52.cache
CUDA_DEVICE_MEMORY_LIMIT_0=3000m
CUDA_DEVICE_SM_LIMIT=30
===== NVIDIA SMI =====
[HAMI-core Msg(57:140009740244800:libvgpu.c:870)]: Initializing.....
Tue Jul 14 07:43:03 2026
+-----------------------------------------------------------------------------------------+
| NVIDIA-SMI 595.71.05 Driver Version: 595.71.05 CUDA Version: 13.2 |
+-----------------------------------------+------------------------+----------------------+
| GPU Name Persistence-M | Bus-Id Disp.A | Volatile Uncorr. ECC |
| Fan Temp Perf Pwr:Usage/Cap | Memory-Usage | GPU-Util Compute M. |
| | | MIG M. |
|=========================================+========================+======================|
| 0 NVIDIA GeForce RTX 3060 On | 00000000:03:00.0 Off | N/A |
| 0% 33C P8 8W / 170W | 0MiB / 3000MiB | 0% Default |
| | | N/A |
+-----------------------------------------+------------------------+----------------------+

+-----------------------------------------------------------------------------------------+
| Processes: |
| GPU GI CI PID Type Process name GPU Memory |
| ID ID Usage |
|=========================================================================================|
| No running processes found |
+-----------------------------------------------------------------------------------------+
[HAMI-core Msg(57:140009740244800:multiprocess_memory_limit.c:703)]: Cleanup on exit for PID 57
[HAMI-core Msg(57:140009740244800:multiprocess_memory_limit.c:739)]: Exit cleanup complete for PID 57

资源不足的情况下运行双 Worker Gang

通过 VolcanoJob 创建两个 Worker。每个 Worker 申请一个 vGPU 份额、9000 MiB 显存和 30% 算力,因此完整 Gang 总共需要 18000 MiB 和 60% 算力:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
cat <<EOF | kubectl apply -f -
apiVersion: batch.volcano.sh/v1alpha1
kind: Job
metadata:
name: vcjob-vgpu-gang-insufficient
namespace: volcano-demo
spec:
schedulerName: volcano
queue: default
minAvailable: 2
maxRetry: 1
tasks:
- name: vgpu-worker
replicas: 2
template:
spec:
restartPolicy: Never
containers:
- name: cuda
image: nvidia/cuda:11.0.3-base-ubuntu18.04
imagePullPolicy: IfNotPresent
command: ["bash", "-lc", "nvidia-smi; sleep 3600"]
resources:
limits:
volcano.sh/vgpu-number: 1
volcano.sh/vgpu-memory: 9000
volcano.sh/vgpu-cores: 30
EOF

创建后,可以看到两个 Pod 都处于 Pending 状态:

1
2
3
4
root@test-2:~# kubectl -n volcano-demo get pod
NAME READY STATUS RESTARTS AGE
vcjob-vgpu-gang-insufficient-vgpu-worker-0 0/1 Pending 0 2m52s
vcjob-vgpu-gang-insufficient-vgpu-worker-1 0/1 Pending 0 2m52s

检查 Volcano Job,会发现如下提示:

1
2
3
4
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Warning PodGroupPending 2m31s (x2 over 2m31s) vc-controller-manager PodGroup volcano-demo:vcjob-vgpu-gang-insufficient unschedulable, reason: 1/2 tasks in gang unschedulable: pod group is not ready, 2 Pending, 2 minAvailable; Pending: 1 Schedulable, 1 Unschedulable

单独来看集群的资源可以放置一个 Worker,但因为完整的双 Worker Gang 调度无法满足,所以没有只启动其中一部分。


Volcano 中的 Queue

Volcano 中的 Queue(队列)可以理解为一组作业共享的资源池和调度边界。

它主要解决多租户、多团队或不同业务之间的资源分配问题,例如:

  • 研发队列:development
  • 生产队列:production
  • AI 训练队列:training

提交到不同 Queue 的 Job,会按照 Queue 的资源配额、权重和优先级参与调度。Volcano Job 未指定 Queue 时,通常会进入 default Queue。


创建 Queue

创建一个最多允许两个 vGPU 份额、6000 MiB 和 60% 算力的 Queue:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
cat <<EOF | kubectl apply -f -
apiVersion: scheduling.volcano.sh/v1beta1
kind: Queue
metadata:
name: gpu-small-queue
spec:
weight: 1
reclaimable: false
capability:
cpu: "2"
memory: "4Gi"
volcano.sh/vgpu-number: "2"
volcano.sh/vgpu-memory: "6000"
volcano.sh/vgpu-cores: "60"
EOF

Queue 上限以内的 Job

创建双 Worker 的 Gang,总共需要两个 vGPU 份额、6000 Mib 显存和 60% 算力,并指定 Queue 为 gpu-small-queue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
cat <<EOF | kubectl apply -f -
apiVersion: batch.volcano.sh/v1alpha1
kind: Job
metadata:
name: vcjob-vgpu-queue-fit
namespace: volcano-demo
spec:
schedulerName: volcano
queue: gpu-small-queue
minAvailable: 2
tasks:
- name: vgpu-worker
replicas: 2
policies:
- event: TaskCompleted
action: CompleteJob
template:
spec:
restartPolicy: Never
containers:
- name: cuda
image: nvidia/cuda:11.0.3-base-ubuntu18.04
command: ["bash", "-lc", "nvidia-smi; sleep 60"]
resources:
limits:
volcano.sh/vgpu-number: 1
volcano.sh/vgpu-memory: 3000
volcano.sh/vgpu-cores: 30
EOF

Pod 能够正常调度运行:

1
2
3
4
root@test-2:~# kubectl -n volcano-demo get pod
NAME READY STATUS RESTARTS AGE
vcjob-vgpu-queue-fit-vgpu-worker-0 1/1 Running 0 46s
vcjob-vgpu-queue-fit-vgpu-worker-1 1/1 Running 0 46s

查看 Queue 资源的分配情况:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
spec:
capability:
cpu: "2"
memory: 4Gi
volcano.sh/vgpu-cores: "60"
volcano.sh/vgpu-memory: "6000"
volcano.sh/vgpu-number: "2"
dequeueStrategy: traverse
parent: root
reclaimable: false
weight: 1
status:
allocated:
cpu: "0"
memory: "0"
pods: "2"
volcano.sh/vgpu-cores: "60"
volcano.sh/vgpu-memory: 6k
volcano.sh/vgpu-number: "2"

超过 Queue 上限的 Job

创建双 Worker 的 Gang,总共需要 2 个 vGPU 份额、9000 Mib 显存和 60% 算力,并指定 Queue 为 gpu-small-queue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
cat <<EOF | kubectl apply -f -
apiVersion: batch.volcano.sh/v1alpha1
kind: Job
metadata:
name: vcjob-vgpu-queue-exceeds
namespace: volcano-demo
spec:
schedulerName: volcano
queue: gpu-small-queue
minAvailable: 2
tasks:
- name: vgpu-worker
replicas: 2
template:
spec:
restartPolicy: Never
containers:
- name: cuda
image: nvidia/cuda:11.0.3-base-ubuntu18.04
command: ["bash", "-lc", "nvidia-smi; sleep 60"]
resources:
limits:
volcano.sh/vgpu-number: 1
volcano.sh/vgpu-memory: 4500
volcano.sh/vgpu-cores: 30
EOF

尽管 GPU 节点有 12 GiB 显存,但由于 9000 MiB 超过 Queue 上限,Pod 无法正常调度:

1
2
3
4
root@test-2:~# kubectl -n volcano-demo get pod
NAME READY STATUS RESTARTS AGE
vcjob-vgpu-queue-exceeds-vgpu-worker-0 0/1 Pending 0 3s
vcjob-vgpu-queue-exceeds-vgpu-worker-1 0/1 Pending 0 3s
Author

Warner Chen

Posted on

2026-07-14

Updated on

2026-07-14

Licensed under

You need to set install_url to use ShareThis. Please set it in _config.yml.
You forgot to set the business or currency_code for Paypal. Please set it in _config.yml.

Comments

You forgot to set the shortname for Disqus. Please set it in _config.yml.