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

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

Как удалить нагрузку, которой выделен GPU?

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

d8 k delete job gpu-hello

В результате удалятся связанные с нагрузкой поды и GPU освободится.

Как установить драйвер NVIDIA на GPU-узлы?

Драйвер NVIDIA и NVIDIA Container Toolkit устанавливается через NodeGroupConfiguration. Значение nodeGroups при установке приведите в соответствие со своей GPU-NodeGroup.

Список поддерживаемых платформ NVIDIA Container Toolkit приведён в документации NVIDIA.

Установка драйвера на Ubuntu

Протестировано для Ubuntu 24.04.

apiVersion: deckhouse.io/v1alpha1
kind: NodeGroupConfiguration
metadata:
  name: install-cuda-ubuntu.sh
spec:
  bundles:
  - ubuntu-lts
  content: |
    #!/bin/bash
    set -e

    if ! command -v curl &> /dev/null || ! command -v wget &> /dev/null; then
      sudo apt update
      sudo apt install -y curl wget
    fi

    CUDA_KEYRING_DEB="cuda-keyring_1.1-1_all.deb"
    NVIDIA_GPG_KEY="/usr/share/keyrings/nvidia-container-toolkit-keyring.gpg"

    sudo apt update

    if [ ! -f "$CUDA_KEYRING_DEB" ]; then
      wget -q https://developer.download.nvidia.com/compute/cuda/repos/ubuntu2404/x86_64/$CUDA_KEYRING_DEB
      sudo dpkg -i $CUDA_KEYRING_DEB
      sudo apt update
    fi

    if [ ! -f "$NVIDIA_GPG_KEY" ]; then
      curl -fsSL https://nvidia.github.io/libnvidia-container/gpgkey | \
        sudo gpg --dearmor -o /usr/share/keyrings/nvidia-container-toolkit-keyring.gpg
      curl -s -L https://nvidia.github.io/libnvidia-container/stable/deb/nvidia-container-toolkit.list | \
        sed 's#deb https://#deb [signed-by=/usr/share/keyrings/nvidia-container-toolkit-keyring.gpg] https://#g' | \
        sudo tee /etc/apt/sources.list.d/nvidia-container-toolkit.list
      sudo apt update
    fi

    if ! dpkg-query -W -f='${Status}' "linux-headers-$(uname -r)" 2>/dev/null | grep -q "ok installed"; then
      sudo apt install -y "linux-headers-$(uname -r)"
    fi

    if ! dpkg-query -W -f='${Status}' cuda-drivers 2>/dev/null | grep -q "ok installed"; then
      sudo apt install -y cuda-drivers
    fi

    if ! dpkg-query -W -f='${Status}' nvidia-container-toolkit 2>/dev/null | grep -q "ok installed"; then
      sudo apt install -y nvidia-container-toolkit
    fi

    if ! grep -q "nouveau.modeset=0" /etc/default/grub; then
      sudo sed -i 's/GRUB_CMDLINE_LINUX=""/GRUB_CMDLINE_LINUX="nouveau.modeset=0"/' /etc/default/grub
      sudo update-grub
    fi

  nodeGroups:
  - gpu
  weight: 5

Установка драйвера на Debian

Протестировано для Debian 13.

apiVersion: deckhouse.io/v1alpha1
kind: NodeGroupConfiguration
metadata:
  name: install-cuda.sh
spec:
  bundles:
  - debian
  content: |
    #!/bin/bash
    set -e

    export DEBIAN_FRONTEND=noninteractive

    # Предустановка ответов для debconf, чтобы избежать интерактивных вопросов.
    echo "keyboard-configuration keyboard-configuration/layout select English (US)" | debconf-set-selections
    echo "keyboard-configuration keyboard-configuration/model select Generic 105-key (Intl) PC" | debconf-set-selections
    echo "keyboard-configuration keyboard-configuration/variant select English (US)" | debconf-set-selections
    echo "console-setup console-setup/charmap select UTF-8" | debconf-set-selections

    sudo -E apt update

    if ! command -v curl &> /dev/null || ! command -v wget &> /dev/null; then
      sudo apt update
      sudo apt install -y curl wget
    fi

    CUDA_KEYRING_DEB="cuda-keyring_1.1-1_all.deb"
    NVIDIA_GPG_KEY="/usr/share/keyrings/nvidia-container-toolkit-keyring.gpg"

    sudo apt update

    if [ ! -f "$CUDA_KEYRING_DEB" ]; then
      wget -q https://developer.download.nvidia.com/compute/cuda/repos/debian13/x86_64/$CUDA_KEYRING_DEB
      sudo dpkg -i $CUDA_KEYRING_DEB
      sudo apt update
    fi

    if [ ! -f "$NVIDIA_GPG_KEY" ]; then
      curl -fsSL https://nvidia.github.io/libnvidia-container/gpgkey | \
        sudo gpg --dearmor -o /usr/share/keyrings/nvidia-container-toolkit-keyring.gpg
      curl -s -L https://nvidia.github.io/libnvidia-container/stable/deb/nvidia-container-toolkit.list | \
        sed 's#deb https://#deb [signed-by=/usr/share/keyrings/nvidia-container-toolkit-keyring.gpg] https://#g' | \
        sudo tee /etc/apt/sources.list.d/nvidia-container-toolkit.list
      sudo apt update
    fi

    if ! dpkg-query -W -f='${Status}' "linux-headers-$(uname -r)" 2>/dev/null | grep -q "ok installed"; then
      sudo apt install -y "linux-headers-$(uname -r)"
    fi

    if ! dpkg-query -W -f='${Status}' cuda-drivers 2>/dev/null | grep -q "ok installed"; then
      sudo apt install -y -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confold" cuda-drivers
    fi

    if ! dpkg-query -W -f='${Status}' nvidia-container-toolkit 2>/dev/null | grep -q "ok installed"; then
      sudo apt install -y nvidia-container-toolkit
    fi

    if ! grep -q "nouveau.modeset=0" /etc/default/grub; then
      sudo sed -i 's/GRUB_CMDLINE_LINUX=""/GRUB_CMDLINE_LINUX="nouveau.modeset=0"/' /etc/default/grub
      sudo update-grub
    fi

  nodeGroups:
  - gpu
  weight: 5

Установка драйвера на CentOS

Протестировано для CentOS 9.

apiVersion: deckhouse.io/v1alpha1
kind: NodeGroupConfiguration
metadata:
  name: install-cuda.sh
spec:
  bundles:
  - centos
  content: |
    #!/bin/bash
    set -e
    INSTALL_NEEDED=false

    # Установка драйверов (только при первом запуске).
    if ! rpm -q nvidia-driver-cuda &> /dev/null; then
      dnf install -y curl epel-release dkms gcc make dracut kernel-devel-$(uname -r) elfutils-libelf-devel
      dnf config-manager --add-repo https://developer.download.nvidia.com/compute/cuda/repos/rhel9/x86_64/cuda-rhel9.repo
      rpm --import https://developer.download.nvidia.com/compute/cuda/repos/GPGKEY
      dnf install -y nvidia-driver-cuda nvidia-driver-cuda-libs nvidia-settings nvidia-persistenced
      curl -s -L https://nvidia.github.io/libnvidia-container/stable/rpm/nvidia-container-toolkit.repo | tee /etc/yum.repos.d/nvidia-container-toolkit.repo
      dnf install -y nvidia-container-toolkit
      INSTALL_NEEDED=true
    fi

    # Принудительная пересборка модуля для текущего ядра и обновление initramfs.
    dkms autoinstall
    dracut --force

    # Настройка автозагрузки и отключение nouveau.
    printf '%s\n' 'nvidia' 'nvidia_uvm' 'nvidia_drm' > /etc/modules-load.d/nvidia.conf
    grubby --update-kernel ALL --args="nouveau.modeset=0"

    # Ребут, если были изменения.
    if [ "$INSTALL_NEEDED" = true ]; then
      base64_timer="W1VuaXRdCkRlc2NyaXB0aW9uPWJhc2hpYmxlIHRpbWVyCgpbVGltZXJdCk9uQm9vdFNlYz0xbWluCk9uVW5pdEFjdGl2ZVNlYz0xbWluCgpbSW5zdGFsbF0KV2FudGVkQnk9bXVsdGktdXNlci50YXJnZXQK"
      echo "$base64_timer" | base64 -d | tee /etc/systemd/system/bashible.timer
      systemctl enable bashible.timer
      base64_bashible="W1VuaXRdCkRlc2NyaXB0aW9uPUJhc2hpYmxlIHNlcnZpY2UKCltTZXJ2aWNlXQpFbnZpcm9ubWVudEZpbGU9L2V0Yy9lbnZpcm9ubWVudApFeGVjU3RhcnQ9L2Jpbi9iYXNoIC0tbm9wcm9maWxlIC0tbm9yYyAtYyAiL3Zhci9saWIvYmFzaGlibGUvYmFzaGlibGUuc2ggLS1tYXgtcmV0cmllcyAxMCIKUnVudGltZU1heFNlYz0zaAo="
      echo "$base64_bashible" | base64 -d | tee /etc/systemd/system/bashible.service
      systemctl enable bashible.service
      systemctl reboot
    fi

  nodeGroups:
  - gpu
  weight: 5

Установка драйвера на РЕД ОС

Протестировано для РЕД ОС 8.

apiVersion: deckhouse.io/v1alpha1
kind: NodeGroupConfiguration
metadata:
  name: install-cuda.sh
spec:
  bundles:
  - redos
  content: |
    #!/bin/bash
    set -e
    INSTALL_NEEDED=false

    # Установка драйверов NVIDIA.
    if ! rpm -q nvidia-drivers &> /dev/null || ! rpm -q cuda &> /dev/null || ! rpm -q nvidia-persistenced &> /dev/null; then
      echo "Update system"
      sudo dnf update -y
      sudo dnf install -y nvidia-drivers.x86_64 cuda.x86_64 nvidia-persistenced.x86_64
      INSTALL_NEEDED=true
    fi

    # Установка NVIDIA Container Toolkit.
    if ! rpm -q nvidia-container-toolkit &> /dev/null; then
      echo "NVIDIA container toolkit is not installed. Installing..."
      curl -s -L https://nvidia.github.io/libnvidia-container/stable/rpm/nvidia-container-toolkit.repo | sudo tee /etc/yum.repos.d/nvidia-container-toolkit.repo
      sudo dnf install -y nvidia-container-toolkit
      INSTALL_NEEDED=true
    fi

    # Настройка GRUB и отключение драйвера nouveau.
    if ! grep -q "nouveau.modeset=0" /etc/default/grub; then
      echo "GRUB configuration and nouveau disabling"
      sudo -E grubby --update-kernel ALL --args="nouveau.modeset=0"
      sudo grub2-mkconfig -o /boot/grub2/grub.cfg
      INSTALL_NEEDED=true
    fi

    # Создание сервиса bashible, если были установлены драйверы.
    if [ "$INSTALL_NEEDED" = true ]; then
      base64_timer="W1VuaXRdCkRlc2NyaXB0aW9uPWJhc2hpYmxlIHRpbWVyCgpbVGltZXJdCk9uQm9vdFNlYz0xbWluCk9uVW5pdEFjdGl2ZVNlYz0xbWluCgpbSW5zdGFsbF0KV2FudGVkQnk9bXVsdGktdXNlci50YXJnZXQK"
      echo "$base64_timer" | base64 -d | sudo tee /etc/systemd/system/bashible.timer
      sudo systemctl enable bashible.timer
      base64_bashible="W1VuaXRdCkRlc2NyaXB0aW9uPUJhc2hpYmxlIHNlcnZpY2UKCltTZXJ2aWNlXQpFbnZpcm9ubWVudEZpbGU9L2V0Yy9lbnZpcm9ubWVudApFeGVjU3RhcnQ9L2Jpbi9iYXNoIC0tbm9wcm9maWxlIC0tbm9yYyAtYyAiL3Zhci9saWIvYmFzaGlibGUvYmFzaGlibGUuc2ggLS1tYXgtcmV0cmllcyAxMCIKUnVudGltZU1heFNlYz0zaAo="
      echo "$base64_bashible" | base64 -d | sudo tee /etc/systemd/system/bashible.service
      sudo systemctl enable bashible.service
      sudo systemctl reboot
    fi

  nodeGroups:
  - gpu
  weight: 5

Установка драйвера на Astra Linux

Протестировано для Astra Linux 1.8.

apiVersion: deckhouse.io/v1alpha1
kind: NodeGroupConfiguration
metadata:
  name: install-cuda-astra.sh
spec:
  bundles:
  - astra
  content: |
    #!/bin/bash
    set -e
    export DEBIAN_FRONTEND=noninteractive

    # Очистка.
    rm -f /etc/apt/sources.list.d/cuda* 2>/dev/null || true
    rm -f /etc/apt/sources.list.d/nvidia* 2>/dev/null || true

    apt update
    apt install -y curl wget linux-headers-$(uname -r) build-essential dkms

    # Репозиторий CUDA (Debian 12).
    wget -q https://developer.download.nvidia.com/compute/cuda/repos/debian12/x86_64/cuda-keyring_1.1-1_all.deb
    dpkg -i cuda-keyring_1.1-1_all.deb
    rm -f cuda-keyring_1.1-1_all.deb
    apt update

    # Репозиторий Container Toolkit.
    curl -fsSL https://nvidia.github.io/libnvidia-container/gpgkey | gpg --dearmor -o /usr/share/keyrings/nvidia-container-toolkit-keyring.gpg
    curl -s -L https://nvidia.github.io/libnvidia-container/stable/deb/nvidia-container-toolkit.list | \
      sed 's#deb https://#deb [signed-by=/usr/share/keyrings/nvidia-container-toolkit-keyring.gpg] https://#g' > /etc/apt/sources.list.d/nvidia-container-toolkit.list
    apt update

    # Установка драйвера 580.
    apt install -y nvidia-driver-580 nvidia-settings nvidia-compute-utils-580 nvidia-container-toolkit

    # Отключение nouveau.
    sed -i 's/GRUB_CMDLINE_LINUX=""/GRUB_CMDLINE_LINUX="nouveau.modeset=0"/' /etc/default/grub
    update-grub

  nodeGroups:
  - gpu
  weight: 5

После применения конфигурации перезагрузите узлы, затем проверьте драйвер на узле командой nvidia-smi.

Секция Processes в выводе nvidia-smi должна быть пустой до запуска нагрузок. На узлах с графическим окружением GPU часто занят процессами Xorg, gnome-shell, gdm, sddm или lightdm. Такие процессы занимают память GPU и блокируют реконфигурацию MIG и VFIO-проброс.

Как проверить GPU-узел?

Модуль добавляет лейблы на каждый подготовленный узел.

Для получения лейблов используйте команду:

d8 k get node <имя-узла> -o json | jq -r '.metadata.labels | with_entries(select(.key | test("gpu")))'

На работающем GPU-узле должны присутствовать все следующие лейблы:

Лейбл Значение
node.deckhouse.io/gpu-setup-complete Подготовка узла завершена. Пока лейбла нет, ни один компонент GPU на узел не планируется.
gpu.deckhouse.io/vendor Обнаружен поддерживаемый GPU; значение — его вендор. По этому лейблу планируется соответствующий адаптер.
node.deckhouse.io/gpu-vfio-ready true, если узел готов к VFIO-пробросу. Значение false допустимо, если проброс не нужен.

Для получения стека, работающего на узле, используйте команду:

d8 k -n d8-nvidia-gpu get pods -o wide

Пример вывода:

NAME                             READY   STATUS    RESTARTS   AGE
gpu-controller-7d9f8b6c4-xk2lp   2/2     Running   0          5m
gpu-dcgm-2gflv                   1/1     Running   0          5m
gpu-dcgm-exporter-vnjrq          1/1     Running   0          5m
gpu-node-agent-54gk8             1/1     Running   0          5m
nvidia-adapter-8qgqx             3/3     Running   0          5m

Для получения информации об обнаруженном оборудовании используйте команду:

d8 k get physicalgpus -o wide

Все ожидаемые карты должны быть в Ready со значением HEALTHY=True. Колонка HEALTHY — расширенная, она появляется только с -o wide; вывод по умолчанию заканчивается на AGE и её не содержит.

Для получения информации об устройствах, предложенных планировщику используйте команду:

d8 k get resourceslices

Если объекты PhysicalGPU есть, а ResourceSlice нет, значит карты не в фазе Ready: устройства публикуются только в ней. См. Диагностика.

Как опубликовать GPU для пользователей?

Чтобы опубликовать GPU для пользователей, опишите GPUClass, и пользователи смогут запрашивать GPU по имени:

apiVersion: gpu.deckhouse.io/v1alpha1
kind: GPUClass
metadata:
  name: a100
spec:
  selector:
    matchLabels:
      gpu.deckhouse.io/device: a100-sxm4-40gb

Для просмотра всех DeviceClass, которые были автоматически созданы контроллером GPU на основе GPUClass используйте команду:

d8 k get gpuclass a100 -o jsonpath='{.status.deviceClassNames}' | jq

Передайте эти имена пользователям. Фильтры партиций и разделения описаны в разделе Классы GPU.

Как мигрировать с режима Device Plugin на DRA?

Для миграции измените значение параметра dra.enabled с false на true. Миграция выполнится автоматически:

  1. Хук check_migration обнаруживает существующий неймспейс d8-nvidia-gpu и добавляет на него лейбл gpu.deckhouse.io/managed-by=gpu.
  2. На следующей реконсиляции migrationReady становится true, и шаблоны DRA рендерятся.
  3. Helm удаляет стек Device Plugin — device plugin, GFD, MIG-менеджер, DCGM — и разворачивает DRA-стек в том же неймспейсе.

После миграции:

  • spec.gpu в NodeGroup перестаёт действовать: он больше ничем не управляет. Способ разделения теперь выбирается на уровне нагрузки. Этот параметр можно удалить.
  • Лейблы узла из режима Device Plugin, такие, как node.deckhouse.io/device-gpu.config и лейблы nvidia.com/*, опубликованные NFD, могут какое-то время оставаться после удаления NFD. В режиме DRA они игнорируются.
  • Нагрузки, запрашивающие nvidia.com/gpu, перестанут планироваться, потому что этот ресурс больше не публикуется. Переведите их на claim’ы до переключения либо запланируйте простой.

Почему DRA-стек вообще не развернулся?

Если после установки значения параметра dra.enabled: true в неймспейсе d8-nvidia-gpu нет ни gpu-controller, ни gpu-node-agent, ни nvidia-adapter, возможно, не выполнено одно из следующих условий для рендеринга шаблонов DRA (они должны выполняться одновременно): dra.enabled, migrationReady и draFeatureGatesReady.

Проверьте статус модуля:

d8 k get module gpu -o jsonpath='{.status.conditions}' | jq

Частая причина — отсутствующие feature gates для DRA. Это отражается как FeatureGatesReady=False с причиной FeatureGatesNotEnabled и алертом D8GpuDraFirstEnablementBlocked. Gates включает платформа, а не модуль, поэтому решение — обновить DKP. Подробнее — в разделе «Готовность feature gates для DRA».

Что случится при отключении Feature gate при работающем DRA?

Появится D8GpuDraFeatureGateDisabledOnComponent. Он появляется, когда необходимый gate отключён на apiserver, планировщике или отдельном kubelet. Интересен именно последний случай: проверка готовности читает только apiserver, поэтому потеря gate kubelet’ом отлавливается этим алертом, а не проверкой готовности.

Что делать, если на GPU-узле нет DRA-подов?

Проверьте лейблы узла:

d8 k get node <имя-узла> -o json | jq -r '.metadata.labels | with_entries(select(.key | test("gpu")))'
Отсутствует или неверно Значение
нет node.deckhouse.io/gpu-setup-complete Подготовка узла не завершилась. Скорее всего, не прошла проверка драйвера — выполните nvidia-smi на узле.
нет gpu.deckhouse.io/vendor GPU не обнаружен. Проверьте, видна ли карта на шине PCI и работает ли gpu-node-agent.
gpu.deckhouse.io/enabled=false Узел явно исключён из управления GPU.
gpu.deckhouse.io/maintenance=true Узел выведен на обслуживание и исключён намеренно.

Что делать, если объекты PhysicalGPU есть, а ResourceSlice нет?

Устройства публикуются только, пока карта находится в фазе Ready. Проверьте фазу и условия.

Для просмотра всех физических GPU в кластере используйте команду:

d8 k get physicalgpus

Для просмотра условий для конкретного GPU используйте команду:

d8 k get physicalgpu <имя> -o jsonpath='{.status.conditions}' | jq

AdapterReady=False с причиной DriverNotReady и сообщением NVML capabilities not available for this device означает, что адаптер не смог обратиться к карте через NVML. Проверьте драйвер на узле и логи контейнера nvidia-adapter:

d8 k -n d8-nvidia-gpu logs daemonset/nvidia-adapter -c nvidia-adapter

Карта в фазе Passthrough привязана к vfio-pci и намеренно не предлагается как CUDA-устройство.

Что делать, если под остаётся в Pending, claim не выделяется?

Ни одно опубликованное устройство не удовлетворяет claim. Получите список всех устройств из ресурсов ResourceSlice с их именами и атрибутами:

d8 k get resourceslices -o json | jq '.items[].spec.devices[]? | {name, attributes}'

Частые причины проблемы:

  • CEL-селектор ничему не соответствует. Типичная ошибка — запрос sharePercent при селекторе, исключающем sharing-варианты через !has(...sharingStrategy): эти два условия противоречат друг другу. Подробнее — в разделе «Использование».
  • Необёрнутый атрибут в CEL. Обращение к необязательному атрибуту без has(...) приводит к ошибке всего выражения, а не к значению false.
  • Запрошенный MIG-профиль конфликтует с раскладкой, которую сейчас держит другая нагрузка. Партиции существуют только пока они заняты, поэтому под ждёт завершения держателя. Это планировщик отказывается выделить слайсы карты дважды, а не ошибка.

Что делать, если GPU-нагрузки не работают, хотя драйвер и toolkit установлены?

Факт установки пакетов ничего не гарантирует. В DRA-режиме (dra.enabled: true) модуль проверяет не наличие компонентов хост-стека GPU, а его работоспособность: загружен ли модуль ядра, отвечает ли nvidia-smi, совпадают ли версии модуля ядра и драйвера, установлен ли контейнерный toolkit, видит ли GPU nvidia-container-cli и согласованы ли версии toolkit.

Проверка выполняется ранним шагом bashible на узле и никогда не блокирует конвергенцию узла: узел продолжает получать обновления, а сам шаг не может ни упасть, ни зависнуть (каждый вызов вендорного бинаря ограничен по времени, поэтому зависший модуль ядра не останавливает прогон). Меняется другое:

  • снимается признак готовности node.deckhouse.io/gpu-setup-complete, поэтому компоненты GPU-стека не едут на неисправный узел. Одного снятия лейбла недостаточно — более поздний шаг подготовки узла в том же прогоне ставит этот лейбл по своему собственному состоянию, — поэтому вердикт дополнительно поднимает локальный bashbooster-флаг gpu-host-stack-unhealthy, и именно он заставляет более поздний шаг (gpu-setup.sh в DRA-режиме, gpu-sysctl.sh в режиме Device Plugin) лейбл не возвращать. Состояние не залипает: и лейбл, и флаг пересчитываются с нуля каждым прогоном подготовки узла, поэтому после починки ничего подчищать руками не нужно;

  • на узел ставятся два лейбла с результатом:

    d8 k get nodes -L node.deckhouse.io/gpu-host-stack-error,node.deckhouse.io/gpu-host-stack-vendor
  • модуль экспортирует метрику gpu_node_host_stack_unhealthy{node,vendor,reason} и через 15 минут зажигает alert D8GpuNodeHostStackUnhealthy;

  • метрика gpu_node_host_stack_unhealthy_nodes показывает число неисправных узлов и публикуется всегда, в том числе со значением 0: по ней видно, что проверка выполняется, даже когда неисправных узлов нет.

Драйвер и контейнерный toolkit — внешние предпосылки: модуль их диагностирует, но не устанавливает, не обновляет и не починяет.

Когда выполняется проверка и как запустить её заново?

Это проверка подготовки узла, а не мониторинговая проба. bashible пропускает все шаги, если контрольная сумма конфигурации не изменилась, нет аннотации на перезагрузку и uptime узла вырос, — поэтому вердикт пересчитывается при bootstrap узла, при смене конфигурации (настройки модуля, NodeGroup, обновление Deckhouse) и после перезагрузки, а не по таймеру.

Из этого следуют две вещи:

  • поломка, возникшая после успешной конвергенции (например, рассинхрон версий драйвера и toolkit из-за обновления пакетов без перезагрузки), не будет замечена до следующего прогона подготовки узла;
  • починка сама по себе вердикт не снимает — проверка должна выполниться заново.

Чтобы заставить перепроверку на узле, перезагрузите узел либо удалите файл контрольной суммы и дождитесь следующего тика bashible:

rm -f /var/lib/bashible/configuration_checksum

Это ограничение касается только программного хост-стека. Состояние самих GPU-устройств отслеживается непрерывно и независимо от подготовки узла: оно публикуется в PhysicalGPU (status.driverBinding.mode — наблюдаемый драйвер, status.driverBinding.managementState — кто управляет устройством), а карта, оставшаяся на бесхозной passthrough-привязке — без владеющего claim и без объявленного отказа, — классифицируется как Orphaned, продержавшись так двухминутный период ожидания. Алерт D8GPUPhysicalGPUOrphaned срабатывает ещё позже: у него for: 15m, то есть он поднимается примерно через семнадцать минут после того, как привязка стала бесхозной, а не через две.

В каком порядке проверять узел?

Выполняйте на самом узле и именно в этом порядке — проверки идут от нижнего уровня стека к верхнему, и первая непройденная указывает на корневую причину:

cat /proc/driver/nvidia/version                  # загружен ли модуль ядра?
nvidia-smi -L                                    # отвечает ли драйвер?
nvidia-smi --query-gpu=driver_version --format=csv,noheader
nvidia-container-cli info                        # работает ли контейнерный путь?
nvidia-ctk --version
nvidia-container-cli --version                   # совпадают ли версии toolkit?

Детализация, которой нет в reason-коде, — в логе шага на узле. Где именно лежит этот лог, зависит от версии Deckhouse, а модуль поддерживает обе:

# Deckhouse 1.77 и новее — отдельный файл на каждый шаг bashible:
cat /var/log/d8/bashible/step.006_gpu-check.sh.log

На Deckhouse 1.75 и 1.76 отдельных логов шагов нет. Шаг писал в /var/lib/bashible/step.log, который перезаписывает каждый следующий шаг, а gpu-check идёт с weight 6 — поэтому к концу прогона его вывод уже потерян. В этом случае прогоните проверки выше руками.

Что означают reason-коды NVIDIA?

gpu-host-stack-error Что означает Что делать
host-stack-not-installed Узел входит в GPU-NodeGroup, но вендорного хост-стека на нём нет вовсе: ни модуля ядра, ни nvidia-smi, ни контейнерного toolkit. Установите на узле драйвер NVIDIA и NVIDIA Container Toolkit либо выведите узел из GPU-NodeGroup.
driver-not-loaded Модуль ядра NVIDIA не загружен или не сообщает версию. Проверьте dmesg | grep -i nvidia, пересоберите DKMS под текущее ядро, перезагрузите узел.
nvidia-smi-failed Модуль ядра загружен, но nvidia-smi завершается с ошибкой или не показывает ни одного GPU. Переустановите драйвер той же версии, что и загруженный модуль ядра.
driver-version-mismatch Загруженный модуль ядра и установленный драйвер разных версий — типичный след DKMS после смены ядра. Пересоберите модуль ядра под установленный драйвер и перезагрузите узел.
driver-too-old Версия драйвера ниже требуемой 450.80.02. Обновите драйвер.
compute-cap-unsupported Compute capability GPU не выше 6.0. Карта не поддерживается модулем, используйте более новый GPU.
toolkit-not-installed Отсутствует nvidia-container-runtime или nvidia-container-cli. Установите NVIDIA Container Toolkit. На узле GPU-NodeGroup containerd по умолчанию использует runtime nvidia, поэтому без toolkit не запустится ни один контейнер.
container-cli-failed nvidia-container-cli info завершается с ошибкой: libnvidia-container не видит GPU. Проверьте SELinux, устройства /dev/nvidia*, CDI-спеку и пакеты nvidia-container*.
toolkit-version-mismatch nvidia-ctk и nvidia-container-cli различаются по major.minor. Выровняйте версии пакетов nvidia-container-toolkit, nvidia-container и nvidia-container-tools.

Reason-коды вендор-специфичны: неисправность определяется парой (gpu-host-stack-vendor, gpu-host-stack-error). GPU вендоров, для которых проверки пока не реализованы, не проверяются и лейблов не порождают.

Про узел вне GPU-области проверка ничего не утверждает: если узел не входит ни в одну GPU-NodeGroup и карта в нём оказалась случайно (control-plane с запасным GPU, хост виртуализации, держащий GPU под passthrough), если GPU отданы в passthrough драйверу vfio-pci или если GPU поддерживаемого вендора на шине нет вовсе — reason-кода и alert’а не будет, а вердикт, оставшийся от предыдущего прогона, снимается молча. Поэтому узел не продолжает алертить после того, как из него вынули карту. Узел, который в GPU-NodeGroup входит, но вендорного хост-стека не имеет, — случай другой, он сообщается кодом host-stack-not-installed.

После починки следующий прогон подготовки узла снимает оба лейбла gpu-host-stack-*, снимает флаг gpu-host-stack-unhealthy, возвращает признак готовности, и alert гаснет — как запустить этот прогон, если починка не потребовала ни перезагрузки, ни смены конфигурации, описано выше.

Что делать в RED OS 7.3 и других дистрибутивах с расходящимися версиями пакетов?

В RED OS MUROM 7.3 репозиторий updates отдаёт сразу несколько версий контейнерных пакетов NVIDIA (например, nvidia-container-toolkit 1.14.4 рядом с nvidia-container и nvidia-container-tools 1.18.0~rc). Штатная установка спокойно их смешивает: на хосте nvidia-smi работает, карта видна — а в контейнерах GPU не работает. Это и есть случай toolkit-version-mismatch.

Что проверить:

rpm -qa 'nvidia-container*'
nvidia-ctk --version
nvidia-container-cli --version

Все пакеты nvidia-container* должны быть одной версии; фиксируйте версию явно при установке, а не полагайтесь на выбор репозитория по умолчанию. Драйвер устанавливайте по инструкции NVIDIA по установке драйвера, контейнерный toolkit — по инструкции NVIDIA по установке Container Toolkit.

Что делать, если контейнер запустился, но GPU в нём нет?

Самая частая ошибка в манифестах DRA: spec.resourceClaims объявлен, но у контейнера нет соответствующей записи resources.claims. GPU выделяется и просто не передаётся в контейнер — без ошибки, без события и без предупреждения.

spec:
  resourceClaims:
    - name: gpu
      resourceClaimTemplateName: my-claim
  containers:
    - name: app
      resources:
        claims:
          - name: gpu     # Проверьте, что указана эта часть.

Что делать, если nvidia-smi внутри пода ничего не находит?

Если у пода нет claim, это корректное поведение. DRA-нагрузки работают в стандартном container runtime и получают устройства только через claim, поэтому под без claim не видит ни одного /dev/nvidia*. Установка NVIDIA_VISIBLE_DEVICES=all ничего не меняет — эта переменная в модуле не действует.

Если claim у пода есть, проверьте запись resources.claims, затем посмотрите, какое устройство было выделено:

d8 k -n <неймспейс> get resourceclaim -o json \
  | jq '.items[].status.allocation.devices.results[] | {device, pool}'

Что делать, если запрос через extended-ресурс игнорируется?

Под запрашивает gpu.deckhouse.io/<класс> и остаётся в Pending, а ресурс по-прежнему виден в спецификации под.

Мутирующий вебхук, превращающий extended-ресурсы в claim’ы, намеренно не применяется в kube-system и во всех неймспейсах d8-*. Там запрос остаётся без изменений, и поскольку ни один узел его не публикует, под никогда не запланируется. Запускайте 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

Доступные имена смотрите командой d8 k get gpuclass <имя> -o jsonpath='{.status.deviceClassNames}'.

Что делать, если создание или изменение GPUClass отклоняется?

У вебхука GPUClass установлено failurePolicy: Fail, поэтому пока gpu-controller недоступен, запись отклоняется. Проверьте, что он работает:

d8 k -n d8-nvidia-gpu get deploy gpu-controller

При наличии проблемы появляется ошибка валидации:

partitionFilter value "1g.5gb" must be a DNS label: must not contain dots

Имена профилей в partitionFilter — DNS-метки, пишите 1g5gb (без точки). Форма с точками используется только в CEL-селекторах и в возможностях PhysicalGPU.

Что делать, если в GPUClass нет ожидаемого DeviceClass?

Если ожидаемого имени нет в .status.deviceClassNames, скорее всего оборудование не поддерживает эту комбинацию. Фильтры ограничивают то, что публикуется, но не создают возможности. Проверьте, что карта действительно умеет:

d8 k get physicalgpu <имя> -o jsonpath='{.status.capabilities}' | jq

Также посмотрите .status.matchingPhysicalGPUCount — значение 0 означает, что селектор ничему не соответствует.

Что делать, если выделение GPU зависло?

Каждое опубликованное устройство объявляет bindingConditions: ["Ready"] и bindingFailureConditions: ["BindingFailed"], поэтому неудачная подготовка отражается на claim, а не оставляет под зависшим без оповещений. Проверьте ResourceClaim:

d8 k -n <неймспейс> get resourceclaim <имя> -o yaml

Ошибки подготовки для VFIO-claim’ов перечислены в разделе «Использование».

Что делать, если вместо разделения MPS получено time-slicing?

Это ожидаемое поведение и об этом сообщается. MPS и time-slicing объявляются на уровне узла, по факту наличия исполняемых файлов вендора — модель карты не проверяется никогда. Поэтому claim с запросом MPS принимается даже на железе, которое его не потянет.

Несоответствие вскрывается при подготовке. Модуль переключается на time-slicing и записывает событие. Для просмотра событий используйте команду:

d8 k -n d8-nvidia-gpu get events --field-selector reason=MPSFallbackToTimeSlicing

Нагрузка всё равно получит разделение, но не то, которое запрашивала: без MPS нет ограничения потоков на клиента, поэтому sharePercent больше не ограничивает вычисления так, как это было бы под MPS. Если и сам fallback не удался, подготовка останавливается с MPSPrepareFailed.

MPS требует compute capability 7.0 и выше. Подробнее — в разделе «Возможности модуля в зависимости от вендора GPU».

Почему поды DCGM исчезают во время изменения MIG?

Это ожидаемое поведение. Перед реконфигурацией MIG nvidia-adapter выставляет gpu.deploy.dcgm и gpu.deploy.dcgm-exporter в paused-for-mig-change, чтобы DCGM перестал держать GPU. DaemonSet’ы возвращаются после завершения операции.

Что делать, если реконфигурация MIG или VFIO-проброс не выполняются?

Карта должна быть свободна от запущенных процессов. Проверьте на узле:

nvidia-smi

Секция Processes должна быть пустой. На узлах с графическим окружением GPU часто занят процессами Xorg, gnome-shell, gdm, sddm или lightdm; они занимают память GPU и блокируют реконфигурацию.

Отдельно для VFIO узлу требуется IOMMU, включённый в BIOS и в командной строке ядра, что отражается значением node.deckhouse.io/gpu-vfio-ready=true.

Что делать, если удаление/отключение модуля зависает?

При удалении хук pre-delete удаляет все объекты PhysicalGPU и ждёт до 600 секунд, чтобы gpu-controller успел снять свои финалайзеры до удаления CRD. Если хук истёк по таймауту, поищите неудаленные объекты PhysicalGPU:

d8 k get physicalgpus -A

Что делать, если GPU отображается как Orphaned?

Если вывод команды d8 k get physicalgpus показывает Management=Orphaned, значит passthrough-привязка драйвера существует без владеющего ResourceClaim и без явного исключения — сегодня это привязка к vfio-pci, единственному драйверу, дающему режим Passthrough, оставшаяся от исчезнувшей нагрузки. Passthrough здесь обязателен, а не типичен: карта с привязкой в режиме Compute и без claim’а — обычная свободная карта, она читается как Unclaimed, пока свободна, и Orphaned не становится никогда. Классификация присваивается только после того, как бесхозная passthrough-привязка продержалась две минуты: это не кратковременное состояние.

Посмотрите причину в условии Managed:

d8 k get physicalgpu <имя> -o jsonpath='{.status.conditions[?(@.type=="Managed")]}' | jq

Только значение Orphaned делает устройство пригодным для reclaim. С каким из восьми случаев вы имеете дело, говорит причина, и каждая из них однозначно задаёт, что стоит в колонке Management:

  • BackingAllocation → Managed. На карту ссылается выделенный ResourceClaim, она занята.
  • NoBackingAllocation → Unclaimed. Вообще не проблема: карта опознана, привязка живая и вычислительная, никто её не держит. Делать ничего не нужно.
  • ExcludedReleasePending → Managed. Отказ объявлен, но привязка ещё жива, поэтому карта пока не подлежит reclaim.
  • DeliberatelyExcluded → Excluded. Отказ вступил в силу, привязка освобождена.
  • OrphanPending → Unknown. Период ожидания ещё идёт.
  • Orphaned → Orphaned. Бесхозная passthrough-привязка продержалась весь период ожидания — случай, которому посвящён этот раздел.
  • NoLiveBinding → Unknown. Управлять нечем: драйвер не привязан, привязанный не удалось классифицировать либо привязка ещё не опубликована.
  • Indeterminate → Unknown. Прочитать не удалось ничего — нет имени Node или нет PCI-адреса, и достаточно отсутствия любого из двух.

Три причины дают в колонке Unknown; отсчёт из них идёт только у OrphanPending.

Чтобы вернуть устройство, уберите бесхозную passthrough-привязку и дайте обнаружению переклассифицировать его либо запросите сброс, как описано ниже.

Что делать, если запрос reclaim отклонён?

Аннотация gpu.deckhouse.io/reclaim просит контроллер сбросить и заново обнаружить устройство. Запрос, который контроллер выполнить не может, отклоняется, а не игнорируется молча, и отказ записывается в условие Reclaim:

d8 k get physicalgpu <имя> -o jsonpath='{.status.conditions[?(@.type=="Reclaim")]}' | jq

Условие — не постоянная запись. Контроллер снимает его, как только condition Managed переходит после отказа: ответ описывал состояние, которое карта с тех пор покинула, и уходит вместе с ним. Поэтому пустой вывод означает одно из двух — отказов на этой карте не было вовсе либо карта с тех пор сменила состояние. Дольше условия живёт событие ReclaimRefused, до очистки apiserver’ом по --event-ttl (около часа), а после него остаётся лог контроллера, куда отказ пишется всегда:

d8 k get events --field-selector reason=ReclaimRefused

Почти всегда отказ называет колонку Management, а не то, что за ней стоит: reclaim гейтится на managementState раньше всего остального, и допуск открывает только Orphaned. Поэтому сначала читайте колонку — она и решает, что делать:

d8 k get physicalgpus
  • Unclaimed. Отказ верен, и повтор запроса не сработает никогда. Unclaimed — это живая Compute-привязка, которую никто не держит: здоровая свободная карта, выделяемая как есть, и сбрасывать на ней нечего. Освобождение claim’а на такой карте снова даёт Unclaimed, а не Orphaned, поэтому повтор после освобождения зацикливается.
  • Managed. Либо карту держит выделенный ResourceClaim, либо её исключили, пока привязка ещё жива (причина ExcludedReleasePending условия Managed). Модуль не отбирает карту у живой нагрузки. Освободите claim и проставляйте аннотацию заново только в том случае, если после этого карта действительно прочиталась как Orphaned: так бывает с бесхозной passthrough-привязкой после периода ожидания, тогда как карта с Compute-привязкой окажется в Unclaimed, и reclaim ей не нужен вовсе.
  • Excluded. Карту сознательно исключили аннотацией gpu.deckhouse.io/unmanaged, и привязка уже освобождена. Сбрасывать нечего; чтобы вернуть карту под управление модуля, снимите аннотацию.
  • Unknown с причиной OrphanPending. Идёт двухминутный период ожидания. Запрос не отклоняется, а удерживается, и reclaim начнётся сам, как только карта станет Orphaned. Дождитесь, а не проставляйте аннотацию заново.
  • Unknown с причиной NoLiveBinding или Indeterminate. Сбрасывать нечего — живой привязки нет, — либо у объекта нет имени Node или нет PCI-адреса, и достаточно отсутствия любого из двух, так что сопоставлять claim с картой не с чем вовсе. Повторная аннотация ничего не меняет. Сначала восстановите идентичность: проверьте, что gpu-node-agent работает на узле и публикует status.nodeInfo.nodeName и status.pciInfo.address.

Отказ можно получить и на карте, которая действительно читается как Orphaned. За гейтом по колонке контроллер ещё раз проверяет занятость и остаётся fail-closed: claim, который всё-таки нашёлся, или занятость, которую установить не удалось, отклоняют запрос уже на этом шаге. Такие отказы редки и означают, что колонка и claim’ы разошлись, — перечитайте и то и другое.

Один терминальный исход в условии Reclaim вообще не является отказом на ваш запрос: если аннотацию сняли, пока сброс шёл, условие фиксирует reclaim как прерванный. Чтобы начать заново, проставьте аннотацию снова.

Почему запрос проброса завершается ошибкой вместо перехода в Passthrough?

VFIO-проброс требует bare-metal-узла. На виртуализированном узле адаптер вендора выставляет AdapterReady=False с причиной VFIORequiresBareMetal, что выводит фазу Failed, а не Passthrough.

Как вывести GPU из-под управления модулем?

Проставьте аннотацию gpu.deckhouse.io/unmanaged. Пока привязка существует, устройство остаётся в Managed с причиной ExcludedReleasePending; после освобождения оно переходит в Excluded.

Как мониторить GPU?

DCGM Exporter разворачивается автоматически, его метрики собирает Prometheus, а дашборды доступны в Grafana — обзор кластера, представления по узлам и неймспейсам, инвентарь GPU и выделения по нагрузкам. Модуль также экспортирует собственные метрики (gpu_inventory_*, gpu_bootstrap_*, gpu_resourceclaim_*). Подробнее — в разделе «Мониторинг».

Поддерживаются ли GPU AMD или Intel?

Для выделения — нет. Адаптеры поставляются для NVIDIA и MetaX, а именно адаптер превращает обнаруженную карту в то, что может запросить рабочая нагрузка.

Всё, что находится выше адаптера, для других вендоров уже работает. Обнаружение распознаёт устройства AMD (1002) и Intel (8086), поэтому их карты появляются как объекты PhysicalGPU с заполненными PCI-данными, а узел получает лейбл gpu.deckhouse.io/vendor=amd или =intel. Без адаптера такие объекты остаются в фазе Pending: их возможности никто не читает, ResourceSlice не публикуется, и планировщик их не видит.

d8 k get physicalgpus
NAME                   NODE       MODEL   PHASE     MANAGEMENT   STATUS                                AGE
worker-2-0-1002-7408   worker-2           Pending   Unknown      adapter not scheduled to a workload   3m

В колонке MANAGEMENT стоит Unknown с причиной NoLiveBinding. Драйвер amdgpu модуль не классифицирует, поэтому живой вычислительной привязки у карты нет и управлять модулю нечем. Сюда карту приводит неклассифицируемый драйвер, а не отсутствие адаптера: карта NVIDIA, для которой адаптер не запланирован, всё равно привязана к nvidia, всё равно даёт mode: Compute и потому читается как Unclaimed — та же фаза Pending, другое значение MANAGEMENT. Значение Unclaimed отведено карте, привязкой которой модуль мог бы управлять и которую никто не взял.

Колонка MODEL читает нормализованный slug модели из лейбла gpu.deckhouse.io/device и остаётся пустой, пока модель не определена; PCI vendor ID и device ID при этом видны в имени объекта, а PCI-адрес — в расширенном выводе. Колонка STATUS объясняет, почему карта не в Ready, и заполняется компонентом gpu-controller: здесь она повторяет сообщение условия AdapterReady=False/NotScheduled, которое и перевело карту в Pending. Вендор, PCI-адрес и здоровье устройства тоже показываются в расширенном выводе (d8 k get physicalgpus -o wide).

Добавление вендора не требует изменений ни в CRD, ни в компонентах ядра: нужен контейнер, реализующий контракт адаптера, PCI vendor ID в ConfigMap gpu-supported-vendors и DaemonSet адаптера.

Режим Device Plugin

Всё, что ниже, относится только к режиму Device Plugin (dra.enabled: false).

Как проверить компоненты режима Device Plugin?

Поды NVIDIA в d8-nvidia-gpu:

d8 k -n d8-nvidia-gpu get pod

Ожидаемый корректный вывод (пример):

NAME                                  READY   STATUS    RESTARTS   AGE
gpu-feature-discovery-80ceb7d-r842q   2/2     Running   0          2m53s
nvidia-dcgm-exporter-w9v9h            1/1     Running   0          2m53s
nvidia-dcgm-njqqb                     1/1     Running   0          2m53s
nvidia-device-plugin-80ceb7d-8xt8g    2/2     Running   0          2m53s

Поды NFD в d8-nvidia-gpu:

d8 k -n d8-nvidia-gpu get pods | egrep '^(NAME|node-feature-discovery)'

Ожидаемый корректный вывод (пример):

NAME                                             READY   STATUS      RESTARTS       AGE
node-feature-discovery-gc-6d845765df-45vpj       1/1     Running     0              3m6s
node-feature-discovery-master-74696fd9d5-wkjk4   1/1     Running     0              3m6s
node-feature-discovery-worker-5f4kv              1/1     Running     0              3m8s

Что делать при наличии ошибки Incompatible strategy detected auto в логах device plugin или GFD?

Ошибки вида Incompatible strategy detected auto, failed to create resource manager: unsupported strategy auto или invalid device discovery strategy означают, что компонент не может определить платформу NVML внутри контейнера — обычно libnvidia-ml.so.* недоступна, потому что не используется runtime из NVIDIA Container Toolkit.

Проверьте (и при необходимости исправьте) следующее:

  1. nvidia-smi работает на узле.
  2. NVIDIA Container Toolkit установлен (существует /usr/bin/nvidia-container-runtime).
  3. containerd настроен на использование runtime nvidia на GPU-узлах. Модуль делает это после установки драйвера и toolkit и перезапуска containerd или перезагрузки узла.

После исправления пересоздайте поды nvidia-device-plugin-* и gpu-feature-discovery-* в неймспейсе d8-nvidia-gpu.

Как посмотреть доступные MIG-профили?

Предопределённые профили лежат в ConfigMap mig-parted-config:

d8 k -n d8-nvidia-gpu get cm mig-parted-config -o json | jq -r '.data["config.yaml"]'

В секции mig-configs: перечислены модели GPU по PCI ID и профили, которые поддерживает каждая карта, например all-1g.5gb, all-2g.10gb и all-balanced. Выбранное имя укажите в spec.gpu.mig.partedConfig.

Как задать свой MIG-профиль для каждого GPU на узле?

Используйте partedConfig: custom и опишите партиционирование по индексам GPU:

gpu:
  sharing: MIG
  mig:
    partedConfig: custom
    customConfigs:
      - index: 0
        slices:
          - profile: "1g.10gb"
            count: 7
      - index: 1
        slices:
          - profile: "2g.20gb"
            count: 3

Далее модуль генерирует уникальное имя MIG-конфигурации для NodeGroup и выставляет его в лейбле nvidia.com/mig.config, для перечисленных GPU рендерит mig-enabled: true с заданными слайсами, а для каждого неупомянутого индекса — mig-enabled: false, чтобы остальные карты остались в полном режиме.

Что делать, если MIG-профиль не активируется?

Если MIG-профиль не активируется, выполните следующие действия:

  1. Проверьте модель GPU. MIG поддерживается на H100, A100 и A30 и не поддерживается на V100 и T4. Подробнее — в таблицах профилей NVIDIA.

  2. Убедитесь, что GPU не используется. Подробнее — в разделе «Что делать, если реконфигурация MIG или VFIO-проброс не выполняются».

  3. Проверьте конфигурацию NodeGroup.

  4. Дождитесь, пока nvidia-mig-manager вытеснит нагрузку с узла и переконфигурирует GPU. Это может занять несколько минут, в течение которых на узле висит taint mig-reconfigure.

  5. Следите за прогрессом по лейблу nvidia.com/mig.config.state: pending, rebooting, success или failed.

  6. Если ресурсы nvidia.com/mig-* так и не появились, используйте команду:

    d8 k -n d8-nvidia-gpu logs daemonset/nvidia-mig-manager
    nvidia-smi -L