Kubernetes-оператор для Tantor в production: failover, backup и мониторинг через Prometheus
Деплоили Tantor через K8s-оператор в production: оператор взял на себя failover, backup и scaling - делимся опытом, нюансами persistent storage и мониторингом.
Выход production-зрелых Kubernetes-операторов для отечественных СУБД - Tantor, Postgres Pro - конец 2025
К концу 2025 года операторы для отечественных СУБД в Kubernetes перестали быть экспериментом на GitHub и начали появляться в production у реальных заказчиков. Мы деплоили Tantor через K8s-оператор на одном из проектов сопровождения - и вот что из этого вышло.
Контекст: почему оператор, а не Patroni вручную
У заказчика уже работал Kubernetes-кластер (Deckhouse), туда же хотелось вынести СУБД - без отдельного «ручного» Patroni на выделенных VM-ках. Логика понятная: один инструмент деплоя, единый CI/CD, мониторинг уже в Prometheus/Grafana. Мы с HA на Patroni знакомы хорошо, но держать отдельный стек рядом с K8s - это операционная нагрузка, которую хотелось убрать.
Оператор для Tantor появился в публичном доступе летом, а к ноябрю-декабрю набрал достаточно фикс и документации, чтобы пробовать его в production. Официально он называется production-ready - мы отнеслись к этому с разумным скептицизмом.
Что оператор берёт на себя
Базовая механика стандартная для оператора БД: вы описываете кластер в CRD (TantorCluster), оператор разворачивает StatefulSet с primary и репликами, настраивает репликацию, ставит PodDisruptionBudget, прописывает сервисы для чтения и записи.
Три вещи, которые нас интересовали в первую очередь:
- Failover. При падении primary-пода оператор запускает смену роли на реплике без ручного вмешательства. Под капотом там своя логика на базе etcd-locks, не Patroni. Время обнаружения и переключения - несколько секунд, конкретика зависит от таймаутов healthcheck в CRD.
- Backup. Оператор поддерживает scheduled backup через встроенную интеграцию с S3-совместимым хранилищем. Вы указываете расписание и endpoint в spec кластера, оператор сам запускает pg_basebackup-based джобы и пишет туда WAL-архив.
- Scaling. Добавить реплику - это изменить
replicasв манифесте. Оператор сам добавляет ноду, запускает base backup с primary, поднимает репликацию. На практике это работало ровно так, как написано в документации, без сюрпризов.
Нюансы persistent storage
Вот тут началась основная возня. Оператор создаёт PVC для каждого пода StatefulSet-а, и от того, как настроен StorageClass, зависит очень много.
Проблема с ReadWriteOnce. Мы использовали локальные диски через local-path-provisioner - для production PostgreSQL это нормально, если ноды фиксированные. Но при пересоздании пода на другой ноде (после drain, например) PVC с volumeMode: Filesystem и accessModes: ReadWriteOnce просто не примонтируется - нода не та. Это поведение Kubernetes, не баг оператора, но документация оператора этот момент не акцентирует. Решили через node affinity: каждый StatefulSet-под привязан к конкретной ноде через label. Неэлегантно, но предсказуемо.
Размер WAL-архива. Оператор кладёт WAL-архив в тот же PVC, что и данные, если не указать отдельный volume для архива в spec. При высоком темпе записи это быстро съедает место. Мы это поймали не в production - в staging, что хорошо, - и вынесли WAL-архив на отдельный PVC с другим StorageClass (там уже сетевое хранилище).
fsGroup и права. Tantor внутри контейнера запускается от своего uid/gid. Если StorageClass монтирует том без правильного fsGroup в securityContext, база не стартует с ошибкой прав на datadir. Оператор выставляет fsGroup по умолчанию - но если ваш StorageClass делает что-то нестандартное с ownership, нужно проверять.
Мониторинг через Prometheus
Оператор разворачивает ServiceMonitor (если установлен Prometheus Operator) и экспортирует метрики через postgres_exporter, встроенный в под. Набор метрик стандартный: connections, replication lag, transaction rate, lock waits.
Несколько вещей, которые пришлось настроить дополнительно:
- Алерт на смену primary. Оператор сам не пишет событие в Prometheus при failover. Мы добавили алерт на основе изменения метки
roleвpg_replication_is_replica- если значение меняется, это сигнал что произошло переключение. Грубовато, но работает. - Мониторинг backup-джобов. Встроенного экспортёра статуса backup нет. Смотрим на статус Kubernetes Job через
kube_job_status_succeededиkube_job_status_failed- backup-джобы именуются предсказуемо, фильтруем по prefix. - WAL-lag реплик.
pg_stat_replicationдоступна черезpostgres_exporter, но только с primary. Если primary недоступен - нет метрик. Это особенность архитектуры, не специфика Tantor, но учитывать нужно при алертинге.
Отдельно отметим: сертификация Tantor SE, которую мы разбирали в ноябре, пока относится к конкретной сборке. Оператор деплоит тот же дистрибутив, но нужно убедиться, что образ в CRD указывает именно на сертифицированную версию, а не на тег latest из публичного registry.
Что по failover в реальности
Мы намеренно убивали primary-под несколько раз в тестовом окружении, аналогичном production. Оператор обнаруживал падение и завершал переключение за время, которое укладывалось в наш SLO. Клиент (приложение с connection pooling через встроенный PgBouncer-образ) терял соединение и переподключался к новому primary через Service - стандартная схема.
Один неприятный момент: при одновременном падении primary и одной из реплик (мы это тоже проверили) оператор уходил в ожидание кворума дольше ожидаемого. Это не баг - это корректное поведение для split-brain prevention - но таймаут в конфигурации по умолчанию оказался великоват. Настраивается через параметры в spec, уменьшили вдвое.
Итого на сейчас
Оператор для Tantor в конце 2025 года - это рабочий инструмент, но не «поставил и забыл». Failover и scaling работают как обещано. Storage требует явного проектирования под конкретный кластер Kubernetes. Мониторинг нужно доводить руками, потому что встроенного покрытия для операционных сценариев недостаточно.
Для проектов, где Kubernetes уже есть и нужна отечественная СУБД без отдельного VM-стека, это разумный выбор. Для тех, у кого нет Kubernetes и PostgreSQL стоит на VM - переходить только ради оператора смысла не видим, Patroni справляется.
Подробнее о подходах к сопровождению PostgreSQL-кластеров - в разделе managed-сопровождения.