Как сэкономить на GPU в Kubernetes с помощью Karpenter
Счёт за GPU выставляется по часам аренды, а не по числу обработанных запросов: карта арендуется 730 часов в месяц, а полезной работой занята 150–200. Karpenter закрывает этот разрыв, но с ускорителями ведёт себя иначе, чем с обычными сервисами.
Почему GPU-инференс требует отдельного подхода к масштабированию
Схема трёхуровневая: HPA добавляет реплики, VPA подбирает requests и limits, Karpenter добавляет ноды. Отсюда типовая ситуация «HPA отрабатывает, а поды остаются в Pending»: HPA не умеет создавать ёмкость.
Чем масштабирование ускорителей отличается от веб-сервиса
Дискретность: nvidia.com/gpu: 1 — минимальная единица, половину карты стандартный планировщик не выдаст. Цена ошибки: разница между 4 и 5 vCPU в бюджете теряется, между двумя и тремя картами — нет. Холодный старт: выделение ВМ, драйверы, device plugin, пул образа, загрузка весов в VRAM — суммарно минуты, и это норма. Четвёртое часто упускают: загрузка GPU — плохой сигнал для скейлинга. Карта может показывать 95–100% на неэффективном батче и обслуживать вдвое меньше запросов, чем могла бы.
Что Karpenter даёт вместо Cluster Autoscaler
Cluster Autoscaler двигает счётчик внутри заранее описанных групп нод: под каждый тип карты нужна отдельная группа. Karpenter смотрит на требования подов в Pending и обращается к API облака напрямую, подбирая тип инстанса под задачу, а consolidation проверяет, нельзя ли разместить поды плотнее.
Манифесты с karpenter.sh/v1alpha5 и объектом Provisioner устарели. Текущий API — karpenter.sh/v1 с парой NodePool (что и в каких границах поднимать) и NodeClass (как поднимать: образ, диск, сеть).
Как настроить GPU-пул в Karpenter и не потерять на нём деньги
Если GPU-ноды не изолированы, на них попадёт обычная нагрузка — сборщик логов, фоновый обработчик, тестовый под.
Конфигурация GPU-пула: requirements, taints и связка с драйверами
apiVersion: karpenter.sh/v1
kind: NodePool
metadata:
name: gpu-inference
spec:
template:
spec:
requirements:
- key: node.kubernetes.io/instance-type
operator: In
values: ["gpu-a100-40", "gpu-l40s-48"]
- key: karpenter.sh/capacity-type
operator: In
values: ["on-demand"]
taints:
- key: nvidia.com/gpu
value: "true"
effect: NoSchedule
nodeClassRef:
group: karpenter.k8s.local
kind: NodeClass
name: gpu-node-class
limits:
nvidia.com/gpu: "8"
disruption:
consolidationPolicy: WhenEmpty
consolidateAfter: 15m
- Taint на пуле. Без него дорогая нода становится обычной нодой с большой памятью. Инференс-поды получают toleration, остальные — нет.
- Лимит
nvidia.com/gpu. Даже если HPA попросит двадцать реплик, пул не выйдет за восемь карт. consolidationPolicy:WhenEmpty. АгрессивнаяWhenEmptyOrUnderutilizedопасна: Karpenter снимет ноду с подом, который сейчас генерирует ответ.
Драйверы и device plugin ставятся через GPU-оператор NVIDIA; его поды должны иметь toleration к вашему taint — иначе ресурс на ноде не появится. Это частая причина ситуации «нода в Ready, а под всё ещё Pending».
Сигналы масштабирования и защита инференса от вытеснения
Для LLM-инференса рабочий сигнал — длина очереди запросов и TTFT, а не загрузка карты. Собирается через Prometheus (метрики движка плюс DCGM exporter) и prometheus-adapter, отдающий кастомную метрику в metrics API для HPA. Для событийных сценариев удобнее KEDA — она умеет scale-to-zero.
- PodDisruptionBudget —
minAvailableне ниже числа реплик, покрывающих трафик. Karpenter уважает PDB. - Disruption budgets на уровне NodePool — доля нод, которые контроллер вправе трогать одновременно, плюс окна запрета прерываний.
terminationGracePeriodSeconds— столько, сколько длится самая долгая генерация.
Когда автоскейлинг GPU окупается, а когда только добавляет рисков
Как посчитать экономию: стоимость часа, доля простоя, цена холодного старта
Аренда ВМ с картой уровня A100 — порядка 170 000–180 000 ₽ в месяц при почасовой оплате, примерно 240 ₽ за час; при годовом контракте ставка заметно ниже. Пик требует двух карт и держится около 9 часов в будни.
- Статичный парк. Две карты круглосуточно: 2 × 180 000 × 12 = 4 320 000 ₽ за год. Утилизация второй — около 27%.
- Динамический пул. Первая карта постоянно — 2 160 000 ₽. Вторая на пик: 9 × 21 × 12 = 2 268 часов, округляем до 2 500 часов по 240 ₽ — 600 000 ₽.
- Накладные расходы. Настройка — 120 000 ₽ разово, сопровождение — 100 000 ₽ за год, оплаченные минуты холодного старта — около 6 000 ₽.
- Итог за год: 4 320 000 ₽ против 2 986 000 ₽. Разница — около 1,3 млн ₽.
Ориентир: автоскейлинг GPU окупается примерно с 40% простоя дорогой карты, при простое ниже 25–30% выгоднее оставить постоянную ноду.
Холодный старт: выделение ВМ (1,5–3 мин), драйверы и device plugin (30–90 с), пул образа (2–5 мин), загрузка весов на 30–40 ГБ (3–8 мин), прогрев CUDA-контекста (30–120 с). Итого 8–18 минут, и всё это время карта тарифицируется. Сокращают предзагрузкой образов, кэшем весов на локальном NVMe, тёплым резервом.
Для команд, которым нужны ускорители под обучение и инференс без покупки железа, задачу закрывает облачный GPU-сервер ML-платформы Cloud4U: ёмкость берётся под фактический профиль нагрузки, а размещение в российских ЦОДах уровня Tier III снимает вопросы с 152-ФЗ. Суммы ориентировочны: итог сдвинется на ±25% в зависимости от типа карты и условий договора.
Сценарии, где динамический пул не нужен
- Стабильная круглосуточная нагрузка. Простой ниже 20–25% — снимать нечего.
- Жёсткое требование к TTFT. Холодный старт в десять минут не успеет за SLA в секунды.
- Одна-две карты в парке. Экономию даёт шеринг: MIG, time-slicing или MPS.
Квоты на GPU-инстансы стоит согласовывать с провайдером заранее, а инференс на пользовательских данных требует аттестованного контура. Начинать стоит с замера: сколько часов карта реально загружена и какой профиль у пиков.