CloudNativePG: резервные копии, PITR и пулинг соединений

Проектирование отказоустойчивых систем с CloudNativePG

Как управлять резервными копиями, восстановлением и пулингом соединений, когда на кону — продакшн

В предыдущей статье я рассказал, что такое CloudNativePG (CNPG) и как запустить кластер PostgreSQL на Kubernetes.

Это была простая часть.

Но в ней осталось за кадром то, что в продакшне важно гораздо больше, — всё, что происходит после запуска кластера:

  • Что делать, если данные повреждены?

  • Как восстановить состояние на конкретный момент времени?

  • Что произойдёт, если к базе одновременно придут 500 соединений?

Запустить PostgreSQL — это не то же самое, что эксплуатировать PostgreSQL.

Эта статья посвящена трём аспектам, которые отличают демонстрационный стенд от настоящей продакшн-системы: резервному копированию, восстановлению и пулингу соединений.

Postgres на Kubernetes: дискуссия, не утихающая в 2026 году

Несмотря на то как далеко продвинулась экосистема, я по-прежнему вижу знакомый паттерн в архитектурных обсуждениях: стоит кому-то предложить запустить Postgres на Kubernetes — и в комнате повисает пауза.

Идею обычно отметают сразу: «слишком рискованно» или «не стоит таких сложностей».

И надо признать, раньше так оно и было. Но сегодня, с такими операторами (Kubernetes Operator), как CloudNativePG:

  • Переключение при отказе (failover) автоматизировано

  • Репликация встроена

  • Резервное копирование и восстановление описываются декларативно

  • Средства наблюдаемости (observability) интегрированы нативно

Настоящий вопрос уже не в том, способен ли Kubernetes запускать Postgres. Вопрос в том, готовы ли команды взять на себя операционную ответственность, которая с этим связана.

Ведь в отличие от управляемых сервисов, здесь ничего не абстрагировано.

Управляемый Postgres против CNPG

Давайте честно оценим компромиссы.

Ключевое различие:

Управляемый Postgres оптимизирован для удобства. CNPG оптимизирован для контроля.

Что изменилось в CNPG v1.28

Если вы смотрели на CNPG несколько версий назад, механизм резервного копирования теперь работает иначе.

Начиная примерно с v1.26 и окончательно в v1.28:

  • Встроенная логика резервного копирования удалена из основного оператора

  • Всё перенесено в систему плагинов (CNPG-I)

Это не мелкое изменение — оно принципиально меняет подход к проектированию резервных копий.

В v1.28:

  • Плагин Barman Cloud — рекомендуемый способ резервного копирования в объектное хранилище

  • Снимки томов (Volume Snapshots) поддерживаются как Kubernetes-нативная альтернатива

  • Полное восстановление и восстановление на момент времени (Point-In-Time Recovery, PITR) — полноценные возможности первого класса

  • Пулинг соединений через PgBouncer встроен

Резервная копия бесполезна, если по ней нельзя восстановиться

Представьте реальную ситуацию. Пятница, послеобеденное время:

  • 14:05 — запускается миграция

  • 14:12 — вы обнаруживаете, что она удалила данные

Выходные оказались под угрозой. Теперь важен только один вопрос:

Можно ли вернуться к состоянию на 14:04?

В CNPG v1.28 резервные копии явные и плагин-ориентированные. Большинство конфигураций опираются на плагин Barman Cloud:

  1. Установка CRD плагина Barman Cloud:

helm repo add cloudnative-pg https://cloudnative-pg.io/charts/
helm install barman-cloud-plugin cloudnative-pg/plugin-barman-cloud
  1. Определение объектного хранилища:

apiVersion: v1
kind: Secret
metadata:
  name: my-object-store-secret
data:
  ACCESS_KEY_ID: <your-base64-encoded-access-key>
  ACCESS_SECRET_KEY: <your-base64-encoded-secret-access-key>
apiVersion: barmancloud.cnpg.io/v1
kind: ObjectStore
metadata:
  name: my-object-store
spec:
  retentionPolicy: "14d"
  configuration:
    endpointURL: https://s3.amazonaws.com
    destinationPath: "s3://my-bucket/postgres/"
    s3Credentials:
      accessKeyId:
        name: my-object-store-secret
        key: ACCESS_KEY_ID
      secretAccessKey:
        name: my-object-store-secret
        key: ACCESS_SECRET_KEY
    wal:
      compression: gzip
  1. Привязка хранилища к кластеру:

apiVersion: postgresql.cnpg.io/v1
kind: Cluster
...
spec:
  plugins:
    - enabled: true
      name: barman-cloud.cloudnative-pg.io
      isWALArchiver: true
      parameters:
        barmanObjectName: my-object-store # ссылается на ресурс ObjectStore
  1. Создание резервной копии (в данном случае — по расписанию, ScheduledBackup):

apiVersion: postgresql.cnpg.io/v1
kind: ScheduledBackup
metadata:
  name: my-cluster-backup
spec:
  immediate: true
  suspend: false
  schedule: "0 0 */1 * * *"  # Обратите внимание на дополнительное поле секунд
  backupOwnerReference: self
  cluster:
    name: my-cluster  # ссылается на ресурс Cluster
  target: "primary"
  method: plugin
  pluginConfiguration:
    name: barman-cloud.cloudnative-pg.io

Конфигурация выглядит просто. Но суть не в ней.

Базовая резервная копия (base backup) дотащит вас лишь до определённой точки. Архивирование журнала упреждающей записи (Write-Ahead Log, WAL) — вот что позволяет добраться именно до нужного момента. Без WAL-архивирования вы восстановитесь в прошлое и смиритесь с потерей данных.

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

Снимки томов (VolumeSnapshots) удобны для быстрого отката, но не заменяют восстановление на основе WAL. Они решают вопрос скорости, а не точности.

Главный вывод:

Резервные копии вас не спасут — вас спасёт возможность восстановления.

Восстановление: единственное, что по-настоящему важно

Та же ситуация:

  • Последняя резервная копия — 13:00

  • Сбой — 14:05

Без WAL вы теряете больше часа данных. С WAL — восстанавливаетесь до нескольких секунд до инцидента.

В CNPG восстановление — это тот же PostgreSQL под капотом, но описанный декларативно:

apiVersion: postgresql.cnpg.io/v1
kind: Cluster
...
spec:
  bootstrap:
    recovery:
      source: my-object-store
      recoveryTarget:
        targetTime: "2026-03-20 14:04:59"

Выглядит просто. Но это обманчиво.

Важно другое — работает ли это на самом деле:

  • WAL полон и непрерывен

  • Хранилище согласованно

  • Время восстановления известно заранее

Большинство команд не знают ответа ни на один из этих вопросов — до тех пор, пока он им не понадобится.

Резервные копии — это контрольные точки. WAL — временна́я шкала между ними.

Пулинг соединений: проблема, которую вы не замечаете

Не все сбои очевидны. Некоторые просто ощущаются как «что-то тормозит».

Kubernetes усугубляет ситуацию. По мере масштабирования приложения вместе с ним растёт и количество соединений. А PostgreSQL плохо справляется с таким ростом.

Ничего не падает — всё просто деградирует.

CNPG решает эту проблему через PgBouncer:

apiVersion: postgresql.cnpg.io/v1
kind: Pooler
spec:
  cluster:
    name: my-cluster
  instances: 2
  pgbouncer:
    poolMode: session

PgBouncer встаёт перед базой данных, повторно использует соединения и сглаживает пиковые нагрузки.

Выбор режима пулинга — это компромисс:

  • session — безопаснее, сохраняет состояние сессии.

  • transaction — эффективнее, но может ломать операции, зависящие от состояния сессии.

Итог

На практике всё это накатывает одновременно.

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

Если оба механизма настроены — восстановление происходит быстро, и система остаётся стабильной.

Запустить PostgreSQL на Kubernetes уже не самое сложное. Сложнее — спроектировать систему так, чтобы она выдерживала отказы.

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

В следующей статье мы рассмотрим, как все эти части складываются в единую картину, — разберём архитектурные паттерны CNPG: маршрутизацию сервисов, поведение при переключении отказа и развёртывание в нескольких зонах доступности:

© 2026 meganuke