OBS PACK

Все практики

Пакет наблюдаемости

Пакет наблюдаемости соединений PostgreSQL при развёртывании в Kubernetes

Набор метрик, панелей, оповещений и проверок, который связывает развёртывание Kubernetes с ростом пулов приложений и исчерпанием соединений PostgreSQL.

PostgreSQLKubernetesObservabilityHighloadArchitecture

Развёртывание прошло штатно по мнению Kubernetes, но PostgreSQL начал отвергать соединения. Обычно это выглядит как проблема базы, хотя её создал временный состав приложения: новые Pod уже подняли пулы, старые ещё обслуживают запросы или завершаются, а лимит соединений остался прежним.

Этот пакет нужен до инцидента. Он связывает состояние Deployment, настройки пулов и фактические сеансы PostgreSQL в один проверяемый бюджет. Названия метрик ниже — внутренний контракт, а не обещание конкретного экспортера. Их нужно сопоставить с телеметрией проекта.

Граница наблюдения

Контур включает один Deployment, все его версии во время развёртывания, прикладной пул и один PostgreSQL-кластер. Если между приложением и базой стоит PgBouncer, нужны оба слоя: клиентские соединения приложения и серверные соединения посредника. Один график pg_stat_activity не объяснит, кто удерживает бюджет.

Kubernetes разрешает RollingUpdate временно создать Pod сверх желаемого числа через maxSurge. Кроме того, завершающиеся Pod не входят в availableReplicas и способны некоторое время продолжать потреблять ресурсы. Поэтому оценка replicas × pool_max — только нижняя граница возможного спроса во время смены версии, а не безопасный предел.

PostgreSQL ограничивает число одновременных сеансов через max_connections, но весь этот предел нельзя отдавать приложению. Часть мест нужна администрированию, обслуживанию, репликации и другим клиентам. Увеличение max_connections также увеличивает ресурсы, которые PostgreSQL резервирует под соединения, поэтому это не автоматический ответ на переполнение.

Контракт метрик

Сначала договориться о шести сигналах и их владельцах:

  • postgres_sessions{cluster,database,application,state} — фактические сеансы, сведённые из pg_stat_activity; application должен различать сервисы, но не экземпляры;
  • postgres_application_connection_budget{cluster,database} — утверждённая часть предела, доступная прикладным сервисам после всех резервов;
  • app_db_pool_connections{service,namespace,state} — открытые, используемые и ожидающие соединения внутри пула;
  • app_db_pool_limit{service,namespace} — действующий предел пула на один процесс;
  • workload_pods{service,namespace,revision,phase} — число готовых, неготовых и завершающихся Pod по ревизии;
  • счётчики ошибок получения соединения, тайм-аутов пула и отказов PostgreSQL с низкокардинальными причинами.

Метрики соединений должны иметь одинаковый смысл во всех версиях приложения. Идентификатор Pod допустим для короткой диагностики, но опасен в долговечных агрегатах и оповещениях. Строку запроса, идентификатор заказа и адрес клиента в метки не помещают.

Панель развёртывания

Одна временная шкала должна отвечать на последовательные вопросы.

Состав приложения

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

Спрос на соединения

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

Состояние PostgreSQL

Показать сеансы по application и состоянию, долю утверждённого прикладного бюджета, открытия и закрытия соединений, задержку установления соединения и ошибки сервера. Отдельный ряд для idle in transaction нужен потому, что такие сеансы удерживают транзакцию и способны быть более опасными, чем обычные idle.

Пользовательский эффект

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

PromQL как шаблон контракта

Доля занятого прикладного бюджета:

sum(postgres_sessions{cluster="$cluster",database="$database"})
/
max(postgres_application_connection_budget{cluster="$cluster",database="$database"})

Число ожидающих соединение процессов по сервису:

sum by (service, namespace) (
  app_db_pool_connections{cluster="$cluster",state="waiting"}
)

Потенциальный предел пула всех живых Pod:

sum by (service, namespace) (
  workload_pods{cluster="$cluster",phase=~"ready|not_ready|terminating"}
)
*
on (service, namespace) group_left
max by (service, namespace) (app_db_pool_limit{cluster="$cluster"})

Последнее выражение корректно только при одном процессе и одинаковой настройке пула на Pod. Если процессов несколько или версии различаются, предел экспортируется каждым процессом и суммируется напрямую. Это важная проверка контракта, а не деталь панели.

Логика оповещений

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

Второе оповещение ловит расхождение: число живых Pod или сумма их лимитов выросли при развёртывании, а запас бюджета стал меньше необходимого для следующего шага. Такое событие может приостановить автоматическое продвижение версии, но не должно автоматически увеличивать max_connections.

Третье оповещение ищет утечку после завершения: старая ревизия исчезла, а её application_name, открытые соединения или общий уровень сеансов не вернулись в рабочий диапазон. Для всех правил нужен for, чтобы краткая выборка между закрытием старого и открытием нового соединения не стала инцидентом.

Маршрут диагностики

  1. Проверить событие развёртывания и фактическое число Pod, включая завершающиеся.
  2. Сопоставить ревизии с настройками пула: предел, минимальный размер, время жизни и закрытие при остановке.
  3. Разложить pg_stat_activity по application_name, базе и состоянию; найти неожиданных владельцев бюджета.
  4. Проверить очередь пула и пользовательскую задержку. Свободный сеанс в PostgreSQL не помогает процессу, который исчерпал собственный пул.
  5. Если старая версия не освобождает соединения, проверить обработку завершения: прекращение приёма запросов, ожидание текущих операций и явное закрытие пула.
  6. Если обе версии ведут себя штатно, сверить стратегию maxSurge, автомасштабирование и суммарный бюджет всех Deployment, использующих кластер.

При угрозе отказа безопаснее сначала приостановить развёртывание или уменьшить параллелизм, сохранив диагностический резерв. Перезапуск PostgreSQL и безусловное расширение лимита скрывают владельца спроса и могут увеличить конкуренцию внутри базы.

Проверка перед выпуском

Тест выполняют на контуре с сопоставимой конфигурацией пулов и безопасным потолком нагрузки. Запускают развёртывание с теми же maxSurge, временем завершения и readiness-поведением. Искусственно задерживают завершение старого Pod, но не обрывают реальные пользовательские запросы. Наблюдают, что панель показывает обе ревизии, потенциальный спрос растёт, оповещение проходит состояние pending, а пауза развёртывания не лишает команду административного доступа к базе.

Успех — не отсутствие сигнала. Успехом считается правильная причинная цепочка: ревизия → число процессов → состояние пулов → сеансы PostgreSQL → пользовательский эффект. После теста старые Pod и их соединения должны исчезнуть, а правила — разрешиться без ручного удаления временных рядов.

Когда пакет даст ложную уверенность

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

Не стоит блокировать каждый выпуск только по теоретической сумме pool_max: это наказывает ленивые пулы и редкие административные процессы. Решение о продолжении развёртывания должно учитывать фактическое давление и пользовательскую ошибку. Пакет фиксирует бюджет и причинную связь; допустимый риск всё равно выбирает владелец сервиса.

Основания для проверки реализации