
Современный Kubernetes приучил нас к тому, что инфраструктура должна быть эластичной. Для управления ресурсами традиционно мы можем использовать Horizontal Pod Autoscaler (HPA): растёт трафик — увеличивается количество подов, трафик падает — лишние поды удаляются. Но в высоконагруженных системах при быстром росте нагрузки стандартные инструменты масштабирования могут стать менее эффективными. Проблема заложена в самой природе реактивного подхода: система всегда догоняет реальность, а не формирует её. Решение лежит на поверхности: надо повышать осознанность и внедрять ИИ.
Меня зовут Дмитрий Чуринов, я руководитель команды разработки в трайбе Synapse. Хочу поделиться практическим опытом внедрения механизмов предиктивного масштабирования. Мой коллега Александр Козлов курирует разработку нескольких продуктов в СберТехе, в том числе Platform V SynAI, отвечающего за интерактивное управление трафиком на основе систем машинного обучения и моделирования.
Вместе мы разберём, как настроить предиктивное управление в Kubernetes на основе open source-компонентов, и почему нам этого не хватило.
Проблема №0: ноль не просто цифра
Предположим, что у нас есть редко используемый сервис, который запускается не по расписанию и привязан не ко времени, а к триггеру. Например, сервис рассылок по событиям или клинер, который запускается несколько раз в сутки. Держать их постоянно запущенными неэкономно.
Почему мы не можем использовать HPA? Стандартный HPA в Kubernetes не умеет масштабироваться в ноль и обратно. Он работает на основе текущего потребления ресурсов (CPU/RAM). Если текущее количество подов — ноль, то и потребления ноль: метрик нет, и HPA не знает, что пора просыпаться. В итоге требуется держать минимум один под на всякий случай.
KEDA: оживляем мертвых
Чтобы решить проблему масштабирования из нуля, мы взяли KEDA (Kubernetes Event-driven Autoscaling). И вот почему:
Нативная работа с событиями. Когда подов нет, KEDA смотрит не на процессор, а на источник: очередь в RabbitMQ, количество сообщений в Kafka или наличие записей в базе данных.
Масштабирование 0 < → 1. Как только в очереди появляется сообщение, KEDA подсказывает Kubernetes, что надо поднимать первый под, а дальше в дело может вступить HPA.
Гибкость. Большое количество различных триггеров, которые позволяют гибко настроить масштабирование подов под любые нужды.
Активное сообщество. Если требуется дополнительный триггер, возникает ошибка или требуется помощь, то у KEDA есть большое сообщество, где достаточно быстро отвечают на вопросы.
Как создать триггер для масштабирования
Для этого в KEDA существует спецификация ScaledObject, которая описывает определение пользовательского ресурса ScaledObject. Он определяет триггеры и режимы масштабирования.
Пример для триггера Cron: фиксированное количество реплик (масштабирование до 10 реплик с 6 утра до 8 вечера и масштабирование до 0 реплик в противном случае).
apiVersion: keda.sh/v1alpha1 kind: ScaledObject metadata: name: cron-scaledobject namespace: default spec: scaleTargetRef: name: my-deployment minReplicaCount: 0 cooldownPeriod: 300 triggers: - type: cron metadata: timezone: Asia/Kolkata start: 0 6 * * * end: 0 20 * * *
Несмотря на все достоинства, KEDA не умеет заглядывать в будущее, и масштабирование всё ещё остаётся реактивным. Рассмотрим, почему это проблема и как можно её решить.
Холодный старт и задержка подготовки
Хоть KEDA реагирует быстрее HPA и решает проблему масштабирования из нуля, ей требуется время на запуск пода.
Допустим:
9:00:00 утра — резкий подъём трафика.
KEDA это видит и даёт команду поднимать поды в 9:00:10.
На запуск подов требуется время.
Только что запущенный под с приложением работает медленнее и требует больше ресурсов.
Всё это время сервис лежит.
Поэтому нам нужно решение, которое сможет предсказать, что через 5 минут будет жарко и поднимать поды нужно уже сейчас.
Ищем пророка
Мы начали искать способ предсказать нагрузку:
Расписание (Cron): подходит, если пики нагрузки всегда выпадают на 9:00. Не сработает, если они зависят от поведения или внешних триггеров.
Линейная регрессия: не учитывает сезонность и праздники, неустойчива к мусору.
Prophet: библиотека для прогнозирования временных рядов на основе аддитивной модели.
Мы остановились на библиотеке с открытым исходным кодом Prophet, потому что она может работать с временными рядами, устойчива к отсутствию данных и сдвигам в тренде, и, как правило, хорошо справляется с отсутствующими значениями.
Как мы подружили математику с K8s
Мы реализовали схему с предиктивным триггером.
Схема работы:
Сбор данных: раз в 15 минут специальный сервис (назовём его Predictor) забирает из Prometheus историю запросов за последние 14 дней.
Обучение и прогноз: Prophet обрабатывает эти данные и выдаёт прогноз на следующий час с детализацией по минутам.
Экспорт метрики: прогноз записывается обратно в Prometheus как новая метрика http_requests_predicted.
KEDA: мы настраиваем Prometheus Scaler в KEDA так, чтобы он смотрел на http_requests_predicted.
Чего мы добились? Теперь, если Prophet предсказывает всплеск в 12:00, KEDA начинает поднимать дополнительные реплики уже в 11:55. К моменту наступления пика все поды подняты, прогреты и готовы к бою.

Пример конфигурации для Prophet:
kind: ConfigMap apiVersion: v1 metadata: name: prem-adapter-config data: metrics-config.yaml: | metrics: - name: "cpu1" query: "integration_cpu_load{}" recalculationInterval: "30s" lookbackWindow: "2h" prophet: freq: "10s" # Частота метрик periods: 200 # Количество периодов для предсказания timeout: "2d" # Время хранения метрик seasonality: # Отслеживаемые сезонности (В долях от суток). hourly: 24 # часовая сезонность ten_minutally: 144 # десятиминутная сезонность (В одном дне 1440 минут или 144 десятков минут)
Нехватка ресурсов
Здесь мы столкнулись с проблемой нехватки ресурсов. В поисках решения обнаружили, что некоторые сервисы работают в разное время. Упростим и для примера рассмотрим сервис А (ночной) и сервис Б (дневной).
Сервис А (ночной): работает с 00:00 до 06:00. Ему нужно 50 процессорных ядер.
Сервис Б (дневной): работает с 06:05 до 20:00. Ему тоже нужно 50 процессорных ядер.
Если мы забронируем 100 процессорных ядер под оба сервиса, то будем транжирить ресурсы, потому что одновременно нам требуется всего 50 процессорных ядер.
Коммунизм в мире ресурсов
Решая проблему экономии ресурсов у себя в Platform V SynAI, мы пришли к концепции Project Capacity Policy (PCP) и благодаря этому можем переключать режимы и давать ресурсы в зависимости от потребности. Это позволило не держать резервы для каждого сервиса по отдельности, а создать общий пул и из него выделять мощности туда, где они нужны.

На рисунке изображена работа приложения днёми ночью — Profile Day и Profile Night: днём работает Service 1, ночью — Service 2. Без PCP мы должны были держать запас Reserve. С PCP нам не нужно закладывать дополнительные мощности, и мы можем пользоваться и перераспределять ресурсы между Service 1 и Service 2.
Вернёмся к нашим сервисам А и Б. В результате ночью мы отдаём 50 процессорных ядер сервису А, днём их снимаем и отдаём сервису Б.
Project Capacity Policy
Остановимся подробнее на PCP. Он позволяет задать глобальные правила для всего пространства имён или проекта, и в реальном времени анализирует источники данных, решая, в каком режиме живёт сейчас проект.
PCP состоит из секции для указания источников данных Sources и секции для указания уровней использования UsageLevels.
Sources. Источники данных могут быть двух типов:
Utilization — прямой замер того, сколько сервис потребляет процессора или памяти;
Prometheus — любая кастомная метрика.
UsageLevels. Набор условий (conditions), привязанных к источникам данных. Источники описывают состояние проекта, как в примере с сервисом А и Б, «дневной режим» и «ночной режим».
Пример PCP:
kind: ProjectCapacityPolicy metadata: name: policy1 spec: sources: - name: cpu_var type: utilization config: type: cpu limit: 10 # Лимит в 10 ядер для расчета процентов - name: http_requests type: prometheus config: query: 'avg(rate(http_requests{namespace="user-namespace"}[3m]))' limit: 1000 # Лимит в 1000 RPS для границ уровня usageLevels: - name: low conditions: - source: cpu_var lowerBound: 0% upperBound: 75% - source: http_requests lowerBound: 0 upperBound: 750 ...
И теперь можно доработать ScaledObject: он больше не будет находиться в вакууме и сможет связываться с UsageLevels. Когда PCP понимает, что наступил «дневной режим», он сообщает об этом всем ScaledObject и сервисам. В таком случае «дневной режим» получает команду о том, что «минимальные реплики выставлены на 50, работай». «Ночной режим» получает информацию «ты пока не нужен».
Недостатки использования open source-инструментов
Работая в корпоративном сегменте, мы столкнулись с рядом критичных проблем.
Риски отказоустойчивости KEDA. Стандартная схема «Prophet → Prometheus → KEDA» создаёт слишком хрупкую цепочку передачи данных. Любой лаг при опросе источника или временная недоступность Prometheus может привести к замиранию всей системы масштабирования, а это слишком рискованно для критических процессов.
Безопасность. Требуются неограниченные права, нет защиты передаваемых данных и детального аудита изменений, внутри продуктов могут использоваться уязвимые библиотеки, что недопустимо в случае повышенных требований к безопасности.
Функциональная ограниченность. Стандартные триггеры KEDA изолированы и не учитывают лимиты всего проекта. Они не способны эффективно балансировать ресурсы между зависимыми сервисами в рамках общей квоты.
Эти ограничения привели нас к созданию Platform V SynAI. Мы оптимизировали математическое ядро, сделав его значительно легче и быстрее, внедрили механизмы отказоустойчивости.
Одной из ключевых фич продукта стал ProjectCapacityPolicy (PCP). Благодаря PCP SynAI обеспечивает бесшовную интеграцию между прогнозом и исполнением. В результате у нас получился надёжный, лёгкий и контролируемый бизнес-инструмент.
Заключение
Open source-инструменты KEDA и Prophet помогают перейти от реактивного масштабирования к проактивному для управления ресурсами в кластерах Kubetnetes. Для компаний, где нет критических требований по отказоустойчивости и безопасности, это рабочая схема. Но крупным заказчикам с подобными требованиями, по нашему опыту, больше подходят промышленные решения, позволяющие оптимизировать потребляемые ресурсы.