Обычная ситуация - у нас есть сервис запущенный в 2 подах. Для сервиса настроен HPA, который запускает новый pod при увлечении использования CPU до 80%. Это помогает поддерживать доступность приложения и масштабировать его при росте процессорной нагрузки. Но HPA не выбирает pod для каждого отдельного запроса: запрос может попасть на любую из доступных реплик.

Проблема возникает, когда несколько пользователей почти одновременно запускают длительный запрос и все их запросы попадают на один pod. Например, пользователи запустили отчёт, который удерживает соединение с базой данных в течении нескольких минут. Kubernetes не учитывает, сколько соединений занято в HikariCP каждого pod’а, поэтому все запросы легко могут попасть на один pod и исчерпать его пул, даже если у второго ещё есть свободные соединения.

Например, если в каждом pod’е пул ограничен пятью соединениями, 5 одновременно выполняющихся отчётов могут занять все соединения одной реплики. Новые запросы к ней будут ждать освобождения соединения. При этом масштабирование по CPU не обязательно поможет: нагрузка на пул базы данных не всегда сопровождается ростом CPU, а HPA меняет количество реплик, но не распределяет уже поступающие запросы по уровню занятости HikariCP.

Что можно сделать

Ограничить параллельность. Разрешать фиксированное число тяжёлых операций одновременно, а остальные ставить в очередь или отклонять с понятным ответом. Лимит на pod защищает локальный пул, но общий лимит должен учитывать все реплики приложения.

Использовать очередь и фоновых работников. API принимает задачу и возвращает её идентификатор, а worker’ы выполняют отчёты с контролируемой параллельностью. Так обработку можно масштабировать отдельно от пользовательских запросов. Цена — необходимость управлять статусами, повторами, сбоями и возможными дубликатами.

Масштабировать pod’ы. Дополнительные реплики помогают при устойчивом росте нагрузки, если база выдерживает больше одновременных запросов. HPA масштабирует количество pod’ов, но не выбирает для каждого запроса наименее загруженную реплику. Новые pod’ы также увеличивают потенциальное число подключений к базе.

Маршрутизировать с учётом нагрузки. Балансировщик или другой маршрутизирующий слой может учитывать активные запросы либо метрики пула. Это сложнее обычного Kubernetes Service и зависит от актуальности метрик. Уже выполняющийся запрос перенести на другой pod нельзя.

Изолировать отчётную нагрузку. Отдельная read replica или отчётная база может снизить влияние тяжёлых чтений на основную нагрузку. Здесь появляются дополнительные расходы и требования к допустимому отставанию данных.

Подход Польза Цена
Ограничение параллельности Защищает пул и базу от перегрузки Часть запросов ждёт или получает отказ
Очередь и worker’ы Контролируемое выполнение и независимое масштабирование Дополнительная инфраструктура и обработка сбоев
Масштабирование pod’ов Больше ресурсов приложения при устойчивом росте нагрузки Не гарантирует равномерную маршрутизацию и может увеличить нагрузку на БД
Маршрутизация по нагрузке Новые запросы можно направлять на менее занятый pod Сложность, задержка метрик и риск неверного выбора
Оптимизация или изоляция чтения Сокращает время операции или отделяет её от основной нагрузки Требует изменений в запросах или инфраструктуре

Почему не стоит просто увеличить пул

Увеличение HikariCP может сократить ожидание внутри приложения, но не обязательно повысит пропускную способность системы. Если база уже близка к пределу, больше одновременных запросов могут увеличить конкуренцию за CPU, диски и блокировки. Очередь просто переместится из приложения в базу, где её будет сложнее контролировать.

Перед увеличением пула нужно проверить, какой уровень параллельности база действительно выдерживает, и учитывать суммарное число соединений от всех pod’ов.

Как выбрать решение

Если тяжёлые операции редкие, начните с измерений и ограничения параллельности. Если отчёты запускаются регулярно и занимают минуты, очередь и отдельные worker’ы обычно дают более предсказуемый контроль. Если проблема связана с конкретными запросами, сначала оптимизируйте их или отделите отчётное чтение. Масштабирование и маршрутизацию имеет смысл добавлять, когда метрики подтверждают, что именно они устранят обнаруженное узкое место.

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