Стадия жизненного цикла модуля: Общедоступная версия

У модуля есть требования для установки

Модуль работает в двух взаимоисключающих режимах, выбор задаётся параметром 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, включает следующие этапы:

  1. 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).
  2. Адаптер вендора для этой карты читает её возможности через библиотеку вендора — на NVIDIA это NVML, — заполняет .status.capabilities объекта PhysicalGPU (архитектура, compute API, версия драйвера, поддерживаемые профили партиций) и переводит объект в Ready.
  3. Как только PhysicalGPU переходит в Ready, dra-plugin публикует объекты ResourceSlice — по набору устройств на узел — описывающие всю карту, все возможные партиции и их sharing-варианты.
  4. Администратор при необходимости создаёт GPUClass; gpu-controller превращает его в набор объектов DeviceClass, которые пользователи запрашивают по имени или через extended-ресурс.
  5. Пользователь создаёт ResourceClaim (обычно через ResourceClaimTemplate) и ссылается на него из пода. Планировщик сопоставляет claim с опубликованными устройствами.
  6. На выбранном узле dra-plugin вызывает NodePrepareResources и передаёт запрос адаптеру вендора, который выполняет необходимую реконфигурацию — на NVIDIA это создание MIG-партиции, запуск control-демона MPS или привязка карты к vfio-pci — и возвращает имя CDI-устройства.
  7. Контейнер запускается с предоставленным 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 выставлены в false
  • restartPolicy: 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 следующий:

  1. Администратор кластера задаёт или меняет spec.gpu в NodeGroup.
  2. Helm разворачивает или удаляет компоненты в неймспейсе d8-nvidia-gpu: GFD и device plugin как DaemonSet’ы на каждую NodeGroup, NFD master/gc на control-plane, NFD worker и DCGM на GPU-узлах.
  3. Хук модуля обновляет лейблы узла — добавляет node.deckhouse.io/gpu и device-gpu.config, а для MIG ещё и nvidia.com/mig.config; при отключении GPU удаляет их и выставляет nvidia.com/mig.config=all-disabled для запуска отката.
  4. Скрипты NodeGroupConfiguration выполняются по весам: gpu-check.sh → gpu-runtime.sh → gpu-sysctl.sh. При успехе узел получает node.deckhouse.io/gpu-setup-complete.
  5. Стек сходится: NFD публикует лейблы, GFD пишет feature-файлы GPU, device plugin публикует ресурсы, DCGM начинает отдавать метрики.

Настройка выделения GPU по стратегии Exclusive

Чтобы GPU выделялся без разделения (каждый GPU полностью выделяется одному поду), выполните следующие действия:

  1. Задайте spec.gpu.sharing: Exclusive.
  2. Модуль устанавливает лейбл node.deckhouse.io/device-gpu.config=Exclusive и поднимает GFD и device plugin.
  3. Узел публикует nvidia.com/gpu; каждый под получает целый GPU.

Для отключения удалите spec.gpu из NodeGroup. Модуль снимет свои лейблы, NodeGroupConfiguration откатит изменения runtime и sysctl, а NFD GC со временем очистит лейблы nvidia.com/*.

Настройка выделения GPU по стратегии TimeSlicing

Чтобы GPU выделялся с разделением по времени (несколько подов могут одновременно использовать один GPU, поочередно получая доступ к его вычислительным ресурсам), выполните следующие действия:

  1. Задайте spec.gpu.sharing: TimeSlicing и при необходимости spec.gpu.timeSlicing.partitionCount (по умолчанию 4).
  2. Device plugin применяет конфигурацию time-slicing.
  3. Узел по-прежнему публикует nvidia.com/gpu, но с большим числом виртуальных слотов.

Переключение на Exclusive или удаление spec.gpu возвращает исходное состояние.

Настройка выделения GPU по стратегии MIG

Чтобы GPU выделялся с разделением на несколько экземпляров (технология NVIDIA MIG, при наличии поддержки), выполните следующие действия:

  1. Задайте spec.gpu.sharing: MIG и spec.gpu.mig.partedConfig: <профиль> (например, all-1g.5gb).
  2. Модуль устанавливает лейблы node.deckhouse.io/device-gpu.config=MIG и nvidia.com/mig.config=<профиль>.
  3. Как только NFD опубликует nvidia.com/mig.capable=true, на узел планируется nvidia-mig-manager, реагирующий на изменения лейбла nvidia.com/mig.config.
  4. Для реконфигурации он устанавливает лейбл nvidia.com/mig.config.state=pending, приостанавливает клиентов GPU через nvidia.com/gpu.deploy.*=paused-for-mig-change, применяет taint mig-reconfigure=true:NoSchedule, ждёт подтверждения drain, удаляет поды-клиенты GPU на узле, применяет профиль (при необходимости с перезагрузкой), затем выставляет success или failed, снимает taint, снимает cordon и восстанавливает лейблы gpu.deploy.*.
  5. После этого кластер публикует ресурсы вида 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 не готов.