Стадия жизненного цикла модуля: Общедоступная версия
У модуля есть требования для установки
В режиме DRA рабочая нагрузка запрашивает GPU на уровне пода, а не на уровне узла. Сделать это можно двумя способами:
- Через extended-ресурс — администратор публикует GPUClass, а пользователь запрашивает его по имени одной строкой. Рекомендуемый способ.
- Создав ResourceClaimTemplate самостоятельно — пользователь создаёт
DeviceClassс CEL-селектором иResourceClaimTemplate. Используйте этот способ, когда опубликованных классов недостаточно.
Запрос GPU по имени
Уточните у администратора, какие классы доступны, или посмотрите сами с помощью следующих команд:
Просмотр всех GPUClass в кластере:
d8 k get gpuclassesПросмотр классов устройств для динамического выделения GPU (DRA), созданных контроллером:
d8 k get deviceclasses -l gpu.deckhouse.io/managed-by=gpu-controllerКаждый опубликованный класс предоставляет extended-ресурс с именем gpu.deckhouse.io/<имя-device-class>. Запрашивайте его как любой другой ресурс:
apiVersion: v1
kind: Pod
metadata:
name: cuda-probe
spec:
restartPolicy: Never
containers:
- name: app
image: nvidia/cuda:12.8.0-devel-ubuntu24.04
command: ["nvidia-smi", "-L"]
resources:
requests:
gpu.deckhouse.io/ml-ai-h100: 1
limits:
gpu.deckhouse.io/ml-ai-h100: 1На этапе admission модуль заменяет extended-ресурс сгенерированным ResourceClaim и сам подключает его к контейнеру — пользователю не нужно писать ни spec.resourceClaims, ни resources.claims.
Какое значение запрашивать
Зависит от класса. Чтобы установить нужное значение, используйте имя класса:
| Имя класса | Что запрашивать | Значение |
|---|---|---|
<класс> |
1 |
Один целый GPU, эксклюзивно. |
<класс>-<профиль> |
1 |
Одна MIG-партиция этого профиля, эксклюзивно. |
<класс>-mps-percent, <класс>-ts-percent |
1–100 |
Указанный процент разделяемого GPU. |
<класс>-<профиль>-mps-percent, -ts-percent |
1–100 |
Указанный процент разделяемой MIG-партиции. |
Например, четверть разделяемой партиции 1g12gb:
resources:
requests:
gpu.deckhouse.io/ml-ai-h100-1g12gb-mps-percent: 25
limits:
gpu.deckhouse.io/ml-ai-h100-1g12gb-mps-percent: 25Чтобы понять, в каких единицах измеряется класс, посмотрите его лейбл gpu.deckhouse.io/capacity-units: значение 1 устанавливается для эксклюзивных классов, 100 — для классов -percent.
На что обратить внимание при запросе GPU по имени
Имя класса должно существовать. Опечатка отклоняется сразу, а не игнорируется без каких-либо предупреждений:
Error from server (Forbidden): admission webhook "podgpuresourceclaim.gpu.deckhouse.io" denied
the request: get DeviceClass "no-such-class": deviceclasses.resource.k8s.io "no-such-class" not found
В системных неймспейсах запрос GPU по имени не работает. Вебхук намеренно не применяется в kube-system и во всех неймспейсах d8-*. Там extended-ресурс остаётся в спецификации пода без изменений — и поскольку ни один узел его не публикует, под навсегда останется в Pending. При этом никаких оповещений или предупреждений не появляется. Запускайте GPU-нагрузки в пользовательских неймспейсах.
Разделение между неймспейсами
По умолчанию разделяемый GPU переиспользуется только нагрузками из того же неймспейса. Проверка выполняется на узле: pre-bind conflict predicate отклоняет выделение до привязки, а адаптер вендора перепроверяет его на этапе подготовки. Отказ виден как условие BindingFailed=True с причиной NamespaceConflict в ResourceClaim.status.devices. Ограничение действует на уровне размещения (placement), поэтому разные MIG-партиции одной карты остаются доступны разным неймспейсам. Администраторы кластера могут снять его параметром dra.allowCrossNamespaceSharing.
Запрос устройства напрямую
Этот вариант подходит для случаев, когда опубликованные классы не удовлетворяют потребности пользователя: нужна конкретная карта по PCI-адресу, нестандартная комбинация атрибутов или opaque-конфигурация устройства, например VFIO-проброс.
Атрибуты, по которым может работать селектор, перечислены в разделе «Справочник атрибутов устройства».
При запросе устройства напрямую пользователь:
- Создает DeviceClass, CEL-селектор которого выбирает подходящие устройства.
- Создает ResourceClaimTemplate, описывающий, что именно выделить из этого класса.
- Описывает в ресурсе Job под, который ссылается на шаблон и передаёт описанный в ResourceClaimTemplate claim в контейнер.
Запрос ресурсов в поде
spec:
resourceClaims: # Объявление созданного ResourceClaimTemplate для пода.
- name: gpu
resourceClaimTemplateName: my-claim
containers:
- name: app
image: nvidia/cuda:12.8.0-devel-ubuntu24.04
resources:
claims:
- name: gpu # Передача claim, описанного в ResourceClaimTemplate контейнеру.При описании пода в ресурсе Job spec.resourceClaims и spec.containers.resources.claims обязательны. Если объявить spec.resourceClaims, но не указать spec.containers.resources.claims в спецификации контейнера, GPU будет выделен, но контейнер его не получит: при этом не будет сообщения об ошибке, предупреждения или события. Под запустится, но CUDA не найдёт запрошенный ресурс.
NVIDIA_VISIBLE_DEVICES в модуле gpu не действует. Значение all ничего не даёт, потому что DRA-нагрузки работают в стандартном container runtime и получают устройства только через claim. Под без spec.containers.resources.claims не видит ни одного /dev/nvidia* — это сделано намеренно.
Запрос целого GPU эксклюзивно
В примере ниже условие !has(...sharingStrategy) делает выделение эксклюзивным — оно исключает разделяемые варианты той же карты (запрос на выделение карты целиком):
apiVersion: resource.k8s.io/v1
kind: DeviceClass
metadata:
name: nvidia-whole-gpu
spec:
selectors:
- cel:
expression: |
device.attributes["gpu.deckhouse.io"].vendor == "nvidia" &&
device.attributes["gpu.deckhouse.io"].computeAPI == "CUDA" &&
has(device.attributes["gpu.deckhouse.io"].deviceType) &&
device.attributes["gpu.deckhouse.io"].deviceType == "physical" &&
!has(device.attributes["gpu.deckhouse.io"].sharingStrategy)
---
apiVersion: resource.k8s.io/v1
kind: ResourceClaimTemplate
metadata:
name: whole-gpu
spec:
spec:
devices:
requests:
- name: gpu
exactly:
deviceClassName: nvidia-whole-gpu
count: 1Запрос MIG-партиции
Чтобы запросить MIG-партицию, добавьте в селектор атрибуты партиции. Имена профилей здесь аппаратные, с точками — 1g.5gb, а не 1g5gb:
apiVersion: resource.k8s.io/v1
kind: DeviceClass
metadata:
name: nvidia-mig-1g5gb
spec:
selectors:
- cel:
expression: |
device.attributes["gpu.deckhouse.io"].vendor == "nvidia" &&
device.attributes["gpu.deckhouse.io"].computeAPI == "CUDA" &&
has(device.attributes["gpu.deckhouse.io"].deviceType) &&
device.attributes["gpu.deckhouse.io"].deviceType == "partition" &&
has(device.attributes["gpu.deckhouse.io"].partitionTechnology) &&
device.attributes["gpu.deckhouse.io"].partitionTechnology == "MIG" &&
has(device.attributes["gpu.deckhouse.io"].partitionProfile) &&
device.attributes["gpu.deckhouse.io"].partitionProfile == "1g.5gb"Запрос имеет тот же вид, что и для целой карты.
Команду nvidia-smi -mig пользователю выполнять не нужно. Состоянием MIG управляет модуль:
- Если запрошенная партиция уже существует и свободна, она выделяется как есть.
- Если её нет, модуль включает MIG на карте и создаёт партицию на этапе подготовки.
- Если на карте сейчас другая, конфликтующая раскладка, модуль её перестраивает.
Последние два случая разрушающие. Включение MIG и перестройка раскладки вытесняют GPU-нагрузки с узла и могут привести к его перезагрузке. Не запускайте их без необходимости на общем кластере.
Перестройка возможна только если конфликтующая раскладка сейчас никем не занята. MIG-партиции существуют лишь пока их кто-то держит — они создаются при подготовке и удаляются при освобождении claim, — поэтому если другая нагрузка держит несовместимую партицию, ваш под останется в Pending до её завершения. Это не сбой: планировщик отказывается выделить слайсы карты дважды.
Возврат к целой карте
Запрос устройства типа physical на карте, где включён MIG, заставляет модуль отключить режим партиционирования — снова с вытеснением нагрузки. Наблюдать переход можно с помощью команды:
d8 k get physicalgpu <имя> -o jsonpath='{.status.currentState.partition.mode}'Запрос доли GPU через MPS
Для запроса доли GPU через MPS добавьте capacity.requests и opaque-конфиг, выбирающий MPS. Для целой карты — GpuConfig:
apiVersion: resource.k8s.io/v1
kind: ResourceClaimTemplate
metadata:
name: gpu-mps-quarter
spec:
spec:
devices:
requests:
- name: gpu
exactly:
deviceClassName: nvidia-gpu-mps
count: 1
capacity:
requests:
sharePercent: "25"
gpu.deckhouse.io/memory: 1Gi
config:
- requests: ["gpu"]
opaque:
driver: gpu.deckhouse.io
parameters:
apiVersion: resource.gpu.deckhouse.io/v1alpha1
kind: GpuConfig
sharing:
strategy: MPS
mpsConfig:
defaultActiveThreadPercentage: 25Соответствующий DeviceClass должен выбирать разделяемые устройства, поэтому стратегию нужно требовать, а не исключать:
… && has(device.attributes["gpu.deckhouse.io"].sharingStrategy) &&
device.attributes["gpu.deckhouse.io"].sharingStrategy == "mps"
Чтобы разделить MIG-партицию, а не целую карту, используйте kind: MigDeviceConfig и селектор партиции. Для партиции достаточно одного sharePercent — запрашивать память дополнительно не нужно.
На партиции разрешён только MPS. Запрос time-slicing там отклоняется вебхуком с сообщением MIG device sharing supports only MPS strategy.
Нагрузки, разделяющие один GPU, используют общий control-демон MPS: он запускается по требованию и переиспользуется. Удаление одной из них не мешает остальным.
Запрос доли GPU без указания стратегии
Если вам не важно, получите вы MPS или time-slicing, запросите ёмкость и не указывайте стратегию:
requests:
- name: gpu
exactly:
deviceClassName: nvidia-gpu-any
count: 1
capacity:
requests:
sharePercent: "25"
gpu.deckhouse.io/memory: 1GiЭто работает потому, что целая карта вообще не публикует ёмкость sharePercent — её публикуют только разделяемые варианты. Поэтому запрос sharePercent вынуждает планировщик выбрать разделяемый вариант. Если MPS на выбранном устройстве недоступен, запрос попадёт на time-slicing.
Чтобы это работало, селектор DeviceClass должен быть разрешающим: не добавляйте !has(...sharingStrategy), иначе вы исключите те самые устройства, которые могут удовлетворить запрос, и ничего не свяжется.
VFIO-проброс
Проброс отдаёт в контейнер «сырое» PCI-устройство вместо CUDA-устройства. Внутри контейнера нет ни CUDA, ни nvidia-smi — вы получаете /dev/vfio/<iommu_group> и /dev/vfio/vfio для потребителя вроде виртуальной машины, QEMU/KVM или DPDK.
apiVersion: resource.k8s.io/v1
kind: DeviceClass
metadata:
name: h100-vfio
spec:
selectors:
- cel:
expression: |
device.attributes["gpu.deckhouse.io"].vendor == "nvidia" &&
has(device.attributes["gpu.deckhouse.io"].productName) &&
device.attributes["gpu.deckhouse.io"].productName.lowerAscii().matches("^.*h100.*$") &&
has(device.attributes["gpu.deckhouse.io"].deviceType) &&
device.attributes["gpu.deckhouse.io"].deviceType == "physical" &&
has(device.attributes["gpu.deckhouse.io"].bareMetal) &&
device.attributes["gpu.deckhouse.io"].bareMetal == true
---
apiVersion: resource.k8s.io/v1
kind: ResourceClaimTemplate
metadata:
name: h100-vfio
spec:
spec:
devices:
requests:
- name: gpu
exactly:
deviceClassName: h100-vfio
allocationMode: ExactCount
count: 1
config:
- requests: ["gpu"]
opaque:
driver: gpu.deckhouse.io
parameters:
apiVersion: resource.gpu.deckhouse.io/v1alpha1
kind: VfioDeviceConfigОбратите внимание на отсутствие capacity и sharePercent: для проброса нужна карта целиком. Запрос полного sharePercent: "100" тоже считается эксклюзивным, но любое меньшее значение отклоняется.
Под должен попасть на узел, подготовленный для VFIO. Готовность не публикуется как атрибут устройства, поэтому её нельзя указать в CEL-селекторе — используйте лейбл узла:
spec:
nodeSelector:
node.deckhouse.io/gpu-vfio-ready: "true"
resourceClaims:
- name: gpu
resourceClaimTemplateName: h100-vfio
containers:
- name: vfio-consumer
image: busybox:1.36
command: ["/bin/sh", "-c", "ls -l /dev/vfio && sleep infinity"]
resources:
claims:
- name: gpuКарта в режиме проброса имеет .status.phase: Passthrough и исчезает из пула CUDA-устройств.
Требования, проверяемые на этапе подготовки; если что-то не выполнено, подготовка завершается ошибкой с явным сообщением:
| Сообщение | Причина |
|---|---|
vfio requires bare-metal node |
Узел виртуализирован (bareMetal != true). |
vfio requires exclusive allocation |
В запросе присутствует sharing или sharePercent. |
vfio requested for non-physical device |
Селектор выбрал MIG-партицию. |
unexpected driver "..." |
Карта привязана к драйверу, отличному от nvidia и vfio-pci. |
mixed VFIO and non-VFIO allocations… |
В одном claim смешаны проброс и обычные устройства. |
Кроме того, для подготовки требуется отключённый SR-IOV на устройстве и отсутствие запущенных процессов на карте. Если MIG включён, но карта простаивает, модуль снимает MIG-конфигурацию и привязывает карту к vfio-pci самостоятельно.
Для проброса требуется IOMMU, включённый в BIOS и в командной строке ядра. Модуль добавляет параметр ядра сам, но его применение приводит к перезагрузке узла.
Возможности модуля в зависимости от вендора GPU
В таблице описаны возможности модуля в зависимости от вендора GPU:
| Возможность | NVIDIA | MetaX | AMD |
|---|---|---|---|
| Целая карта | Да | Да | В разработке |
| Аппаратное партиционирование | Да (MIG) | Нет | В разработке (SPX/DPX/CPX, NPS) |
| Программное партиционирование | Да (MPS, TS) | В разработке (sGPU) | В разработке (TS) |
| Проброс физической функции (VFIO) | Да | Да | В разработке |
Аппаратное партиционирование даёт изоляцию и предсказуемую производительность. Программное — гибкую нарезку без изоляции, поэтому нагрузки на одной карте влияют друг на друга.
Особенности разных вендоров:
- sGPU делит карту MetaX по квоте — доля вычислительных ресурсов и объём памяти, — а не по фиксированным местам.
- Карты AMD обнаруживаются и появляются как объекты PhysicalGPU, но выделить их пока нельзя.
- Проброс отдаёт физическую функцию целиком — это не vGPU и не SR-IOV. У NVIDIA он требует bare-metal-узла, а включённый SR-IOV его блокирует.
С возможностями и поддержкой карт можно ознакомиться в Глоссарии.
Как решает модуль
Две строки определяются по-разному, и разница заметна на практике.
Аппаратное партиционирование определяется по каждой карте и попадает в .status.capabilities.partitionSupport. Программное разделение объявляется на уровне узла: все карты узла получают одинаковые лейблы gpu.deckhouse.io/sharing.mps и sharing.ts независимо от модели.
Поэтому на карте, которая MPS не поддерживает, лейбл всё равно появится, а claim будет принят. Несоответствие вскроется при подготовке: модуль переключится на time-slicing и опубликует событие MPSFallbackToTimeSlicing. Нагрузка получит разделение, но не то, которое запрошено.
Для просмотра всех физических GPU в кластере и их лейблов используйте команду:
d8 k get physicalgpus --show-labelsДля получения детальной информации о поддержке разделения (MIG и т. п.) для конкретного GPU используйте команду:
d8 k get physicalgpu <имя> -o jsonpath='{.status.capabilities.partitionSupport}' | jqВыбор подходящего типа партиционирования
Аппаратное партиционирование подходит для случаев, когда нужна изоляция и предсказуемая производительность, а карта есть в списке вендора.
Программное партиционирование подходит для случаев, когда аппаратное на модели недоступно либо его фиксированные профили слишком грубы для нагрузки.
Получение информации о выделенном GPU
Чтобы посмотреть, какое устройство было выделено, используйте команду:
d8 k -n <неймспейс> get resourceclaim -o json \
| jq '.items[].status.allocation.devices.results[] | {device, pool}'Информация о выделенном устройстве содержится в его имени:
| Имя | Значение |
|---|---|
gpu-00000000-d5-00-0 |
Целая карта по PCI-адресу 0000:d5:00.0. |
gpu-00000000-d5-00-0-mps |
Разделяемая доля этой карты через MPS. |
gpu-00000000-d5-00-0-ts |
Разделяемая доля этой карты через time-slicing. |
part-gpu-00000000-d5-00-0-p15-s0-n2 |
MIG-партиция — профиль с ID 15, начало со слайса 0, ширина два слайса. |
part-…-n2-mps |
Разделяемая доля этой MIG-партиции. |
Чтобы узнать список объектов, для которых зарезервирован ResourceClaim, используйте команду:
d8 k -n <неймспейс> get resourceclaim <имя> -o jsonpath='{.status.reservedFor}'Внутри контейнера — обычная проверка:
nvidia-smi -LЕсли вывод пуст, см. «Диагностика» — чаще всего причина в отсутствующей записи resources.claims в спецификации пода.
Справочник атрибутов устройства
Селекторы DeviceClass работают с атрибутами в домене gpu.deckhouse.io, которые в CEL адресуются как device.attributes["gpu.deckhouse.io"].<имя>. Необязательные атрибуты всегда оборачивайте в has(...): отсутствующий атрибут приводит к ошибке всего выражения, а не к значению false.
| Атрибут | Тип | Пример | Примечания |
|---|---|---|---|
vendor |
string | nvidia, metax, amd, intel |
Какой адаптер вендора обслуживает устройство. |
vendorID |
string | 10de |
PCI vendor ID. |
deviceID |
string | 20b0 |
PCI device ID. |
deviceModel |
string | a100-sxm4-40gb |
Нормализованная модель; то же значение, что в лейбле gpu.deckhouse.io/device. |
productName |
string | NVIDIA A100-SXM4-40GB |
Маркетинговое имя по данным драйвера. |
architecture |
string | Ampere, Hopper, CDNA3, Xe-HPC |
Архитектура оборудования в терминах вендора. |
computeAPI |
string | CUDA, ROCm, oneAPI |
Вычислительный стек вендора. |
computeAPIVersion |
string | 13.2 |
|
computeCapability |
string | 8.0 |
Compute capability CUDA у NVIDIA. У вендоров без аналога — пусто. |
driverVersion |
string | 595.71.05 |
|
gpuUUID |
string | GPU-5bb94fb4-… |
|
pciAddress |
string | 00000000:d5:00.0 |
Позволяет закрепить claim за одной конкретной картой. |
bareMetal |
bool | true |
Обязательно для проброса. |
deviceType |
string | physical, partition |
|
placementID |
string | gpu/00000000:d5:00.0 |
|
sharingStrategy |
string | mps, ts |
Отсутствует у эксклюзивных устройств — именно так выражается «эксклюзивно». |
partition |
bool | true |
Присутствует у устройств-партиций. |
partitionTechnology |
string | MIG, SPX, SR-IOV |
Технология партиционирования вендора. |
partitionProfile |
string | 1g.5gb, 2g.10gb, 1g.5gb+me |
Аппаратное имя, с точками. |
partitionProfileID |
int | 20 |
|
partitionSliceStart |
int | 6 |
|
partitionSliceSize |
int | 1 |
Посмотреть, что публикует именно ваш кластер:
Для получения информации обо всех устройствах, зарегистрированных в кластере через DRA, используйте команду:
d8 k get resourceslices -o json \
| jq '.items[].spec.devices[]? | {name, attributes, capacity}'Оси ёмкости
ResourceClaim может запрашивать измеримые величины по двум осям:
| Ось | Значения | Доступность |
|---|---|---|
gpu.deckhouse.io/memory |
величина вида 1Gi |
На всех устройствах. |
sharePercent |
1–100 |
Только у разделяемых вариантов. |