Стадия жизненного цикла модуля: Общедоступная версия
У модуля есть требования для установки
Модуль работает в двух взаимоисключающих режимах, выбор задаётся параметром dra.enabled:
- Режим DRA (
dra.enabled: true) — рекомендуемый режим, в статусе Preview; включается явно. GPU запрашиваются через API Kubernetes с помощью ресурсов ResourceClaim. - Режим Device Plugin — режим по умолчанию, работает без дополнительной настройки. GPU запрашиваются через extended-ресурс
nvidia.com/gpu, а способ разделения настраивается на уровне NodeGroup.
Оба режима разворачиваются в неймспейсе d8-nvidia-gpu.
Режим DRA
DRA-режим находится в статусе Preview. Режим доступен для использования, но его параметры и поведение ещё могут измениться в следующих релизах.
Компоненты
Модуль разделён на не зависящее от вендора ядро и адаптеры под конкретных вендоров. Ядро ничего не знает о конкретных марках GPU: всё, что обращается к оборудованию вендора, скрыто за контрактом, который реализует адаптер вендора.
В режиме DRA модуль создает следующие компоненты:
| Компонент | Тип | Зависимость от вендора | Назначение |
|---|---|---|---|
gpu-controller |
Deployment (до 3 реплик, с лидер-элекшеном) | нет | Контроллер уровня кластера. Реконсилирует PhysicalGPU и GPUClass, генерирует объекты DeviceClass и обслуживает admission-вебхуки модуля. С адаптерами взаимодействует только через CRD PhysicalGPU. Работает на master-узлах. |
gpu-node-agent |
DaemonSet | нет | Обнаружение GPU. Читает /sys хоста и базу PCI ID, сопоставляет устройства с ConfigMap gpu-supported-vendors, создаёт по одному PhysicalGPU на карту и добавляет на узел лейбл gpu.deckhouse.io/vendor=<вендор>. Работает на всех узлах, кроме control-plane, синхронизация — каждые 5 минут. |
dra-plugin |
контейнер в поде каждого адаптера | нет | Плагин DRA для kubelet. Один образ для всех вендоров. |
| адаптер вендора | DaemonSet | да | Узловой слой работы с оборудованием одного вендора. Поставляется как nvidia-adapter и metax-adapter. Подробнее — в разделе «Адаптеры вендоров». |
gpu-dcgm, gpu-dcgm-exporter |
DaemonSet | да (NVIDIA) | Телеметрия DCGM и её экспортер для Prometheus. DCGM — инструмент NVIDIA; другие вендоры поставляли бы свой экспортер вместе со своим адаптером. |
Модуль работает за счёт обнаружения: gpu-node-agent запускается везде и помечает узел найденным вендором, а каждый адаптер планируется через nodeSelector по этому лейблу. На узле без поддерживаемого GPU адаптер не запускается вовсе.
Адаптеры вендоров
Адаптер вендора — это DaemonSet с двумя рабочими контейнерами и защитой метрик:
| Контейнер | Зависит от вендора | Назначение |
|---|---|---|
dra-plugin |
нет | Регистрируется в kubelet по пути /var/lib/kubelet/plugins/gpu.deckhouse.io, реализует NodePrepareResources / NodeUnprepareResources и публикует объекты ResourceSlice, построенные из PhysicalGPU. Все операции с оборудованием делегирует контейнеру адаптера вендора через Unix-сокет внутри пода. |
адаптер вендора (nvidia-adapter, metax-adapter) |
да | Работает с оборудованием. Читает возможности, генерирует CDI-спецификации, выполняет партиционирование и проброс, обогащает статус PhysicalGPU. Для NVIDIA это контейнер nvidia-adapter, использующий NVML. |
kube-rbac-proxy |
нет | Защищает эндпоинт метрик. |
Адаптер реализует контракт Backend с тремя обязательными методами: имя вендора, чтение возможностей и запись CDI. Всё остальное — опциональные интерфейсы возможностей, поэтому вендор реализует только то, что действительно умеет его оборудование:
| Возможность | Что покрывает | NVIDIA |
|---|---|---|
PowerManager |
Чтение и установка лимитов мощности | NVML |
HealthMonitor |
События о состоянии оборудования | события XID |
Partitioner |
Разделение карты на изолированные партиции | MIG |
PassthroughManager |
Передача «сырого» устройства в виртуальную машину | vfio-pci |
SharingManager |
Одновременный и разделённый по времени доступ | MPS, time-slicing |
ProcessInspector |
Какие процессы держат GPU | NVML |
UUIDResolver |
UUID устройства | NVML |
CDIBaseSpecProvider, CDIHookResolver |
Базовая CDI-спецификация и путь к hook | да |
Что именно реализует вендор, видно по opaque-конфигурациям устройства, которые регистрирует его адаптер. NVIDIA регистрирует GpuConfig, MigDeviceConfig и VfioDeviceConfig; MetaX — GpuConfig и VfioDeviceConfig, поэтому целые карты и проброс там работают, а партиционирование — нет. Claim с конфигом того вида, который не зарегистрировал ни один адаптер, отклоняется admission-вебхуком. Подробнее о возможностях модуля в зависимости от вендора — в разделе «Возможности модуля в зависимости от вендора GPU».
Чтобы добавить вендора, требуется написать контейнер под этот контракт, добавить его PCI vendor ID в ConfigMap gpu-supported-vendors и поставить DaemonSet адаптера. CRD при этом не меняется, как и ни один компонент ядра.
Адаптеры поставляются для NVIDIA (10de) и MetaX (9999). Обнаружение также распознаёт AMD (1002) и Intel (8086), поэтому карты этих вендоров появляются как объекты PhysicalGPU, остаются в фазе Pending и никогда не публикуются для выделения (т.к. их возможности не читаются).
Рабочие нагрузки DRA получают GPU через CDI-инъекцию в стандартном container runtime. Поставляемый модулем RuntimeClass nvidia используется только DaemonSet’ами DCGM — обычные DRA-нагрузки не должны указывать runtimeClassName: nvidia.
Условия для включения режима
Три условия определяют, что именно разворачивается. Наборы шаблонов у них разные:
| Условие | Кем выставляется | Значение |
|---|---|---|
dra.enabled |
вашим ModuleConfig | Запрошен режим DRA. |
internal.migrationReady |
хук check_migration |
На неймспейс d8-nvidia-gpu добавлен лейбл gpu.deckhouse.io/managed-by=gpu, то есть модуль им владеет. |
internal.draFeatureGatesReady |
хук dra_preflight |
Все необходимые feature gates для DRA действительно включены в кластере. |
Чтобы понять, какое из условий блокирует развёртывание, посмотрите условия модуля и алерты:
d8 k get module gpu -o jsonpath='{.status.conditions}' | jqГотовность feature gates для DRA
Feature gates для DRA включает платформа Deckhouse, а не этот модуль. Перед развёртыванием DRA-стека модуль проверяет, что они действительно включены: реальное состояние читается из метрик kube-apiserver, а не выводится из номеров версий.
Необходимые feature gates:
| Feature gate | Появился в | Что на нём держится |
|---|---|---|
DynamicResourceAllocation |
1.26 | Ядро DRA. |
DRAResourceClaimDeviceStatus |
1.32 | Состояние отдельных устройств в ResourceClaim. |
DRAPartitionableDevices |
1.33 | MIG-партиции как устройства над общими счётчиками. |
DRADeviceBindingConditions |
1.34 | Рукопожатие до привязки, разрешающее конфликты размещения. |
DRAConsumableCapacity |
1.34 | Доли карты: MPS и time-slicing через claim. |
DRAExtendedResource |
1.34 | Запрос GPU по имени класса. |
Три из них появляются только в Kubernetes 1.34 — поэтому DRA-режиму нужна эта версия, хотя само ядро DRA работает и на 1.33.
При dra.enabled: true модуль разворачивает рабочий DRA-стек — контроллер, node agent, вендорные адаптеры и всё, что обслуживает ResourceClaim, — только если включены все требуемые DRA feature gates. Иначе эта часть не разворачивается, а модуль сообщает причину через module condition FeatureGatesReady=False и alert D8GpuDraFirstEnablementBlocked (либо D8GpuDraFeatureGatesCheckError, если состояние gates прочитать не удалось вовсе). Режим Device Plugin этой проверкой не затрагивается.
Подготовка узла гейтуется отдельно, и на кластере без gates это различие существенно. Скрипты NodeGroupConfiguration применяются сразу при dra.enabled: true и завершённой миграции, не дожидаясь gates: узел получает containerd drop-in, sysctl и лейблы готовности, потому что всё это снимается при выключении модуля. Два шага, чей эффект не отменяется, — правка командной строки ядра для IOMMU с запросом перезагрузки и привязка к vfio-pci — не применяются, пока gates подтверждённо отсутствуют и рабочий стек ни разу не разворачивался. То есть «gates отсутствуют» означает, что не приходит рабочий стек и не приходит необратимая половина подготовки узла, а не что не рендерится вообще ничего.
DRA-режим требует, чтобы эти feature gates были включены платформой. На поддерживаемой версии Deckhouse они включаются автоматически; если они не включены, установка dra.enabled: true не развернёт рабочий DRA-стек, не применит необратимые шаги подготовки узла и поднимет alert. Обновите Deckhouse, чтобы активировать DRA-режим. Модуль не модифицирует аргументы control plane и kubelet.
Если необходимый gate будет отключён позже, когда режим DRA уже активен — например, ручным изменением или неполным обновлением, — появится алерт D8GpuDraFeatureGateDisabledOnComponent. Этот алерт также появляется при расхождении на отдельных kubelet, которое проверка только по apiserver обнаружить не может.
Принцип работы модуля при запуске пода
Запуск пода, запрашивающего GPU, включает следующие этапы:
gpu-node-agentобнаруживает PCI-устройства на узле и создаёт PhysicalGPU для каждой карты, добавляя лейблыgpu.deckhouse.io/vendor,gpu.deckhouse.io/device,gpu.deckhouse.io/nodeи поддерживаемые стратегии разделения (gpu.deckhouse.io/sharing.mps,gpu.deckhouse.io/sharing.ts).- Адаптер вендора для этой карты читает её возможности через библиотеку вендора — на NVIDIA это NVML, — заполняет
.status.capabilitiesобъекта PhysicalGPU (архитектура, compute API, версия драйвера, поддерживаемые профили партиций) и переводит объект вReady. - Как только PhysicalGPU переходит в
Ready,dra-pluginпубликует объекты ResourceSlice — по набору устройств на узел — описывающие всю карту, все возможные партиции и их sharing-варианты. - Администратор при необходимости создаёт GPUClass;
gpu-controllerпревращает его в набор объектов DeviceClass, которые пользователи запрашивают по имени или через extended-ресурс. - Пользователь создаёт ResourceClaim (обычно через
ResourceClaimTemplate) и ссылается на него из пода. Планировщик сопоставляет claim с опубликованными устройствами. - На выбранном узле
dra-pluginвызываетNodePrepareResourcesи передаёт запрос адаптеру вендора, который выполняет необходимую реконфигурацию — на NVIDIA это создание MIG-партиции, запуск control-демона MPS или привязка карты кvfio-pci— и возвращает имя CDI-устройства. - Контейнер запускается с предоставленным GPU. При удалении под проходит обратный путь, и партиция, созданная под claim, удаляется.
Публикуемые атрибуты устройств
Устройства в ResourceSlice несут атрибуты в домене gpu.deckhouse.io, а в их именах кодируется информация о том, чем является устройство: целой картой, MIG-партицией или sharing-вариантом. Подробнее — в разделах «Атрибуты устройства» и «Имена устройств».
Каждое устройство объявляет bindsToNode: true с bindingConditions: ["Ready"] и bindingFailureConditions: ["BindingFailed"], поэтому неудачная подготовка проявляется как условие BindingFailed на claim, а не как молча висящий под.
Причины конфликта MIG-раскладок
MIG-партиции моделируются как partitionable devices. Каждый узел публикует набор общих счётчиков на карту (slices, slice-0…slice-7, multiprocessors, copy-engines, decoders, jpeg-engines, ofa-engines), и каждое устройство — как целая карта, так и каждая возможная партиция — объявляет, какие счётчики оно потребляет.
Из этого следуют выводы:
- Планировщик обеспечивает взаимное исключение сам. Выделение партиции
7gпотребляет все счётчики слайсов, поэтому никакая другая партиция на этой карте одновременно выделена быть не может. - MIG-партиции эфемерны. Партиция существует только между
NodePrepareResourcesиNodeUnprepareResources. Постоянной MIG-раскладки, которой нужно управлять, нет: раскладка — следствие того, что запрошено в данный момент.
Жизненный цикл PhysicalGPU
Фазы жизненного цикла PhysicalGPU отображаются в поле .status.phase:
| Фаза | Значение |
|---|---|
Pending |
Обнаружено, ещё не осмотрено адаптером. |
Initializing |
Адаптер читает возможности карты. |
Ready |
Готово к использованию; устройства опубликованы в ResourceSlice. |
Reconfiguring |
Выполняется изменение MIG или привязки драйвера. |
Passthrough |
Карта привязана к vfio-pci для VFIO-проброса и недоступна как CUDA-устройство. |
Failed |
Не удалось прочитать возможности карты или операция завершилась ошибкой. |
Lost |
Карта исчезла с узла. |
Фаза выводится, а не выставляется напрямую. Подробности несут пять условий:
| Условие | Значение | Причины |
|---|---|---|
AdapterReady |
Адаптер вендора инициализировался и прочитал возможности. | NotScheduled, DriverNotReady, DriverInitFailed, ToolkitNotReady, VFIORequiresBareMetal, Ready, Lost |
HardwareHealthy |
Оборудование не сообщает об ошибках. | Healthy, Unhealthy, Unknown |
Reconfiguring |
Выполняется изменение партиций или привязки драйвера. | None, Requested, Draining, InProgress, Completing, DisruptionRequired, DisruptionPending, RebootRequested |
Reclaim |
Ход инициированного администратором сброса. | InProgress, Done, Refused, Failed |
Managed |
Почему устройство управляется модулем или нет. | Подробнее — в разделе «Состояние управления устройством». |
Устройства публикуются только в фазе Ready, поэтому карта с AdapterReady=False не появляется ни в одном ResourceSlice.
При выводе фазы сначала читается привязка драйвера: карта со значением .status.driverBinding.mode равным Passthrough получает фазу Passthrough независимо от условий, потому что у карты, привязанной к vfio-pci, законно нет готовности адаптера и иначе она была бы ошибочно определена как Initializing. Для остальных режимов применяется таблица условий, поэтому возврат карты на compute-драйвер выводит её из этой фазы естественным образом.
| Условия | Фаза |
|---|---|
driverBinding.mode == Passthrough |
Passthrough (имеет приоритет) |
AdapterReady=False/NotScheduled |
Pending |
AdapterReady=False/DriverNotReady или /ToolkitNotReady |
Initializing |
AdapterReady=False/DriverInitFailed или /VFIORequiresBareMetal |
Failed |
AdapterReady=False/Lost |
Lost |
AdapterReady=True, HardwareHealthy=False |
Failed |
AdapterReady=True, HardwareHealthy=True, Reconfiguring=True |
Reconfiguring |
AdapterReady=True, HardwareHealthy=True, Reconfiguring=False |
Ready |
Обратите внимание: запрос проброса на виртуализированном узле не доходит до Passthrough, а завершается ошибкой — он попадает в AdapterReady=False/VFIORequiresBareMetal, что выводит фазу Failed.
Реконфигурация проходит через Requested → Draining → InProgress → Completing → None. Если аппаратный переход требует вмешательства на уровне узла, адаптер вендора поднимает DisruptionRequired, а контроллер проводит его через DisruptionPending к RebootRequested.
Привязка драйвера
.status.driverBinding фиксирует, что реально привязано в ядре и кто владеет устройством. Владение полями разделено через Server-Side Apply:
| Поле | Владелец | Значение |
|---|---|---|
driver |
gpu-node-agent |
Имя драйвера ядра из sysfs — nvidia, vfio-pci или пусто, если ничего не привязано. Считается достоверным источником. |
mode |
gpu-node-agent |
Классифицированный режим работы: Compute, Passthrough, Unbound или Unknown. Не зависит от транспорта — выводить транспорт из него нельзя. |
managementState |
gpu-controller |
Кто управляет устройством: Managed, Excluded, Orphaned, Unclaimed или Unknown. |
В выводе kubectl get physicalgpus поле managementState показано в колонке Management. У driver и mode есть собственные колонки, KernelDriver и Mode, но обе — расширенные: чтобы их увидеть, добавьте -o wide.
Состояние управления устройством
managementState отвечает на вопрос, кто управляет устройством: управляет модуль, оператор сознательно исключил устройство, его никто не занял, это непреднамеренный orphan, оставшийся на passthrough-привязке, — либо ни один из этих ответов не установлен. Только это значение управляет допуском к reclaim, и из пяти ответов допуск открывает единственный — Orphaned. Расширяемую причину несёт условие Managed:
| Причина | Условие | managementState |
Значение |
|---|---|---|---|
BackingAllocation |
True |
Managed |
Привязка подкреплена активным владеющим ResourceClaim. |
NoBackingAllocation |
True |
Unclaimed |
Устройство опознано, привязка в режиме Compute, отказ не объявлен, и ни один claim его не держит. Обычная свободная карта. |
ExcludedReleasePending |
False |
Managed |
Отказ объявлен, но привязка ещё жива, поэтому устройство пока не подлежит reclaim. |
DeliberatelyExcluded |
False |
Excluded |
Оператор исключил устройство, и привязка уже освобождена. |
OrphanPending |
Unknown |
Unknown |
Замечена бесхозная passthrough-привязка, но период ожидания не истёк. Не подлежит ни reclaim, ни алертингу. |
Orphaned |
False |
Orphaned |
Бесхозная passthrough-привязка, продержавшаяся весь период ожидания. Требует действий. |
NoLiveBinding |
Unknown |
Unknown |
Управлять нечем: драйвер не привязан, привязанный не удалось классифицировать либо привязка ещё не опубликована. Отсчёт периода не запускается. |
Indeterminate |
Unknown |
Unknown |
Прочитать не удалось ничего: у объекта нет имени Node или нет PCI-адреса, и достаточно отсутствия любого из двух, — поэтому и определять нечего. Отсчёт периода не запускается. |
В этой таблице легко ошибиться дважды. True не значит, что карта занята: условие отвечает, считает ли модуль привязку своей, и BackingAllocation с NoBackingAllocation — два способа, какими она может ею быть; никем не взятая карта остаётся модулевой. А Unknown не значит, что контроллер ничего не прочитал. Два случая из трёх — как раз прочитанные факты: живой привязки нет (NoLiveBinding) либо бесхозная passthrough-привязка ещё внутри периода ожидания (OrphanPending); читать было нечего только в Indeterminate. Во всех трёх не хватает установленного ответа, а не чтения, и ни один из них не открывает reclaim.
Исключить устройство можно аннотацией gpu.deckhouse.io/unmanaged. Действует исключение не сразу: пока привязка жива, устройство продолжает читаться как Managed — с причиной ExcludedReleasePending условия Managed, — и переходит в Excluded только после того, как привязка освобождена. Поэтому исключённая карта не становится пригодной для reclaim, пока на ней может оставаться нагрузка, — а у карты на vfio-pci привязка как раз живая, и именно на ней утверждение «проставили аннотацию — читается Excluded» оказывается ложным.
Бесхозная passthrough-привязка должна непрерывно продержаться две минуты, прежде чем будет классифицирована как Orphaned — это не даёт кратковременной гонке породить ложную бесхозность. Passthrough здесь — условие классификации, а не её типичный случай: привязка в режиме Compute без claim’а — это обычная свободная карта, она читается как Unclaimed, пока свободна, и в период ожидания не попадает вовсе.
Запросить сброс можно аннотацией gpu.deckhouse.io/reclaim. Ход выполнения контроллер отражает в условии Reclaim, там же он и отклоняет — с указанием причины, а не молча — любой запрос, который выполнить не может. Первым применяется гейт по колонке Management: допуск открывает только Orphaned, поэтому карта в состоянии Managed, Unclaimed, Excluded или Unknown отклоняется независимо от всего остального, и отказ называет именно это значение, а не то, что за ним стоит. Сам собой разрешается один случай — Unknown с причиной OrphanPending, когда период ожидания ещё идёт: такой запрос не отклоняется, а удерживается, и reclaim начинается сам, как только карта станет Orphaned. Читать нужно сначала колонку Management, а не текст отказа: именно колонка говорит, чего требует ситуация — подождать, освободить claim или восстановить идентичность карты. За этим гейтом контроллер ещё раз проверяет занятость и остаётся fail-closed, поэтому отказ можно получить и на карте, которая действительно читается как Orphaned: если выделенный ResourceClaim всё-таки нашёлся либо занятость установить не удалось — без имени Node или без PCI-адреса claim не с чем сопоставлять, и достаточно отсутствия любого из двух.
Начавшийся, но не завершившийся сброс заканчивается значением Failed: если через две минуты после начала карта всё ещё на passthrough-драйвере, в условии сообщается, что driverBinding.mode по-прежнему Passthrough и что может потребоваться перезагрузка узла. Это тот исход, за которым нужно следить, — модуль не может разрешить его самостоятельно.
Admission-вебхуки
Обслуживаются gpu-controller:
| Вебхук | Объекты | Failure policy | Назначение |
|---|---|---|---|
gpuclass |
GPUClass CREATE/UPDATE |
Fail | Валидирует определение класса. Пока gpu-controller недоступен, запись GPUClass отклоняется. |
podgpuresourceclaim |
Pod CREATE | Fail | Путь через extended-ресурс: если контейнер запрашивает ресурс gpu.deckhouse.io/*, генерирует соответствующий ResourceClaim. Не применяется в неймспейсах kube-system и d8-*. |
resourceclaimparameters |
ResourceClaim, ResourceClaimTemplate, DeviceClass |
Ignore | Валидирует opaque-конфиги устройств модуля (GpuConfig, MigDeviceConfig, VfioDeviceConfig). |
podresourceclaim |
Pod с resourceClaims |
Ignore | Проверки согласованности ссылок на claim. |
Кроме того, ValidatingAdmissionPolicy с именем gpu-vfio-switch-guard ограничивает привилегированные задачи, которые nvidia-adapter создаёт для переключения драйвера VFIO (см. ниже). Любая задача в d8-nvidia-gpu, которая привилегирована или использует hostPID — достаточно одного из двух, чтобы проверка сработала, — обязан полностью совпасть с разрешённым профилем:
- ServiceAccount
vfio-switch, без автомонтирования токена - ровно один контейнер с точным образом адаптера
hostNetworkиhostIPCвыставлены в falserestartPolicy: Never- host-пути только
/sys,/dev,/procи/var/lib/nvidia-adapter/checkpoint - команда вида
/app/nvidia-adapter switch <pci-адрес>с каноническим PCI-адресом
VFIO-проброс
Для проброса карту нужно отвязать от драйвера NVIDIA и привязать к vfio-pci. nvidia-adapter делает это не в своём процессе: он запускает короткоживущую привилегированную задачу (/app/nvidia-adapter switch <pci>) под ServiceAccount без прав в Kubernetes, единственное назначение которого — быть идентичностью для admission. Политика gpu-vfio-switch-guard выше и является ограничителем этой возможности.
Подготовка узла выполняется через NodeGroupConfiguration (см. ниже). Карта в режиме проброса имеет .status.phase: Passthrough и не публикуется как CUDA-устройство.
Для проброса требуется IOMMU, включённый и в BIOS, и в командной строке ядра. Модуль может добавить параметр ядра сам, но для этого потребуется перезагрузка узла.
Если переключение драйвера не удалось
Перепривязка карты между драйверами — единственная операция, способная оставить оборудование в состоянии, которое Kubernetes не исправит самостоятельно, поэтому для неё предусмотрен явный путь восстановления.
Неудачная привязка к vfio-pci обычно откатывается на драйвер NVIDIA, и claim просто завершается ошибкой. В следующих случаях откат невозможен:
- после неудачной или зависшей отвязки карта осталась вообще без драйвера;
- при освобождении карту не удалось вернуть на драйвер NVIDIA, и она осталась на
vfio-pciили без драйвера.
В обоих случаях адаптер публикует на PhysicalGPU условие Reconfiguring=DisruptionRequired с сообщением, где указан PCI-адрес и сказано, что требуется перезагрузка узла. Далее gpu-controller проводит передачу через DisruptionPending к RebootRequested. Когда восстановление увидит карту снова на её базовом драйвере — достоверное подтверждение, что перезагрузка действительно произошла, — условие финализируется через Completing.
Ошибки также отражаются в событиях: VFIOPrepareFailed и VFIOUnprepareFailed, а на успешном пути — VFIOPrepareSucceeded.
Подготовка узла
Скрипты NodeGroupConfiguration выполняются на каждом узле в порядке весов. Именно они делают узел пригодным для GPU-стека.
| Скрипт | Вес | Назначение |
|---|---|---|
gpu-check.sh |
6 | Проверяет наличие и работоспособность драйвера NVIDIA. При ошибке снимает node.deckhouse.io/gpu-setup-complete. На узлах только под VFIO пропускает проверки NVIDIA runtime. |
gpu-runtime.sh |
31 | Записывает drop-in для containerd, регистрирующий nvidia как явный runtime handler, а не как runtime по умолчанию. Это сделано намеренно: именно так Pod без claim не видит GPU. |
gpu-vfio-iommu.sh |
39 | Только на bare-metal. Определяет вендора CPU, добавляет intel_iommu=on или amd_iommu=on в командную строку ядра и запрашивает перезагрузку через аннотации обновления узла Deckhouse. |
gpu-vfio.sh |
40 | Загружает модули ядра vfio и vfio_iommu_type1, приводит привязку PCI-функций GPU к нужному состоянию и выставляет node.deckhouse.io/gpu-vfio-ready в true или false. |
gpu-sysctl.sh |
41 | Устанавливает net.core.bpf_jit_harden=1, требуемый NVIDIA Container Toolkit. При очистке восстанавливает прежнее значение. Поддерживает каталог /var/lib/node-feature-discovery/features.d с правами 0755: туда GFD публикует признаки GPU, а непривилегированный worker NFD их читает. Каталог вынесен из /etc/kubernetes, потому что шаг hardening платформы при каждом прогоне bashible делает это дерево доступным только root. |
gpu-setup.sh |
42 | Финальный шлюз: выставляет или снимает node.deckhouse.io/gpu-setup-complete. |
Лейблы узла, влияющие на планирование
Если на узле нет DRA-подов, причина обычно в одном из этих лейблов:
| Лейбл | Кем выставляется | Эффект |
|---|---|---|
node.deckhouse.io/gpu-setup-complete |
gpu-setup.sh |
Должен присутствовать. Пока его нет, ни один компонент GPU-стека на узел не планируется. |
gpu.deckhouse.io/vendor |
gpu-node-agent |
Указывает вендора GPU, найденного на узле. Каждый адаптер планируется через nodeSelector по этому лейблу; на NVIDIA-узлах он также управляет DaemonSet’ами DCGM. |
gpu.deckhouse.io/enabled |
администратор | Переключатель отказа. Значение false исключает узел из управления GPU. |
gpu.deckhouse.io/maintenance=true |
администратор | Исключает узел на время работ с ним. |
node.deckhouse.io/gpu-vfio-ready |
gpu-vfio.sh |
true только когда загружены модули VFIO и присутствуют группы IOMMU. Используйте его как nodeSelector для нагрузок с пробросом: готовность VFIO не публикуется как атрибут устройства, поэтому её нельзя выразить в селекторе DeviceClass. |
gpu.deploy.dcgm, gpu.deploy.dcgm-exporter |
nvidia-adapter |
Временно выставляются в paused-for-mig-change, чтобы DCGM не держал GPU во время реконфигурации. Исчезновение этих подов во время изменения MIG — ожидаемое поведение. |
Удаление модуля
При отключении или удалении модуля с dra.enabled: true Helm-хук pre-delete запускает задачу, которая удаляет все PhysicalGPU в кластере и ждёт их исчезновения до 600 секунд. Это позволяет gpu-controller снять свои финалайзеры до удаления CRD.
Если задача истекла по таймауту, поищите объекты с зависшими финалайзерами:
d8 k get physicalgpus -AРежим Device Plugin
В этом режиме dra.enabled равно false — это значение по умолчанию. Разделение GPU настраивается на уровне NodeGroup через spec.gpu, а не на уровне рабочей нагрузки.
Режим Device Plugin настраивает GPU на уровне узла: группа узлов обслуживает один способ разделения, а нагрузки запрашивают nvidia.com/gpu. Режим DRA стоит выбирать, когда нагрузки должны сами определять свои требования — MIG-партиции, долю GPU или проброс. Перейти позже можно без ручной очистки (подробнее — в разделе «Как мигрировать с режима Device Plugin на DRA?»).
Компоненты
В режиме Device Plugin модуль создает следующие компоненты:
| Компонент | Тип | Назначение |
|---|---|---|
node-feature-discovery-master |
Deployment | Собирает лейблы аппаратных возможностей с воркеров и публикует их как лейблы узлов (feature.node.kubernetes.io/*, nvidia.com/*). |
node-feature-discovery-gc |
Deployment | Удаляет устаревшие лейблы NFD для удалённых узлов. |
node-feature-discovery-worker |
DaemonSet | Определяет устройства PCI/USB на каждом GPU-узле и пишет feature-файлы для NFD master. |
gpu-feature-discovery-<ng> |
DaemonSet, на каждую NodeGroup | Опрашивает драйвер через NVML и пишет подробные feature-файлы GPU (nvidia.com/gpu.count, nvidia.com/mig.capable, …). |
nvidia-device-plugin-<ng> |
DaemonSet, на каждую NodeGroup | Публикует планировщику nvidia.com/gpu (Exclusive, TimeSlicing) или nvidia.com/mig-<профиль> (MIG). |
nvidia-mig-manager |
DaemonSet | Применяет MIG-профили на узлах с A100/H100: вытесняет нагрузку, реконфигурирует, при необходимости перезагружает и возвращает узел в работу. |
nvidia-dcgm |
DaemonSet | Демон DCGM. |
nvidia-dcgm-exporter |
DaemonSet | Экспортер метрик DCGM для Prometheus. |
Ключевые лейблы
node.deckhouse.io/gpu=""— выставляется хуком модуля на узлах Node Group с GPU.node.deckhouse.io/device-gpu.config=<Exclusive|TimeSlicing|MIG>— выставляется хуком; выбирает конфигурацию device plugin и GFD.node.deckhouse.io/gpu-setup-complete=""— выставляетсяgpu-sysctl.shпосле локальных проверок; пока лейбла нет, стек на узел не планируется.feature.node.kubernetes.io/pci-*.present=true— публикуется NFD.nvidia.com/*(например,nvidia.com/gpu.count,nvidia.com/mig.capable) — публикуется NFD из feature-файлов GFD.nvidia.com/mig.config=<профиль|all-disabled>— выставляется хуком: желаемый MIG-профиль илиall-disabledдля отката MIG.nvidia.com/mig.config.state=<pending|rebooting|success|failed>— выставляетсяnvidia-mig-manager.- taint
mig-reconfigure=true:NoSchedule— удерживаетсяnvidia-mig-managerна время реконфигурации. - аннотации
update.node.deckhouse.io/disruption-approved,.../draining,.../drained— контролируемый drain при изменениях MIG.
Принцип работы модуля в режиме Device Plugin
Принцип работы модуля в режиме Device Plugin следующий:
- Администратор кластера задаёт или меняет
spec.gpuв NodeGroup. - Helm разворачивает или удаляет компоненты в неймспейсе
d8-nvidia-gpu: GFD и device plugin как DaemonSet’ы на каждую NodeGroup, NFD master/gc на control-plane, NFD worker и DCGM на GPU-узлах. - Хук модуля обновляет лейблы узла — добавляет
node.deckhouse.io/gpuиdevice-gpu.config, а для MIG ещё иnvidia.com/mig.config; при отключении GPU удаляет их и выставляетnvidia.com/mig.config=all-disabledдля запуска отката. - Скрипты NodeGroupConfiguration выполняются по весам:
gpu-check.sh→gpu-runtime.sh→gpu-sysctl.sh. При успехе узел получаетnode.deckhouse.io/gpu-setup-complete. - Стек сходится: NFD публикует лейблы, GFD пишет feature-файлы GPU, device plugin публикует ресурсы, DCGM начинает отдавать метрики.
Настройка выделения GPU по стратегии Exclusive
Чтобы GPU выделялся без разделения (каждый GPU полностью выделяется одному поду), выполните следующие действия:
- Задайте
spec.gpu.sharing: Exclusive. - Модуль устанавливает лейбл
node.deckhouse.io/device-gpu.config=Exclusiveи поднимает GFD и device plugin. - Узел публикует
nvidia.com/gpu; каждый под получает целый GPU.
Для отключения удалите spec.gpu из NodeGroup. Модуль снимет свои лейблы, NodeGroupConfiguration откатит изменения runtime и sysctl, а NFD GC со временем очистит лейблы nvidia.com/*.
Настройка выделения GPU по стратегии TimeSlicing
Чтобы GPU выделялся с разделением по времени (несколько подов могут одновременно использовать один GPU, поочередно получая доступ к его вычислительным ресурсам), выполните следующие действия:
- Задайте
spec.gpu.sharing: TimeSlicingи при необходимостиspec.gpu.timeSlicing.partitionCount(по умолчанию 4). - Device plugin применяет конфигурацию time-slicing.
- Узел по-прежнему публикует
nvidia.com/gpu, но с большим числом виртуальных слотов.
Переключение на Exclusive или удаление spec.gpu возвращает исходное состояние.
Настройка выделения GPU по стратегии MIG
Чтобы GPU выделялся с разделением на несколько экземпляров (технология NVIDIA MIG, при наличии поддержки), выполните следующие действия:
- Задайте
spec.gpu.sharing: MIGиspec.gpu.mig.partedConfig: <профиль>(например,all-1g.5gb). - Модуль устанавливает лейблы
node.deckhouse.io/device-gpu.config=MIGиnvidia.com/mig.config=<профиль>. - Как только NFD опубликует
nvidia.com/mig.capable=true, на узел планируетсяnvidia-mig-manager, реагирующий на изменения лейблаnvidia.com/mig.config. - Для реконфигурации он устанавливает лейбл
nvidia.com/mig.config.state=pending, приостанавливает клиентов GPU черезnvidia.com/gpu.deploy.*=paused-for-mig-change, применяет taintmig-reconfigure=true:NoSchedule, ждёт подтверждения drain, удаляет поды-клиенты GPU на узле, применяет профиль (при необходимости с перезагрузкой), затем выставляетsuccessилиfailed, снимает taint, снимает cordon и восстанавливает лейблыgpu.deploy.*. - После этого кластер публикует ресурсы вида
nvidia.com/mig-1g.5gb.
Если под nvidia-mig-manager перезапущен во время выполнения операции, его хук preStop дожидается завершения активной операции, затем снимает cordon, taint и аннотации drain по принципу best-effort.
Откат MIG. Переключение с MIG или удаление spec.gpu выставляет лейбл nvidia.com/mig.config=all-disabled, который менеджер применяет тем же способом. После успешного отката лейблы nvidia.com/mig.config и nvidia.com/mig.config.state удаляются, чтобы менеджер не продолжал ждать на узле. На GPU без поддержки MIG (nvidia.com/mig.capable=false) менеджер не запускается — для них используйте Exclusive или TimeSlicing.
Общая инфраструктура модуля
Независимо от режима работы модуль использует следующую инфраструктуру:
| Элемент | Что это |
|---|---|
Неймспейс d8-nvidia-gpu |
Содержит стек в обоих режимах. Помечен так, чтобы Prometheus подхватывал правила модуля. |
Secret gpu-module-registry |
Secret для получения образов, на который ссылается каждый ServiceAccount модуля. |
ConfigMap gpu-supported-vendors |
Таблица PCI-вендоров, используемая gpu-node-agent при обнаружении. |
OperationPolicy gpu-baseline, SecurityPolicy gpu-restricted |
Опциональные ограничения, применяются к неймспейсам с лейблом operation-policy.deckhouse.io/gpu=true или security.deckhouse.io/gpu=restricted. |
| TLS-материал | Локальный для модуля корневой CA (gpu-ca) выпускает сертификаты для вебхуков и прокси метрик. Управляется хуками, действий оператора не требует. |
Мониторинг
Модуль экспортирует собственные метрики наряду с метриками DCGM:
| Источник | Метрики |
|---|---|
gpu-controller, nvidia-adapter |
gpu_inventory_devices_total, gpu_inventory_devices_state, gpu_inventory_handler_errors_total, gpu_bootstrap_node_phase, gpu_bootstrap_condition, gpu_bootstrap_handler_errors_total, gpu_resourceclaim_info, gpu_resourceclaim_requested_devices |
| хуки модуля | gpu_dra_mode, gpu_dra_feature_gate_missing, gpu_dra_feature_gates_check_error |
gpu-dcgm-exporter |
DCGM_FI_* |
Конфигурация сбора метрик создаётся автоматически: ServiceMonitor’ы для gpu-controller и nvidia-adapter и PodMonitor для экспортера DCGM. Её можно отключить параметром monitoring.serviceMonitor.
Дашборды Grafana поставляются для обоих режимов — обзор кластера, представления по узлам и неймспейсам, инвентарь GPU и DRA-нагрузки.
Алерты:
| Алерт | Значение |
|---|---|
D8GpuDraFirstEnablementBlocked |
Необходимые feature gates для DRA отсутствуют; DRA-стек не развёрнут. |
D8GpuDraFeatureGateDisabledOnComponent |
Gate отключён на apiserver, планировщике или kubelet при активном режиме DRA. |
D8GpuDraFeatureGatesCheckError |
Не удалось проверить состояние feature gates — сама проверка готовности завершается ошибкой. |
D8GPUControlPlaneControllerTargetDown |
Prometheus не может собрать метрики gpu-controller. |
D8GPUControlPlaneControllerTargetAbsent |
Target gpu-controller исчез. |
D8GPUControlPlaneControllerPodIsNotReady |
Под gpu-controller не готов. |