Управляемый сервис PostgreSQL

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

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

Этот управляемый сервис контролируется оператором CloudNativePG, обеспечивающим эффективное управление и бесперебойную работу.

Операции

Резервное копирование PostgreSQL имеет два уровня, и в рекомендуемой конфигурации используются оба уровня вместе, а не один из них:

СлойЧто делаетНастраивается через
Архивная инфраструктура (чарт, устаревший вариант)Формирует spec.plugins (со ссылкой на barman-cloud ObjectStore) в ресурсе Cluster cnpg.io начиная с helm-install. CNPG запускает плагин barman-cloud как сайдкар-архиватор WAL: каждое переключение WAL отправляется в объектное хранилище, и любой драйвер резервного копирования получает полную цепочку WAL для начала воспроизведения. Формируется только когда backup.enabled=true, useSystemBucket=false, destinationPath не пусто И заданы встроенные или внешние учётные данные. Пропускается в платформенном режиме useSystemBucket=true — драйвер резервного копирования CNPG применяет ObjectStore через SSA и патчит spec.plugins на живом Cluster в момент первого BackupJob; до этого момента архивирование WAL через плагин не активно, и WAL накапливается на PVC. Запустите разовый BackupJob сразу после включения флага на существующих релизах.Устаревший вариант: backup.enabled=true плюс backup.destinationPath, backup.endpointURL, и либо backup.s3AccessKey+backup.s3SecretKey, либо backup.s3CredentialsSecret. Платформенный: backup.enabled=true плюс backup.useSystemBucket=true — без полей S3.
Оркестрация резервного копирования (рекомендуется)Управляет разовыми и плановыми резервными копиями, политикой хранения и восстановлениями на основе ресурсов backups.cozystack.io, которые могут охватывать несколько приложений Postgres в тенанте. Драйвер применяет собственный barman-cloud ObjectStore через SSA и патчит spec.plugins с ForceOwnership (так что настройки сжатия/цели берутся из шаблона стратегии, при этом serverName живого Cluster — его префикс S3 для архива WAL — всегда сохраняется); значения, управляемые чартом, покрывают тот же пункт назначения, поэтому конфликта на живом Cluster не возникает.strategy.backups.cozystack.io/CNPG + BackupClass + Plan (регулярное) или BackupJob (разовое), восстановление через RestoreJob
Устаревшее плановое резервное копирование, формируемое чартомЧарт также может напрямую формировать cnpg.io/ScheduledBackup. Заменён путём BackupClass + Plan, оставлен для кластеров, которые не были мигрированы. Отключён по умолчанию — формируется только когда заданы одновременно backup.enabled и backup.schedule. При backup.schedule="" архивирование WAL через плагин продолжает работать; отключается только формируемое чартом плановое резервное копирование.backup.enabled=true плюс backup.schedule (cron в формате CNPG из 6 полей, например "0 2 * * * *")

Канонический вариант настройки зависит от того, поставляется ли в кластере платформенный BackupClass cozy-default.

Платформенный вариант (рекомендуется для новых кластеров) — включается через backup.useSystemBucket: true со ссылкой на cozy-default в BackupJob/Plan/RestoreJob. Чарт оставляет координаты S3 пустыми; драйвер CNPG применяет barman-cloud ObjectStore через SSA и патчит spec.plugins координатами управляемого платформой бакета в момент первого BackupJob.

spec:
  backup:
    enabled: true
    useSystemBucket: true        # platform projects cozy-backups-creds + driver SSA-applies ObjectStore + patches spec.plugins

Устаревший вариант, управляемый чартом — для кластеров, созданных до появления платформенного BackupClass, или использующих настроенный нестандартный бакет. Укажите все координаты S3 напрямую (или через s3CredentialsSecret.name); spec.plugins и barman-cloud ObjectStore формируются начиная с helm-install.

spec:
  backup:
    enabled: true                # архивировать WAL в объектное хранилище
    destinationPath: s3://my-bucket/pg-src/
    endpointURL: https://seaweedfs-s3.tenant-foo:8333
    s3CredentialsSecret:
      name: pg-src-cnpg-backup-creds
    endpointCA:
      name: pg-src-cnpg-backup-ca
    # backup.schedule намеренно оставлен пустым — BackupClass / Plan
    # ниже управляют повторяющимся резервным копированием; никакой
    # формируемый чартом ScheduledBackup не должен конкурировать с этим расписанием.

в паре со strategy.backups.cozystack.io/CNPG + BackupClass + Plan в том же тенанте. Сквозной e2e-фикстур в examples/backups/postgres/ служит каноническим примером (05-postgres-src.yaml показывает сторону чарта, 10-cnpg-strategy.yaml и 15-backupclass.yaml показывают сторону оркестрации, 25-backupjob-adhoc.yaml и 40-restorejob-to-copy.yaml показывают разовое резервное копирование и восстановление).

Почему нужны оба уровня, а не один? Архивирование WAL выполняется сайдкар-архиватором плагина barman-cloud — CNPG может подключать или отключать плагин во время работы, но любой WAL, закрытый до начала архивирования плагином, теряется безвозвратно. В резервной копии, снятой с такого кластера, отсутствует WAL, на который указывает её begin_wal, и последующее восстановление завершается ошибкой WAL not found. Если чарт подключает плагин с момента helm-install, эта гонка исключается.

Как включить резервное копирование (предпочтительно: BackupClass + Plan)

Сквозные манифесты находятся в examples/backups/postgres/. Кратко, задействованные компоненты:

  1. strategy.backups.cozystack.io/CNPG описывает целевой бакет и шаблонизирует barman-cloud ObjectStore (включая ссылку на Secret с учётными данными S3 — эти учётные данные никогда не появляются в .spec CR Postgres; см. примечание о безопасности ниже).
  2. backups.cozystack.io/BackupClass называет стратегию и выбирается через applicationRef, соответствующий Kind/Name приложения Postgres.
  3. backups.cozystack.io/Plan (регулярный) или BackupJob (разовый), ссылающийся на BackupClass. Контроллер материализует артефакт Backup, когда завершается Backup cnpg.io; восстановления затем ссылаются на этот Backup через RestoreJob.

Поддерживаются как восстановление на месте (перезапись данных исходного приложения), так и восстановление в копию (восстановление в отдельное целевое приложение Postgres в том же пространстве имён) через поле RestoreJob.spec.targetApplicationRef.

Безопасность: при использовании пути BackupClass учётные данные S3 хранятся в доступном тенанту Secret, на который ссылается шаблон стратегии. Драйвер CNPG передаёт эту ссылку на Secret в поле spec.backup.s3CredentialsSecret приложения Postgres при восстановлении, поэтому ключи доступа никогда не попадают в .spec CR Postgres, объектное хранилище etcd или вывод kubectl get -o yaml. Предпочитайте этот путь управляемому чартом варианту, когда это возможно.

Как включить управляемые чартом плановые резервные копии (устаревший вариант)

Чарт также может напрямую формировать cnpg.io/ScheduledBackup без BackupClass. Заменён путём BackupClass + Plan выше и оставлен для кластеров, которые не были мигрированы. По умолчанию не работает — backup.schedule по умолчанию является пустой строкой, что отключает шаблон ScheduledBackup чарта. Чтобы включить, укажите cron-выражение CNPG из 6 полей:

## @param backup.enabled Включить архивирование WAL через плагин + сформировать управляемый чартом ScheduledBackup
## @param backup.schedule Cron-расписание (формат CNPG из 6 полей). Пусто означает отсутствие управляемого чартом ScheduledBackup
## @param backup.retentionPolicy Политика хранения
## @param backup.destinationPath Путь для хранения резервной копии (например, s3://bucket/path/to/folder)
## @param backup.endpointURL Конечная точка S3, используемая для загрузки данных в облако
## @param backup.s3AccessKey Ключ доступа для S3, используемый для аутентификации
## @param backup.s3SecretKey Секретный ключ для S3, используемый для аутентификации
backup:
  enabled: true
  retentionPolicy: 30d
  destinationPath: s3://bucket/path/to/folder/
  endpointURL: http://minio-gateway-service:9000
  schedule: "0 2 * * * *"  # включение — пусто (по умолчанию) означает отсутствие управляемого чартом расписания
  s3AccessKey: oobaiRus9pah8PhohL1ThaeTa4UVa7gu
  s3SecretKey: ju3eum4dekeich9ahM1te8waeGai0oog

Как восстановить из резервной копии (предпочтительно: RestoreJob)

Для резервных копий, управляемых через BackupClass, создайте backups.cozystack.io/RestoreJob, ссылающийся на нужный Backup. См. examples/backups/postgres/35-restorejob-in-place.yaml и examples/backups/postgres/40-restorejob-to-copy.yaml. При восстановлении в копию контроллер заменяет spec.databases и spec.users целевого приложения снимком исходного spec, сохранённым в Backup.status.underlyingResources, так что post-install init-job чарта не теряет восстановленные роли или базы данных.

Покрытие e2e: восстановление в копию в том же пространстве имён (шаги 0-7) оставляет исходное приложение работающим и служит детерминированным сквозным сигналом; остаётся ручным / dev-cluster эталонным сценарием, не входящим в автоматизированный набор e2e-тестов. Кросс-тенантный вариант (целевой Postgres в другом тенанте, отличном от seaweedfs источника) остаётся ручным / dev-cluster упражнением — доступность заблокирована политикой исходящего трафика Cilium на уровне тенанта. Кодовый путь восстановления на месте поставляется и покрыт на уровне модульных тестов TestClusterHasRecoveryBootstrap_TerminatingCluster, TestCNPGBackupWALArchived, TestCNPGPurgeNeeded и остальными тестами набора internal/backupcontroller/cnpgstrategy_controller_test.go.

Как восстановить резервную копию (управляемая чартом начальная загрузка)

CloudNativePG поддерживает восстановление на момент времени (point-in-time-recovery). Восстановление резервной копии выполняется путём создания нового экземпляра базы данных и восстановления в него данных.

Создайте новое приложение PostgreSQL с другим именем, но идентичной конфигурацией. Установите bootstrap.enabled в true и укажите имя экземпляра базы данных, из которого нужно восстановиться, и время восстановления:

## @param bootstrap.enabled Восстановить кластер базы данных из резервной копии
## @param bootstrap.recoveryTime Метка времени (PITR), до которой будет выполнено восстановление, в формате RFC 3339. Если оставить пустым, будет восстановлена последняя
## @param bootstrap.oldName Имя кластера базы данных до удаления
##
bootstrap:
  enabled: false
  recoveryTime: ""  # оставьте пустым для последней или укажите точную метку времени; пример: 2020-11-26 15:22:00.00000+00
  oldName: "<previous-postgres-instance>"

Как переключить первичную/вторичную реплику

См.:

storageClass помечен как неизменяемый (immutable) в схеме чарта — см. docs/storage-immutability.md, где описан этот контракт и какие потребители его обеспечивают.

TLS для серверных подключений

CNPG управляет цепочкой сертификатов сквозным образом. Оператор автоматически генерирует самоподписанный CA, подписывает им конечные (leaf) сертификаты сервера, клиента и репликации и ротирует их по мере необходимости. Чарт не формирует никаких объектов Issuer/Certificate cert-manager — этот путь взаимоисключающий с управляемой оператором цепочкой на admission webhook CNPG.

Что добавляет чарт: когда TLS включён и external: true, чарт устанавливает spec.certificates.serverAltDNSNames в ресурсе CNPG Cluster CR, чтобы внедрить внешнее имя хоста <release>.<_namespace.host> в список SAN автоматически сгенерированного серверного сертификата. Стандартное покрытие SAN в CNPG уже включает три встроенных сервиса ClusterIP (-rw, -r, -ro) во всех четырёх формах DNS (<svc>, <svc>.<ns>, <svc>.<ns>.svc, <svc>.<ns>.svc.<cluster-domain>); требуется добавить только внешнее имя хоста.

Трёхпозиционный параметр tls.enabled определяет, внедряет ли чарт serverAltDNSNames:

  • tls.enabled: null (по умолчанию) — режим TLS наследуется от external. При external: true чарт внедряет внешнее имя хоста в управляемый оператором сертификат.
  • tls.enabled: true при external: true — эффект тот же, что и по умолчанию.
  • tls.enabled: true при external: false — внедрение serverAltDNSNames не требуется (внешнего имени хоста для добавления нет); автоматически сгенерированный сертификат CNPG покрывает внутренние сервисы.
  • tls.enabled: false — чарт пропускает внедрение serverAltDNSNames. Примечание: CNPG сохраняет встроенный TLS на уровне соединения независимо от этого флага; данный переключатель управляет лишь тем, добавляется ли внешнее имя хоста в сертификат. Чтобы полностью отключить TLS в PostgreSQL, потребовалось бы установить postgresql.parameters.ssl = "off" на уровне CNPG, что выходит за рамки этого флага.

Получение CA-бандла для проверки на стороне клиента:

CNPG включает сертификат CA в каждый создаваемый им Secret с учётными данными пользователя под ключом ca.crt. Получите его из Secret <release>-credentials, который уже доступен тенантам через RBAC дашборда:

kubectl --context <ctx> --namespace <tenant> \
  get secret <release>-credentials \
  --output jsonpath='{.data.ca\.crt}' | base64 --decode

Подключение с полной проверкой (пример psql):

psql "host=<host> port=5432 dbname=app user=app \
  sslmode=verify-full sslrootcert=ca.crt"

Чтобы sslmode=verify-full работал, полученный выше CA-бандл должен быть сохранён в ca.crt. Без него используйте sslmode=require (шифрует, но не проверяет серверный сертификат).

Параметры

Общие параметры

ИмяОписаниеТипЗначение
replicasКоличество реплик Postgres.int2
resourcesЯвная конфигурация CPU и памяти для каждой реплики PostgreSQL. Если не задано, применяется пресет, указанный в resourcesPreset.object{}
resources.cpuCPU, доступный каждой реплике.quantity""
resources.memoryПамять (RAM), доступная каждой реплике.quantity""
resourcesPresetПресет размера по умолчанию, используемый, когда resources не задан.stringt1.micro
sizeРазмер Persistent Volume Claim, доступный для данных приложения.quantity10Gi
storageClassStorageClass, используемый для хранения данных.string""
externalВключить внешний доступ извне кластера.boolfalse
versionМажорная версия PostgreSQL для развёртыванияstringv18

Конфигурация TLS

ИмяОписаниеТипЗначение
tlsКонфигурация TLS для серверных подключений.object{}
tls.enabledТрёхпозиционный переключатель, определяющий, внедряет ли чарт внешнее имя хоста в управляемый оператором сертификат CNPG через spec.certificates.serverAltDNSNames. Если не задан, чарт внедряет SAN при external: true и пропускает в противном случае. Установите явно в true, чтобы внедрять независимо от external (не имеет эффекта при external: false, так как внешнего имени хоста для добавления нет). Установите в false, чтобы пропустить внедрение. Учтите, что CNPG сохраняет встроенный TLS на уровне соединения независимо от этого флага — данный переключатель управляет лишь внедрением SAN на стороне чарта; чтобы полностью отключить TLS в PostgreSQL, установите postgresql.parameters.ssl = "off" на уровне CNPG.*boolnull

Параметры, специфичные для приложения

ИмяОписаниеТипЗначение
postgresqlКонфигурация сервера PostgreSQL.object{}
postgresql.parametersПараметры сервера PostgreSQL. Значения могут быть строками или целыми числами; целые числа приводятся к строкам шаблоном (например, принимаются и max_connections: 100, и max_connections: "100"). ЗАБЛОКИРОВАНЫ (позволяют выполнение произвольного кода): archive_command, restore_command, ssl_passphrase_command, archive_cleanup_command, recovery_end_command, dynamic_library_path, local_preload_libraries, session_preload_libraries, shared_preload_libraries. НЕ переопределяйте параметры, управляемые CloudNativePG: archive_mode, primary_conninfo, wal_level, max_replication_slots.map[string]intOrString{}

Синхронная репликация на основе кворума

ИмяОписаниеТипЗначение
quorumКонфигурация кворума для синхронной репликации.object{}
quorum.minSyncReplicasМинимальное число синхронных реплик, необходимое для фиксации (commit).int0
quorum.maxSyncReplicasМаксимально допустимое число синхронных реплик (должно быть меньше общего числа реплик).int0

Конфигурация пользователей

ИмяОписаниеТипЗначение
usersКарта конфигурации пользователей.map[string]object{}
users[name].passwordПароль пользователя.string""
users[name].replicationИмеет ли пользователь привилегии репликации.boolfalse

Конфигурация баз данных

ИмяОписаниеТипЗначение
databasesКарта конфигурации баз данных.map[string]object{}
databases[name].rolesРоли, назначенные пользователям.object{}
databases[name].roles.adminСписок пользователей с правами администратора.[]string[]
databases[name].roles.readonlyСписок пользователей с правами только на чтение.[]string[]
databases[name].extensionsСписок включённых расширений PostgreSQL.[]string[]

Параметры резервного копирования

ИмяОписаниеТипЗначение
backupКонфигурация резервного копирования.object{}
backup.enabledВключить регулярное резервное копирование.boolfalse
backup.useSystemBucketОпциональная функция: при true пропускается формируемый чартом Secret <release>-s3-creds, И spec.plugins (а также barman-cloud ObjectStore) остаётся НЕ ЗАДАННЫМ в формируемом чартом Cluster — драйвер BackupClass cozy-default применяет ObjectStore через SSA (с destinationPath/endpointURL/учётными данными) и патчит spec.plugins на живом Cluster при запуске первого BackupJob. Следствие: архивирование WAL через плагин НЕ активно до момента срабатывания этого первого BackupJob; тем временем WAL накапливается на PVC, поэтому запустите разовый BackupJob сразу после включения флага на существующих релизах. Используйте вместе с платформенным BackupClass cozy-default — тенантам не нужно заполнять s3AccessKey/s3SecretKey или destinationPath/endpointURL. Путь назначения автоматически ограничивается s3://cozy-backups/<namespace>/<release>/.boolfalse
backup.scheduleУстаревшее. Cron-расписание (формат CNPG из 6 полей) для формируемого чартом ScheduledBackup. Пусто означает отсутствие управляемого чартом расписания, что является рекомендуемой настройкой, когда BackupClass из backups.cozystack.io уже управляет оркестрацией резервного копирования. В устаревшем управляемом чартом варианте spec.plugins вместе с barman-cloud ObjectStore формируется, когда backup.enabled=true И useSystemBucket=false И destinationPath не пусто И заданы встроенные или внешние учётные данные; в платформенном варианте useSystemBucket=true чарт пропускает формирование spec.plugins, а драйвер CNPG применяет ObjectStore через SSA и патчит spec.plugins на живом Cluster в момент первого BackupJob.string""
backup.retentionPolicyПолитика хранения (например, “30d”).string30d
backup.destinationPathУСТАРЕЛО. Настройка S3 на уровне тенанта заменена управляемым платформой BackupClass cozy-default и системным бакетом cozy-backups. Оставьте пустым для новых установок; драйвер BackupClass использует управляемые системой координаты. Сохранено для совместимости при обновлении на месте.strings3://bucket/path/to/folder/
backup.endpointURLУСТАРЕЛО. См. destinationPath.stringhttp://minio-gateway-service:9000
backup.s3AccessKeyУСТАРЕЛО. Тенанты больше не предоставляют ключи S3; системный Secret с бакетом проецируется в пространство имён тенанта контроллером резервного копирования. Игнорируется, если задан s3CredentialsSecret.name или useSystemBucket равен true. Чарт пропускает материализацию <release>-s3-creds, если это поле пустое, чтобы стандартная установка не раскрывала заглушки учётных данных в пространство имён тенанта.string""
backup.s3SecretKeyУСТАРЕЛО. См. s3AccessKey.string""
backup.s3CredentialsSecretУСТАРЕЛО. Существующий Secret с учётными данными S3. Используйте вместо этого управляемый платформой BackupClass cozy-default. Если задан, чарт ссылается на этот Secret напрямую (устаревший управляемый чартом вариант). Драйвер резервного копирования CNPG записывает это поле при восстановлении, поэтому учётные данные никогда не попадают в .spec CR.object{}
backup.s3CredentialsSecret.nameИмя Secret в пространстве имён приложения. Пусто означает, что чарт материализует <release>-s3-creds из s3AccessKey/s3SecretKey.string""
backup.s3CredentialsSecret.accessKeyIDKeyКлюч в Secret, содержащий идентификатор ключа доступа. По умолчанию AWS_ACCESS_KEY_ID.string""
backup.s3CredentialsSecret.secretAccessKeyKeyКлюч в Secret, содержащий секретный ключ доступа. По умолчанию AWS_SECRET_ACCESS_KEY.string""
backup.endpointCAУСТАРЕЛО. Существующий Secret с пакетом CA, которому должен доверять плагин barman-cloud при обращении к самоподписанной конечной точке S3. Используется как для резервного копирования, так и для восстановления при начальной загрузке в устаревшем управляемом чартом варианте.object{}
backup.endpointCA.nameИмя Secret в пространстве имён приложения. Пусто означает, что endpointCA не формируется (плагин использует системное хранилище доверия).string""
backup.endpointCA.keyКлюч в Secret, содержащий пакет CA. По умолчанию ca.crt.string""

Параметры начальной загрузки (восстановления)

ИмяОписаниеТипЗначение
bootstrapКонфигурация начальной загрузки.object{}
bootstrap.enabledВосстанавливать ли из резервной копии.boolfalse
bootstrap.recoveryTimeМетка времени (RFC3339) для восстановления на момент времени; пусто означает последнюю.string""
bootstrap.oldNameИмя предыдущего кластера до удаления.string""
bootstrap.serverNameИмя сервера (префикс пути S3), использовавшееся исходным кластером при записи резервных копий; передаётся в плагин barman-cloud через externalClusters[].plugin.parameters.serverName. По умолчанию равно bootstrap.oldName. Задавайте только когда исходный кластер писал резервные копии под явным именем сервера, отличающимся от имени его ресурса Kubernetes.string""
bootstrap.newServerNameWAL-archive server name (префикс пути в S3), в который пишет ВОССТАНОВЛЕННЫЙ кластер. Должен отличаться от serverName (источника восстановления), чтобы восстановленный кластер архивировал данные в новый, пустой префикс, и barman-cloud-check-wal-archive проходил проверку, пока идёт восстановление из префикса источника. Драйвер резервного копирования CNPG устанавливает это значение при восстановлении; если оно пустое, кластер архивирует данные под собственным именем.string""

Примеры и справочник по параметрам

resources и resourcesPreset

resources задаёт явные конфигурации CPU и памяти для каждой реплики. Если оставить пустым, применяется пресет, указанный в resourcesPreset.

resources:
  cpu: 4000m
  memory: 4Gi

resourcesPreset задаёт именованные конфигурации CPU и памяти для каждой реплики. Эта настройка игнорируется, если задано соответствующее значение resources.

Пресеты следуют облачной схеме именования <серия>.<размер>. Пять серий покрывают весь диапазон соотношений CPU к памяти (t1 1:0.5, c1 1:1, s1 1:2, u1 1:4, m1 1:8), и каждая серия поставляется с восемью размерами (от nano до 4xlarge). Устаревшие плоские имена (nano, micro, small, medium, large, xlarge, 2xlarge) по-прежнему принимаются как устаревшие псевдонимы соответствующих типов инстансов с соотношением 1:1.

См. docs/operations/resource-presets.md, где приведены полная матрица размеров и сопоставление устаревших имён с типами инстансов.

users

users:
  user1:
    password: strongpassword
  user2:
    password: hackme
  airflow:
    password: qwerty123
  debezium:
    replication: true

databases

databases:
  myapp:
    roles:
      admin:
      - user1
      - debezium
      readonly:
      - user2
  airflow:
    roles:
      admin:
      - airflow
    extensions:
    - hstore