Введение: Почему GPU в Kubernetes — это отдельная история

Введение: Почему GPU в Kubernetes — это отдельная история

Когда вы только начинаете работать с Kubernetes, всё кажется простым: есть контейнеры, есть поды, есть ноды. Вы запускаете веб-сервис, он работает, всё ок. Но как только в дело вступают GPU (видеокарты), всё меняется. Это уже не просто «еще один ресурс», как CPU или память. Это узкое место, дорогое удовольствие и специфическая архитектура, где одна ошибка может стоить вам огромных денег за простой оборудования.

Главная боль при масштабировании кластера с GPU — это дисбаланс. Вы можете создать мощную ноду, но если ваш под не умеет правильно запрашивать видеокарту или драйверы не подтянулись, нода будет простаивать, а деньги будут капать. Или наоборот: вы случайно запустили пять инференс-моделей на одной карте, и всё падает из-за нехватки VRAM.

В этой статье мы не будем рассуждать о теории облачных вычислений. Мы разберем, как реально построить кластер, который умеет делить видеокарты между задачами, масштабировать ноды, когда нагрузка растет, и не разориться на счетах за инфраструктуру. Я буду опираться на реальный опыт эксплуатации таких систем, где цена ошибки — это не просто ошибка компиляции, а стоимость арендованного GPU-сервера.


Шаг 1. Выбор стратегии доступа к GPU

Первое и самое важное решение, которое вы должны принять, даже до установки `kubectl` — это как ваши приложения будут пользоваться видеокартами. Здесь есть два основных пути, и выбор зависит от типа ваших задач.

Вариант А: Полная изоляция (1 нода — 1 под — 1 GPU)

Это классический подход. Вы выделяете целую видеокарту под один под. Если под падает, карта освобождается. Это самый простой и надежный вариант для обучения моделей (Training), где процесс должен работать без помех и иметь весь ресурс карты целиком.

Плюсы: Простота настройки, отсутствие конфликтов, предсказуемая производительность.

Минусы: Низкая плотность использования. Если вашему поду нужно всего 10% мощности карты, остальные 90% будут простаивать. Это дорого.

Вариант Б: Разделение (GPU Sharing)

Здесь мы пытаемся «нарезать» одну физическую карту на несколько виртуальных устройств, чтобы запустить на ней несколько легких инференс-сервисов или фоновых задач. Это критически важно для кластеров, где вы крутите инференс (вывод) моделей, а не обучаете их.

Для этого используются специальные технологии:

  • NVIDIA MIG (Multi-Instance GPU): Физическое разделение карт серии A100/H100 на изолированные сегменты. Очень надежно, но требует дорогого железа.
  • NVIDIA MPS (Multi-Process Service): Позволяет нескольким процессам работать на одной карте, но с ограничениями по контексту.
  • Виртуализация через vGPU (vGPU time-slicing): Сторонние решения (например, от Vast, KubeVirt или специфические хуки), которые эмулируют несколько GPU на одной карте на уровне драйвера.

Если вы строите кластер для стартапа и у вас есть бюджетные карты (например, T4 или A10), скорее всего, вам придется использовать подходы уровня MPS или time-slicing. Если у вас дата-центр с A100 — смотрите в сторону MIG.


Шаг 2. Установка драйверов и Runtime

В Kubernetes нет «родной» поддержки GPU из коробки, как у CPU. Вам нужно сказать ноде: «Эй, у тебя есть видеокарта, покажи её кластеру». Это делается через NVIDIA Container Toolkit.

Без этого инструмента вы не сможете запустить контейнер с GPU. Суть проста: в Docker/Kubernetes есть понятие runtime. По умолчанию используется runc. Для GPU нам нужен nvidia-container-runtime. Он берет на себя магию: поднимает под, подматчивает драйверы, подключает файлы устройств (device files) и устанавливает переменные окружения.

Что нужно сделать на нодах:

  1. Установить драйверы NVIDIA (версия драйвера должна соответствовать версии CUDA, которую использует ваше приложение).
  2. Установить NVIDIA Container Toolkit.
  3. Настроить Docker или containerd на использование nvidia как default или явно указывать в деплое.
  4. Установить NVIDIA Device Plugin в кластер. Это де-факто стандарт. Это под, который крутится в системе, видит все GPU на ноде и сообщает контроллеру Kubernetes: «У меня есть 4 карты, они свободны».

Без Device Plugin вы не сможете делать kubectl top nodes и видеть загрузку GPU, а главное — система не поймет, что карта доступна для оркестрации.


Шаг 3. Базовая конфигурация деплоя

Теперь, когда инфраструктура готова, давайте посмотрим, как выглядит запрос ресурса. В `yaml`-файле под это делается в секции `resources`.

Обратите внимание: вы не запрашиваете просто «видеокарту». Вы запрашиваете конкретный тип ресурса, который зарегистрировал Device Plugin. Обычно это nvidia.com/gpu: 1.

resources:
  limits:
    nvidia.com/gpu: 1  # Запрашиваем одну карту
  requests:
    nvidia.com/gpu: 1

Если вы укажете limits и не укажете requests, нода может выдать ошибку. Kubernetes хочет знать, сколько ресурсов он резервирует. Для GPU это критично, так как их нельзя «сверхвыделять» (overcommit) так же легко, как память.


Шаг 4. Масштабирование: Node Autoscaling

Самая частая ошибка — пытаться масштабировать GPU-кластер так же, как CPU. На CPU можно добавить 2 ядра и 4 Гб памяти на лету. На GPU это невозможно. Вы не можете «добавить видеокарту» к существующей ноде (если это не облачный провайдер с горячим подключением, что редкость). Вам нужно добавлять целые ноды.

Здесь вступает в игру Cluster Autoscaler (или аналоги вроде Karpenter в EKS, Cluster Autoscaler в GKE/AKS).

Как это работает:

  1. Вы пытаетесь запустить под с требованием 1 GPU.
  2. Сcheduler смотрит на ноды: «Хм, на всех нодах карты заняты или их вообще нет».
  3. Сcheduler помечает под как Pending.
  4. Autoscaler видит Pending под, связанный с GPU, и говорит облачному провайдеру: «Создай новую ноду с GPU».
  5. Новая нода поднимается, ставится драйвер, под падает на неё.

Важный нюанс: Масштабирование GPU-кластера медленное. Создание ноды с GPU, установка драйверов, запуск Device Plugin может занять от 5 до 15 минут. Если ваша задача требует мгновенного ответа (например, скачок трафика на 10 секунд), автоскейлинг не успеет помочь. Для таких случаев нужно держать «теплый пул» (warm pool) — несколько свободных нод, которые просто ждут работы.


Шаг 5. Сравнение подходов к масштабированию

Давайте сравним, как ведут себя разные стратегии масштабирования GPU-кластера в реальной жизни. Это поможет вам выбрать, на что ориентироваться при планировании бюджета и архитектуры.

Критерий Статичный пул (Fixed Cluster) Cluster Autoscaler (CA) Karpenter / Современный Autoscaler
Скорость реакции Мгновенно (ноды всегда готовы) Медленно (5-15 минут на старт ноды) Быстро (оптимизированный запуск, ~2-5 мин)
Стоимость в простое Высокая (платите за простаивающие карты) Низкая (ноды удаляются, когда работа кончилась) Средняя/Низкая (зависит от настроек TTL)
Сложность настройки Низкая (купил и забыл) Средняя (нужно настроить мин/макс размеры) Высокая (требует тонкой настройки под облако)
Риск неудачного деплоя Низкий (ресурс точно есть) Средний (если провайдер не выдал инстанс) Низкий (умный подбор типа инстанса)
Идеальный сценарий Фоновые задачи, ночное обучение, стабильные нагрузки Событийные нагрузки, тесты, спорадический инференс Сложные микросервисы с разными типами GPU

Обратите внимание на последний столбец. Karpenter (и подобные ему инструменты второго поколения) умеет делать то, что не умеет классический Autoscaler: он понимает, что у вас есть под с требованием «мало памяти, но мощная карта», и сразу выбирает специфичный инстанс, не пытаясь подобрать универсальную ноду из списка шаблонов.


Шаг 6. Управление очередями и приоритетами

Допустим, у вас есть 10 видеокарт. Пришли два пользователя. Один хочет обучить модель (Training), другой — запустить инференс (Inference). Если вы просто отдадите карты первому, кто пришел, второй будет ждать вечно. В продакшене так делать нельзя.

Вам нужно внедрить систему очередей. В чистом Kubernetes для этого используется Volcano или KubeFlow.

Как это работает на практике:

  • Вы создаете очереди: training-high, inference, batch-jobs.
  • Каждой очереди выделите квоту ресурсов (например, 50% GPU только для инференса).
  • Настройте приоритеты. Если задача обучения не критична, она может быть вытеснена (preempted) задачей инференса, если нода занята.

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


Частые ошибки при построении GPU-кластера

Я видел десятки случаев, когда инженеры упирались в одни и те же грабли. Вот список того, что чаще всего ломает проекты:

1. Игнорирование VRAM (Видеопамяти)

Самая частая ошибка. Вы запрашиваете nvidia.com/gpu: 1, но не задаете лимиты памяти внутри контейнера. Под начинает потреблять всю доступную память видеокарты. Если на одной ноде запустить два таких пода, они начнут убивать друг друга (OOMKilled) или вызывать падение драйвера всей системы.
Решение: Всегда указывайте limits.memory для GPU-контейнеров, если это возможно, или используйте механизмы ограничения памяти внутри CUDA (через переменные окружения).

2. Неправильный выбор инстансов

Вы решили сэкономить и купили ноды с малым количеством ядер CPU, но мощными GPU. В итоге процесс распаковки данных (preprocessing) занимает 80% времени, а GPU простаивает, ожидая данные.
Решение: GPU-кластер должен быть сбалансирован. Обычно соотношение CPU:GPU должно быть не менее 1:4 или 1:8 в зависимости от нагрузки. Проверьте метрики загрузки CPU перед тем, как жаловаться на GPU.

3. Отсутствие мониторинга

Вы не видите, что карта перегревается или работает на 100% в течение 24 часов. Без метрик (DCGM Exporter, Prometheus) вы узнаете о проблеме, когда нода уже умерла или сгорела.
Решение: Установите GPU DCGM Exporter и настройте алерты на температуру и ошибку памяти.

4. Запуск инференса на нодах для обучения

Обучение требует пропускной способности (bandwidth) и стабильности. Инференс требует низкой задержки (latency). Смешивать их на одних и тех же нодах, даже если они не загружены, — риск.
Решение: Используйте Taints и Tolerations в Kubernetes, чтобы физически разделять эти типы задач.


Сценарии выбора: Как поступить в вашей ситуации

Не существует универсального рецепта. Давайте разберем, что делать вам, исходя из вашей задачи.

Сценарий 1: Вы стартап, у вас есть бюджет, нужна надежность.
Вам не нужно изобретать велосипед. Используйте управляемый сервис (Managed Kubernetes) от крупного провайдера (AWS EKS, GKE, VK Cloud и т.д.) с их нативными инструментами автоскейлинга.
Рекомендация: Не ставьте драйверы вручную. Используйте их Marketplace-образы. Это сэкономит вам сотни часов на отладке.

Сценарий 2: У вас свои серверы (On-Premise) или дешевые VPS.
Здесь вы сами несете ответственность за драйверы. Вам критически важен NVIDIA Device Plugin.
Рекомендация: Обязательно настройте Node Affinity, чтобы ваши поды падали только на ноды с GPU. Не давайте обычным веб-серверам «весить» на GPU-нодах, если они им не нужны — это просто трата места.

Сценарий 3: Вам нужно запустить 500 легких моделей.
Покупать 500 инстансов с GPU — самоубийство.
Рекомендация: Используйте технологию разделения GPU (MIG или Time-Slicing). Если у вас нет поддержки MIG на железе, используйте volcano или кастомные решения для шедулинга, которые позволяют запускать несколько подов на одну карту, ограничивая их доступ к VRAM.


Практические рекомендации по оптимизации

Чтобы кластер был не просто рабочим, а эффективным, обратите внимание на эти детали.

Используйте Spot-инстансы с умом

Spot-инстансы (прерываемые) от провайдеров могут быть в 3-4 раза дешевле обычных. Для обучения моделей (Training) это идеально, так как задачу можно перезапустить с последней сохраненной точки (checkpoint). Для инференса это опасно — если провайдер заберет ноду, ваш сервис упадет.
Стратегия: Используйте Spot для обучения и фоновых задач. Для инференса держите резервный пул On-Demand нод. Если Spot упал, трафик переключается на On-Demand.

Настройте Taints и Tolerations

Это механизм «отталкивания» и «притягивания». Если у вас нода с GPU, по умолчанию она может быть «отравлена» (tainted), чтобы туда не падали обычные поды, которые не умеют работать с GPU. Так вы не засорите дорогие ноды пустой работой.
Пример: Тэйт gpu=true:NoSchedule. Только поды с толеранцией gpu=true смогут запускаться на таких нодах.

Мониторинг и алертинг

Установите Prometheus + Grafana.
Что отслеживать обязательно:

  • Утилизация GPU (Utilization) — чтобы понять, хватает ли мощности.
  • Использование памяти GPU (VRAM) — критичный параметр.
  • Температура GPU — чтобы нода не перегрелась.
  • Ошибки памяти (ECC errors) — предвестник поломки карты.

Итог: С чего начать прямо сейчас

Построение масштабируемого GPU-кластера — это баланс между стоимостью, производительностью и сложностью. Не пытайтесь сразу внедрить всё и сразу.

Ваш план действий:

  1. Определите типы задач. Это обучение (тяжелые, долгие) или инференс (быстрые, частые)? От этого зависит архитектура.
  2. Выберите инструмент оркестрации. Если вы не эксперт в администрировании Linux, берите Managed Kubernetes. Если вы хотите сэкономить на железе — On-Premise с K3s или K8s.
  3. Настройте Device Plugin. Без него кластер не увидит GPU. Это база.
  4. Внедрите автоскейлинг. Но помните о задержке старта ноды с GPU (минуты, а не секунды).
  5. Разделите ноды. Используйте Taints/Tolerations, чтобы отделить тяжелые задачи от легких.
  6. Наладьте мониторинг. Не запускайте GPU-кластер без дашборда, показывающего загрузку карты и памяти.

Самое главное правило: GPU — это дефицитный ресурс. Ваша задача как архитектора — не дать ему простаивать, но и не дать ему «задохнуться» из-за плохого планирования. Начните с малого: поднимите одну ноду, убедитесь, что поды видят карту, и только потом масштабируйте на десятки.

Информация в статье носит технический и ознакомительный характер. Конфигурация Kubernetes и настройка драйверов требуют административных прав и понимания рисков. В случае некорректной настройки возможны сбои в работе инфраструктуры или потеря данных. Рекомендуется проводить изменения в тестовой среде перед внедрением в продакшн.

dfncfg.ru — цифровой мир и технологии