Если вы запускаете GPU-нагрузки (графические ускорители) на Kubernetes — vLLM, Triton, обучающие задачи или более новые стеки агентного инференса, — вы наверняка сталкивались со знакомой проблемой: стандартный автоскейлинг по-прежнему мыслит в категориях CPU и памяти, а GPU, который реально делает работу, остается невидимым. Из-за этого простаивает дорогая емкость ускорителей, растет задержка инференса и расходуется лишняя энергия — ровно там, где компании пытаются ответственно масштабировать LLM и Agentic Ops (подходы к эксплуатации Agentic-систем).

VK Cloud перевела статью автора, который хотел бы, чтобы KEDA масштабировался по сигналам, которые важны для GPU-нагрузок: утилизации, памяти, температуре и энергопотреблению. На практике это не только вопрос стоимости. Это еще и вопрос GreenOps (экологичный подход к эксплуатации с минимизацией углеродного следа): впустую потраченные GPU-циклы напрямую превращаются в потраченную энергию и более высокие выбросы категории Scope 3 (косвенные выбросы в цепочке создания стоимости).

Оказалось, что это сложнее, чем кажется. Дальше повествование идет от его лица

Проблема

KEDA собирается с CGO_ENABLED=0. NVIDIA Management Library (NVML) — стандартный способ читать метрики GPU — требует CGO (механизм Go для вызова кода на C). Поэтому GPU-скейлер нельзя просто добавить в ядро KEDA так же, как добавляют скейлер для Prometheus или Kafka.

А еще оператор KEDA работает как единый deployment. Вызовы NVML локальны: они читают метрики с GPU на той же ноде. Запросить GPU 0 на ноды-A из пода на ноду-B не получится.

Так что нативный скейлер для KEDA отпадает. Мне нужно что-то, что запускается на каждой GPU-ноде и отдает метрики по сети.

Архитектура

Чтобы решить эту задачу, можно собрать кастомный DaemonSet (мою референсную реализацию смотрите здесь: keda-gpu-scaler), который работает на GPU-ноде. В этой архитектуре каждый под:

  1. Вызывает NVML через go-nvml, чтобы прочитать локальные метрики GPU.

  2. Отдает их по gRPC через интерфейс ExternalScaler от KEDA.

  3. Оператор KEDA подключается к скейлеру и управляет решениями HPA.

Блок-схема GPU-node (DaemonSet)
Блок-схема GPU-node (DaemonSet)

Это тот же паттерн, который Kubernetes использует для device plugins и metrics server: агент на каждой ноде собирает локальные данные оборудования.

По каким метрикам можно масштабировать

Скейлер отдает по каждому GPU такие метрики:

  • gpu_utilization — процент утилизации SM (Streaming Multiprocessor, вычислительное ядро GPU);

  • memory_utilization — утилизация контроллера памяти;

  • memory_used_percent — использование VRAM (видеопамять GPU) в процентах;

  • temperature — температура кристалла GPU в градусах Цельсия;

  • power_draw — текущее энергопотребление в ваттах.

Для multi-GPU ноды вы выбираете агрегацию: max, min, avg или sum — либо нацеливаетесь на конкретный индекс GPU.

Готовые профили

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

triggers:

  - type: external

    metadata:

      scalerAddress: "keda-gpu-scaler.gpu-scaler.svc.cluster.local:6000"

      profile: "vllm-inference"

Профиль

Метрика

Цель

Активация

Сценарий

vllm-inference

memory_used_percent

80%

5%

Обслуживание LLM, scale-to-zero

triton-inference

gpu_utilization

75%

10%

Обслуживание моделей Triton

training

gpu_utilization

90%

0%

Обучающие задачи, без scale-to-zero

batch

memory_used_percent

70%

1%

Пакетный инференс, агрессивное уменьшение масштаба

Любое значение можно переопределить или вообще пропустить профили и задать metricType, targetValue, activationThreshold напрямую.

Быстрый старт

Установка через Helm

helm install gpu-scaler deploy/helm/keda-gpu-scaler \

  --namespace gpu-scaler --create-namespace

Chart разворачивает DaemonSet, который нацеливается на ноде с nvidia.com/gpu.present=true и толерирует taint nvidia.com/gpu. По умолчанию он использует NVIDIA container runtime — если в вашем кластере его нет, установите nvmlHostMounts.enabled=true, чтобы примонтировать файлы устройств напрямую.

Создание ScaledObject

apiVersion: keda.sh/v1alpha1

kind: ScaledObject

metadata:

  name: vllm-gpu-scaler

spec:

  scaleTargetRef:

    name: vllm-deployment

  minReplicaCount: 0

  maxReplicaCount: 8

  triggers:

    - type: external

      metadata:

        scalerAddress: "keda-gpu-scaler.gpu-scaler.svc.cluster.local:6000"

        profile: "vllm-inference"

Вот и всё. Теперь KEDA будет масштабировать ваш vLLM deployment по использованию памяти GPU, включая scale-to-zero (уменьшение до нуля реплик) при простое.

Тестирование без GPU

У скейлера есть режим мок-коллектора для тестирования. Набор e2e-тестов поднимает реальный gRPC-сервер с фейковыми данными GPU и прогоняет весь поток IsActive → GetMetricSpec → GetMetrics. 11 тестов покрывают профили, пути ошибок, стриминг, режимы агрегации. Все запускаются в CI без какого-либо GPU-оборудования.

go test -v -tags=e2e -race ./tests/e2e/

Что дальше

Создание кастомных external скейлеров — мощный способ расширить экосистему CNCF. Это показывает, как graduated-проект вроде KEDA остается гибким: инженеры собирают кастомные DaemonSet под более новые паттерны AI-инфраструктуры и не вгоняют каждую нагрузку в одну и ту же абстракцию.

Сниппеты кода выше и репозиторий — это open-source референсная реализация архитектуры. Если вы запускаете GPU-нагрузки на Kubernetes и хотите автоскейлинг, который нативно понимает метрики GPU, начните с интерфейса ExternalScaler от KEDA — это отличная отправная точка.

GitHub: keda-gpu-scaler