Заметка

Три рычага против переплаты в Kubernetes: idle, scale-to-zero, right-sizing

Средняя утилизация кластера — 20–30%. Остальное вы оплачиваете впустую. Три рычага, которые возвращают деньги без переписывания архитектуры.

Средний Kubernetes-кластер использует 20–30% выделенной мощности. Значит, 70–80% provisioned capacity простаивает — и в счёте за облако это до 35% преодолимых трат. На $1M годового счёта — $350K, которые уходят не на отказоустойчивость, а на страх.

Корень проблемы — не халатность, а defensive overprovisioning. Сервис однажды упал по OOM, инженер удвоил memory request — локально рационально, глобально расточительно. Винить людей бесполезно: пока baseline в шаблоне завышен, каждый новый сервис наследует переплату. Лечится это системно, тремя рычагами. Ни один не требует переписывать приложения.

Рычаг 1. Right-sizing: привести requests к реальности

Завышенные requests — главный драйвер waste. Scheduler пакует поды по requests, а не по фактическому потреблению: сервис просит 500m CPU, ест 95m — а кластер-автоскейлер заводит лишние ноды под воздух. У большинства сервисов CPU-limits в 3–5 раз выше реального p99, результат copy-paste из шаблона без ревью.

Базовое правило — собрать за 30 дней перцентили потребления и пересчитать: новый CPU-лимит = max(p99 × 1.5, max × 1.1, floor), новый memory-лимит = max(p99 × 1.3, max × 1.1).

Ключевая асимметрия: CPU режем агрессивно (throttling обратим — под просто притормозит), memory — осторожно (OOMKill уводит под в restart-loop). floor для CPU — около 100m на GC-пики и старт; memory-floor зависит от рантайма (JVM ~256Mi, Go ~32Mi). Считать перцентили руками не нужно: Robusta KRR берёт данные прямо из Prometheus и различает bursty-батчи и steady-state-сервисы, подстраивая консерватизм рекомендаций. Заявленная экономия — 35–50% compute за 60 дней.

Рычаг 2. Scale-to-zero: не платить за простаивающее

Right-sizing ужимает работающий под; scale-to-zero убирает под целиком, когда работы нет. Три типичные цели:

  • Async-воркеры. HPA по CPU здесь бесполезен: idle-воркер не ест CPU и не масштабируется вниз. Скейлить надо по demand — глубине очереди. KEDA поднимает поды от 0 до N по длине SQS / Kafka / RabbitMQ, а cooldownPeriod гасит дребезг.
  • Preview / PR-окружения. Scale-to-zero по rate входящих запросов даёт около 60% экономии на коротко живущих средах. Ночью feature-namespace честно стоит ноль.
  • Non-prod по расписанию. Stage/demo в режиме 24/7 против 8/5 — это четырёхкратная разница в счёте.

Плата за вход — cold start (~30 секунд до первого ответа). Для latency-чувствительных очередей держат один тёплый под (paused-replicas: "1"), для тяжёлых долгих задач берут ScaledJob вместо long-running воркера. В Kubernetes 1.36 scale-to-zero для External/Object-метрик появился и в нативном HPA (alpha, feature gate HPAScaleToZero), но KEDA остаётся выбором по умолчанию: production-ready и десятки скейлеров из коробки.

Рычаг 3. Idle на уровне нод: bin-packing, consolidation, spot

Даже с честными requests остаётся idle-cost: (ёмкость ноды − сумма requests) × цена ноды. Статический пул нод фиксирует тип инстанса и пакует плохо. Karpenter переворачивает модель: провижинит ноды под конкретные поды (bin-packing), постоянно переоценивает раскладку и схлопывает недогруженные ноды (consolidationPolicy: WhenEmptyOrUnderutilized), а stateless-нагрузку с PDB уводит на spot — на 60–90% дешевле on-demand. Типичный эффект против Cluster Autoscaler с managed node groups — минус 30–60%.

Spot не для всего: базы, синглтоны без реплик и stateful без graceful-shutdown оставляют на on-demand. Рабочий паттерн — baseline на on-demand плюс burst на spot.

Замыкание: это процесс, а не разовая настройка

Главная ошибка — «настроили автоскейлер и забыли». Нагрузка и трафик меняются, и оптимизация без continuous-детекта гниёт. Поэтому рычаги работают только поверх двух вещей. Первое — видимость: OpenCost распределяет реальную стоимость по namespace/label и превращает «EC2 = $X» в «payments-api = $Y». Второе — policy: Kyverno или Gatekeeper блокируют поды без requests, иначе через месяц история с overprovisioning повторится.

Базовые меры дают 15–30% без единого архитектурного изменения. Остальное — дисциплина, а не магия.