KEDA на Kubernetes: базис перед автомасштабированием

Прежде чем внедрять KEDA, сделайте вот что

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

Габриэль Аринс · Инженер-программист, бэкенд и распределённые системы

Большинство статей о KEDA начинаются с YAML-файла ScaledObject. Эта начинается раньше.

Прежде чем написать хоть строчку конфигурации, я потратил время на кое-что, что оказалось самой ценной частью всего проекта: построение базиса (baseline). Два месяца производственных данных, тепловые карты простоя CPU, симуляции количества реплик и финансовая модель для каждой consumer-группы. Только после этого внедрение KEDA обрело смысл.

Эта статья — о том процессе, а не о самой реализации. Конкретно: как понять, нужно ли автомасштабирование вашей нагрузке, какие данные собирать и как превратить сырые метрики в приоритизированный план развёртывания.

Вопрос не в том, «как настроить KEDA?». Вопрос в том, «действительно ли моим потребителям нужно автомасштабирование, и с каких начать?»

Почему базис важен

Kafka-потребители (consumers) на Kubernetes со временем накапливают фиксированное количество реплик. Сервис запускается с тремя репликами, случается инцидент со всплеском нагрузки, кто-то поднимает число до пяти — и так остаётся навсегда. Никто не пересматривает это значение, потому что «всё работает».

Слово «работает» в этом предложении несёт слишком большую нагрузку. Чаще всего оно означает: сервис не бросает ошибок. Это не значит, что количество реплик выбрано правильно.

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

Базис отвечает на три вопроса ещё до того, как вы коснётесь конфигурации:

  • Какие потребители на самом деле перегружены или, наоборот, обеспечены ресурсами с запасом?

  • Как выглядит реальное окно масштабирования для каждого из них?

  • Какова ожидаемая экономия на каждом потребителе и оправдывает ли она усилия по реализации?

Шаг 1 — Собираем данные о простое CPU (не просто об использовании)

Первый инстинкт при анализе потребления ресурсов — смотреть на утилизацию CPU. Это полезно, но это неправильная основная метрика для Kafka-потребителей.

Потребитель, ожидающий сообщений, использует почти нулевой CPU. Поэтому средняя утилизация будет выглядеть низкой, даже если реплики правильно масштабированы. По-настоящему нужно измерить: как часто этот потребитель простаивает и когда именно?

Метрика, которую я использовал: процент времени, когда потребитель находился ниже 20% своего CPU-запроса (CPU request), разбитый на трёхчасовые интервалы. Это даёт тепловую карту простоя — представление о том, когда потребитель действительно ничего не делает, а когда реально обрабатывает данные.

Ключевая метрика: % времени, когда CPU < 20% от запроса, по трёхчасовым слотам и по каждому потребителю. Это показывает окна простоя — а не просто среднюю утилизацию.

В GCP/Cloud Monitoring (или аналогах) это извлекается запросом по метрике container/cpu/request_utilization. Гранулярность в три часа даёт достаточное разрешение, чтобы видеть суточные паттерны без лишнего шума.

Что искать: потребители, у которых тепловая карта по большей части зелёная (простой >60% показаний) на значительных отрезках дня — это ваши кандидаты. Потребители с равномерной активностью на протяжении всего дня, скорее всего, нормально обеспечены ресурсами — KEDA сэкономит на них меньше.

Также соберите: P99-пик CPU (чтобы понять интенсивность всплесков) и среднее значение CPU в связке с процентом простоя. Контраст между средним и P99 покажет, с чем вы имеете дело: с равномерно малонагруженным сервисом или с событийным, работающим вспышками (bursty).

Шаг 2 — Составляем профиль Kafka-лага

Простой CPU говорит вам, когда потребители не работают. Kafka-лаг говорит, когда им следует работать.

Соберите лаг consumer-группы (consumer group lag), разбив его на те же трёхчасовые интервалы. Вы строите картину того, когда лаг реально накапливается — именно это будет триггером масштабирования в KEDA.

Важные метрики:

  • Медианный лаг — ваша нормальная база. У хорошо настроенных потребителей он должен быть близок к нулю большую часть времени.

  • P99-лаг — определяет конфигурацию lagThreshold. Порог должен быть выше нормального разброса P99, но ниже точки, при которой задержка обработки становится проблемой.

  • Пиковый лаг и время его возникновения — показывает максимальный потолок всплеска и предсказуем ли паттерн (ежедневный пик) или случаен.

Сочетание окон простоя CPU и окон всплесков лага даёт профиль масштабирования: когда масштабировать вниз и когда вверх. Если эти окна чётко разделены — например, простой ночью и активность в рабочее время — это идеальный кандидат для KEDA.

Потребитель с предсказуемыми ежедневными всплесками лага хорошо подходит для cron-триггера предварительного масштабирования в дополнение к lag-триггеру. Прогрев реплик до всплеска предотвращает отставание потребителей, пока KEDA реагирует.

Шаг 3 — Подбираем правильный размер ресурсов до моделирования затрат

Этот шаг легко пропустить, но это важная ошибка. Если ваши запросы ресурсов (resource requests) заданы неверно, финансовая модель будет неверной, а прогнозируемая экономия — неточной.

Запросы ресурсов определяют, сколько вычислений каждая реплика «стоит» с точки зрения планировщика. Если у потребителя запрос CPU — 400m, а фактическое среднее потребление — 5m, каждая реплика обходится вам как будто потребляет 400m, хотя реально использует почти ничего.

Если перед внедрением KEDA скорректировать запрос этого потребителя до 50m, то:

  • финансовая модель будет отражать реальную стоимость вычислений на реплику, а не раздутую;

  • KEDA будет масштабироваться исходя из более точной стоимости реплики, и прогноз экономии станет реалистичным;

  • планировщик кластера получит более точную информацию при размещении подов во время масштабирования вверх.

Для памяти: проверьте среднее потребление памяти против запроса. Простаивающие потребители нередко используют малую долю запрошенной памяти. Корректировка памяти уменьшает ресурсный след каждой реплики — что даёт дополнительный эффект поверх сокращения числа реплик при автомасштабировании.

Одна оговорка: если запрос памяти равен лимиту памяти, новые поды, запускаемые KEDA при всплеске, могут столкнуться с проблемами планирования, поскольку планировщик не имеет гибкости. Перед активацией автомасштабирования убедитесь, что лимит памяти превышает запрос — даже на умеренную величину.

Шаг 4 — Строим финансовую модель

Имея точные запросы ресурсов и чёткий профиль масштабирования, можно оценить, во что на самом деле обойдётся KEDA — и сравнить это с текущими затратами.

Модель несложная. Для каждого потребителя:

  • Текущие затраты = (запрос CPU × цена CPU в GCP/AWS + запрос памяти × цена памяти в GCP/AWS) × количество реплик × часов в месяц

  • Ожидаемые затраты с KEDA = та же формула, но вместо фиксированного количества реплик используется среднее расчётное. Среднее число реплик получается из симуляции поведения KEDA на ваших данных о лаге: как часто порог превышался бы и какое количество реплик это вызывало?

Симуляция упрощена — реальное поведение KEDA зависит от периодов охлаждения (cooldown), порогов активации и интервалов опроса. Но её точности достаточно, чтобы выстроить рейтинг приоритетов и убедиться, что проект вообще стоит затеивать.

Пример того, как такая модель может выглядеть для набора потребителей в похожем окружении:

Таблица финансовой модели по consumer-группам

Значения иллюстративны — проценты и соотношения отражают реальные паттерны из производственной среды. Суть в структуре: высокий процент простоя CPU + несколько фиксированных реплик = сильный кандидат для KEDA. Низкий простой = слабый кандидат вне зависимости от абсолютных затрат.

Тепловая карта простоя CPU по потребителям и трёхчасовым слотам

Время простоя CPU по каждому потребителю в разрезе трёхчасовых слотов. Зелёный = простой (>60% показаний ниже 20% запроса CPU) = возможность для масштабирования вниз. Красный = активность = реплики нужны. Два из четырёх потребителей простаивали 99–100% времени во все часы — работая на полном количестве реплик круглосуточно без какой-либо причины.

Шаг 5 — Расставляем приоритеты

Имея готовую финансовую модель, расстановка приоритетов становится механической задачей. Ранжируйте потребителей по:

  • Потенциальной годовой экономии — сначала наибольшая.

  • Проценту простоя CPU — чем выше простой, тем чище профиль масштабирования и тем меньше риск при настройке.

  • Предсказуемости лага — ежедневные паттерны настраиваются проще, чем случайные всплески.

  • Среде — всегда проверяйте на staging перед prod, даже если экономия на staging минимальна.

На практике это означает: начинайте с потребителя, который явно перегружен ресурсами, имеет высокий потенциал экономии и предсказуемый паттерн простоя. Используйте staging для проверки конфигурации ScaledObject перед тем, как трогать продакшн.

Порядок внедрения: staging наиболее приоритетного потребителя → проверка поведения → prod наиболее приоритетного потребителя → остальные потребители по приоритету. Никогда не начинайте с продакшна.

Шаг 6 — Настраиваем KEDA на основе данных

Только теперь YAML-файл ScaledObject обретает смысл. Каждый параметр должен соотноситься с чем-то конкретным, что вы наблюдали при построении базиса.

Масштабирование подхода: что происходит на уровне всей платформы

Описанный подход масштабируется. Как только у вас есть методология и финансовая модель, применять её к новым потребителям — это инкрементальная работа.

Если на вашей платформе десятки consumer-деплойментов всё ещё работают с фиксированным числом реплик, даже консервативная средняя экономия в 30% суммируется быстро. В среде, с которой я работал, это выливается примерно в $3 000 в год на вычислительных ресурсах — и это без учёта дополнительной экономии от корректировки размеров ресурсов поверх автомасштабирования.

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

Цель не в том, чтобы внедрить KEDA везде. Цель — внедрить его там, где это даёт ощутимую экономию при приемлемом риске. Именно базис указывает, где это место.

Что может пойти не так (и как этого избежать)

  • Настройка lagThreshold без данных P99. Если порог ниже нормального разброса P99, KEDA будет постоянно масштабировать вверх — вызывая нестабильность (flapping) и сводя экономию на нет. Всегда основывайте порог на наблюдаемом P99, а не на интуиции.

  • Пропуск корректировки ресурсов. Автомасштабирование потребителя с запросом CPU 400m при реальном потреблении 5m означает, что вы масштабируете раздутые затраты вверх и вниз вместо реальных. Сначала скорректируйте ресурсы.

  • Отсутствие проверки идемпотентности потребителя. Масштабирование вверх в KEDA добавляет потребителей в ту же группу разделов (partition group). Доставка «как минимум один раз» (at-least-once delivery) означает, что потребители могут видеть дубликаты при перебалансировке. Если ваш потребитель не идемпотентен, реализуйте это свойство до активации автомасштабирования.

  • Развёртывание сразу в продакшн. Даже если экономия на staging минимальна, используйте его для проверки поведения ScaledObject под достаточно реалистичной нагрузкой. Сюрпризы в продакшне из-за неверно настроенного автомасштабирования дорого обходятся при отладке.

  • Запрос памяти равен лимиту памяти. Новым подам нужен запас для планирования. Установите лимит памяти выше запроса перед активацией автомасштабирования.

Итоговый чеклист базиса

Прежде чем написать хоть один ScaledObject:

Чеклист шагов базисного анализа перед внедрением KEDA

Заключение

Большинство инженеров воспринимают автомасштабирование как задачу конфигурирования: задай правильные параметры — и всё заработает. Это верно, но параметры имеют смысл только если анализ был сделан заранее.

Базис — это не дополнительная работа. Это и есть работа. YAML-файл ScaledObject — последний шаг, а не первый.

Если вы запускаете Kafka-потребителей на Kubernetes с фиксированным числом реплик и никогда не смотрели на тепловую карту простоя CPU — вот с чего стоит начать. Данные могут вас удивить.

© 2026 meganuke