Device Plugin честно отработал свою эпоху: он научил Kubernetes видеть GPU и выдавать их подам. Но с бурным ростом AI-нагрузок GPU превратились в общий ресурс для десятков команд. Теперь недостаточно просто запустить задачу — нужны жёсткая изоляция, топология и высокая утилизация. Старая модель достигла предела. Чтобы обойти её ограничения, команды вынуждены создавать поверх Kubernetes «второй Kubernetes». Это дорого, нестабильно и плохо масштабируется.
Именно поэтому индустрия обращает внимание на DRA (Dynamic Resource Allocation) — встроенный в Kubernetes механизм, доступный по умолчанию с версии 1.35. DRA переносит сложную логику выделения и управления устройствами в ядро системы, заменяя конструктор из сторонних инструментов единым, предсказуемым API.
Разберём, почему старые подходы перестали справляться с современными AI-нагрузками, на какие проблемы уже наступили команды в продакшене и зачем изучать DRA, даже если пока не планируете переходить.
Как всё начинается: «дай мне один GPU, и поехали»
Типичный сценарий интеграции GPU в Kubernetes:
- Устанавливается NVIDIA Device Plugin — вручную или через GPU Operator.
- В манифесте пода указывается
resources.limits: nvidia.com/gpu: 1. - Pod размещается на GPU-узле, и контейнер получает доступ к устройству.
На этом этапе кажется, что интеграция завершена. И для многих сценариев этого действительно достаточно.
Но в реальной эксплуатации всё усложняется: появляется больше команд, разнородных нагрузок, требований к изоляции, стабильности и предсказуемости. Возникает вопрос: «Почему именно эта GPU, а не любая другая?».
Умножим на продакшен: коммунальный H100-кластер
Рассмотрим типичный AI-кластер, характерный для многих реальных инфраструктур:
- 8 GPU-узлов;
- по 8 NVIDIA H100 на каждом;
- всего 64 GPU;
- общие ресурсы для нескольких команд и типов нагрузок.
В таком кластере обычно сосуществуют три типа рабочих нагрузок.
1. Продовый инференс
Сервисы, обрабатывающие реальный трафик: vLLM, Triton, LLM-API, embedding/rerank, мультимодальные модели. Для крупных моделей типичный паттерн — одна реплика на один узел целиком: 8 GPU, tensor-parallel-size=8, предсказуемая латентность. vLLM, например, рекомендует использовать количество GPU в узле как размер tensor parallel.
Здесь инференс — не эксперимент, а сервис, который не терпит сюрпризов.
2. ML-обучение и эксперименты
Второй сегмент — обучение моделей. Здесь типичный запрос: «нужно 32 GPU на четырёх узлах, и все должны стартовать одновременно».
Без gang scheduling и специализированных batch-шедулеров такие задачи легко приводят к фрагментации: часть подов запускается, часть зависает в ожидании. Поэтому в экосистеме часто появляются инструменты вроде Volcano — нативный batch-шедулер для Kubernetes, ориентированный на AI/ML и HPC.
3. «Мелочь», которая съедает половину кластера
Третий слой — задачи, которые в отдельности кажутся незначительными, но в сумме создают серьёзную нагрузку на инфраструктуру:
- короткие CUDA-тесты и CI-задачи;
- профилировщики и утилиты;
- небольшие инференс-сервисы (embeddings, STT/TTS), которым не нужен целый H100;
- спорадические нагрузки: «нужно на 5 минут».
Если выделять под них целые GPU, утилизация падает. А это прямое влияние на бюджет, SLO и нервы инженеров. Вопрос перехода на DRA перестаёт быть теоретическим — он становится экономическим и операционным.
Device Plugin: контракт между kubelet и драйвером
Device Plugin — это фреймворк расширения kubelet, позволяющий подключить внешний процесс (обычно в виде DaemonSet) для:
- обнаружения устройств;
- сообщения kubelet о доступных устройствах;
- обработки запросов на выделение устройств (Allocate).
Ключевой момент: Device Plugin работает на уровне kubelet, а не scheduler. Планировщик видит только абстракцию: «на узле есть nvidia.com/gpu: 8». Реальное распределение происходит уже на узле.
Работа по шагам:
- PodSpec запрашивает
nvidia.com/gpu: 1; - планировщик выбирает узел по целочисленному ресурсу;
- kubelet вызывает Allocate у Device Plugin;
- плагин возвращает параметры: какие устройства примонтировать, переменные окружения, девайсы;
- runtime (containerd или CRI-O) запускает контейнер.
Где это живёт на узле
Kubelet использует директорию /var/lib/kubelet/device-plugins/. Туда Device Plugin помещает свой Unix-сокет и регистрируется через kubelet.sock.
Работа по gRPC:
- Device Plugin запускает gRPC-сервис и создаёт сокет в
/var/lib/kubelet/device-plugins/; - Регистрируется в kubelet через
/var/lib/kubelet/device-plugins/kubelet.sock.
Какие gRPC-методы есть у Device Plugin
Интерфейс включает:
- GetDevicePluginOptions() — возможности плагина;
- ListAndWatch() — потоковое обновление списка устройств;
- Allocate() — выделение устройств при создании контейнера;
- опционально GetPreferredAllocation() и PreStartContainer().
Kubelet также предоставляет Registration-сервис с методом Register(). Важно: плагин должен начать обслуживать запросы до регистрации.
Что происходит при рестарте kubelet
При старте kubelet удаляет все сокеты в /var/lib/kubelet/device-plugins/. Device Plugin обязан пережить это и перерегистрироваться. Это ожидаемое поведение, описанное в документации.
На практике это объясняет частые проблемы: «обновили kubelet — и GPU пропали». Причина — не в мистике, а в жизненном цикле сокетов и совместимости компонентов.
Почему Device Plugin перестаёт справляться
Device Plugin не плох. Он эффективно решает задачу эксклюзивного выделения целых устройств.
Но с ростом AI-нагрузок ожидания изменились:
- GPU больше не редкий ресурс для одного эксперимента;
- они стали частью продуктовых платформ;
- появилась потребность в разделении, изоляции, топологии и обслуживании без простоев.
Главная проблема: в Kubernetes с Device Plugin GPU остаётся extended resource — внешней сущностью. Платформа знает только количество, но не понимает внутреннюю структуру устройства.
Ограничения extended resources
Для nvidia.com/gpu действуют строгие правила:
- если указан
limits, он считается и заrequests; - если указаны оба — они должны совпадать;
requestsбезlimitsзапрещены.
Ресурс — целочисленный, неделимый и не может быть выделен нескольким контейнерам.
В коммунальном кластере это приводит к проблемам:
- инференс забирает узлы целиком;
- обучение требует пачки GPU одновременно;
- мелкие задачи вынуждены занимать целые GPU, снижая утилизацию.
Квоты тоже работают поштучно. Это ограничивает гибкость управления ресурсами.
Когда нужен не целый GPU, а его часть, индустрия прибегает к костылям: time-slicing, MPS, MIG, vGPU, SR-IOV, кастомные шедулеры.
Популярные практики разделения GPU
Практика 1. GPU Operator: device plugin — это только верхушка
Установка Device Plugin — лишь часть инфраструктуры. Полноценный GPU-контур включает драйверы, container runtime, мониторинг, MIG-менеджер, NFD-лейблы.
GPU Operator объединяет эти компоненты, но имеет ограничения: требует интернета, ограниченная кастомизация. Некоторые команды предпочитают ручную сборку.
Device Plugin решает выделение GPU, но не покрывает жизненный цикл инфраструктуры на узле.
Практика 2. Time-slicing: видимость большего количества GPU
Реализуется через конфигурацию Device Plugin: он публикует увеличенное количество nvidia.com/gpu. Несколько подов делят один GPU по времени.
Проверка:
kubectl describe node <gpu-node>— смотримCapacity/Allocatable;- запускаем несколько подов с
nvidia.com/gpu: 1— они стартуют на одном узле.
Грабли:
- нет изоляции памяти и сбоев;
- нет гарантии пропорциональной производительности;
- метрики DCGM не разделяются по контейнерам;
- изменения конфигурации не всегда подхватываются автоматически — может потребоваться перезапуск DaemonSet.
Практика 3. MIG: аппаратная нарезка с высокой стоимостью эксплуатации
MIG (Multi-Instance GPU) даёт аппаратную изоляцию. Но требует сложного управления:
- конфигурация применяется на узле;
- требуется остановка подов и иногда перезагрузка;
- нужно синхронизировать с Kubernetes-ресурсами.
В GPU Operator за это отвечает MIG Manager — привилегированный DaemonSet, который:
- следит за лейблом
nvidia.com/mig.config; - останавливает поды и хостовые процессы;
- применяет новую конфигурацию через
mig-parted; - может требовать перезагрузки.
Проблемы:
- логика управления — в shell-скрипте из ConfigMap;
- в образе MIG Manager есть
kubectl— нестандартно для контроллера; - обновление ConfigMap не всегда отражается в поде из-за кэширования файла.
Грабли:
- переконфигурация требует остановки нагрузок;
- баги драйверов (например, зависание задач при смешанном использовании MIG и целых GPU);
- ошибки в топологии — высокая цена при динамическом управлении.
Практика 4. vGPU: два мира
4.1 NVIDIA vGPU — решение для виртуализации, ориентированное на виртуальные машины (например, KubeVirt). Требует лицензирования и не является Kubernetes-native.
4.2 vGPU для контейнеров — реализуется через Volcano + HAMi, где:
- HAMi-core — программная виртуализация через hijacking CUDA API;
- Dynamic MIG — динамическое создание MIG-инстансов.
Появляются:
- отдельный планировщик (Volcano);
- новые ресурсы (например,
volcano.sh/vgpu-); - свой механизм распределения.
Грабли:
- это отдельная экосистема, а не пара YAML;
- настройка через конфиг-файл не работает — device plugin читает только аргументы командной строки;
- конфликты плагинов на узле — рекомендуется один GPU device plugin.
Практика 5. Топология: не любой GPU подойдёт
Теперь важно не только количество, но и качество GPU:
- NVLink между картами;
- одна NUMA-область;
- конкретный SKU;
- связь с NIC для RDMA;
- избежание конфликтов по PCIe.
Device Plugin может передавать topology hints (например, NUMA-аффинити), и Kubernetes поддерживает это через Topology Manager. Но это не решает проблему полностью.
На практике команды используют:
- лейблы и taints;
- nodeAffinity;
- отдельные пулы узлов;
- кастомные шедулеры;
- ручные проверки NVLink.
Это второй аргумент в пользу DRA: хочется описывать требования через атрибуты устройства, а не угадывать подходящие узлы.
Эксплуатация: GPU внезапно исчезают
Операционные проблемы — частая боль. Device Plugin должен переживать рестарты kubelet, перерегистрироваться, следить за сокетами. Но обновления идут непрерывно:
- kubelet;
- containerd/CRI-O;
- ядро;
- драйверы NVIDIA;
- container toolkit;
- операторы;
- политики безопасности.
Несовместимости, баги или неправильный порядок обновления могут привести к исчезновению GPU.
Требования к платформе растут:
- устройства должны быть проверены до начала работы;
- выделение — детерминированное;
- состояние драйверов и железа — полностью наблюдаемо;
- должен быть понятный механизм вывода узлов в обслуживание;
- обновления не должны быть лотереей.
Всё это выходит за рамки изначальной концепции Device Plugin.
Итоги: где Device Plugin хорош, а где начинаются разDRAжения
Device Plugin отлично справляется с:
- эксклюзивной выдачей целых устройств;
- вендор-специфичной подготовкой устройств в Allocate;
- сигнализацией о состоянии устройств и их видимостью для kubelet.
Device Plugin упирается в потолок, когда нужны:
- гибкое и предсказуемое разделение устройств;
- описание запросов через атрибуты (SKU, память, топология, fabric и т.д.);
- управление жизненным циклом устройства как полноценного объекта платформы;
- унифицированная модель для GPU, сетевых адаптеров, FPGA и разных вендоров.
Ощущение, что Device Plugin перестал быть достаточным, возникает не из-за его неисправности, а потому что индустрия вышла за рамки его изначального назначения.
Именно поэтому появляется DRA — следующий шаг в эволюции управления устройствами в Kubernetes.