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

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

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

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

Операции

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

УровеньНазначениеНастраивается через
Архивная обвязка (archive plumbing) (чарт, устаревший вариант)Формирует spec.backup.barmanObjectStore в ресурсе Cluster (cnpg.io) начиная с момента установки через helm. CNPG запускает postgres с archive_command=barman-cloud-wal-archive, каждое переключение WAL отправляется в объектное хранилище, и любой драйвер резервного копирования получает полную цепочку WAL, с которой можно начать воспроизведение. Формируется только когда backup.enabled=true, useSystemBucket=false, destinationPath не пуст И предоставлены встроенные или внешние учётные данные. Пропускается в платформенном режиме useSystemBucket=true — драйвер резервного копирования CNPG применяет barmanObjectStore к работающему Cluster через SSA-патч в момент запуска первого BackupJob; до запуска этого первого BackupJob archive_command неактивен, и 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 в рамках одного тенанта. Драйвер объединяет собственную структуру barmanObjectStore поверх структуры чарта через SSA с ForceOwnership (благодаря чему настройки сжатия / serverName / target берутся из шаблона стратегии); значения, управляемые чартом, указывают на то же место назначения, поэтому конфликта за владение работающим Cluster не возникает.strategy.backups.cozystack.io/CNPG + BackupClass + Plan (повторяющееся) или BackupJob (разовое), восстановление через RestoreJob
Устаревшее плановое резервное копирование, формируемое чартомЧарт также может напрямую формировать cnpg.io/ScheduledBackup. Заменён связкой BackupClass + Plan, сохранён для кластеров, которые не были перенесены. Выключено по умолчанию — формируется только когда и backup.enabled, и backup.schedule непусты. При backup.schedule="" архивная обвязка всё равно работает; не активно только плановое резервное копирование, формируемое чартом.backup.enabled=true плюс backup.schedule (cron CNPG из 6 полей, например "0 2 * * * *")

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

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

spec:
  backup:
    enabled: true
    useSystemBucket: true        # платформа проецирует cozy-backups-creds + драйвер применяет barmanObjectStore через SSA-патч

Устаревший поток, управляемый чартом — для кластеров, созданных до появления платформенного BackupClass, или использующих специально настроенный нестандартный bucket. Укажите все параметры доступа к S3 встроенно (или через s3CredentialsSecret.name); barmanObjectStore формируется начиная с момента установки через helm.

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 — это настройка postmaster: CNPG может менять archive_command во время работы через SIGHUP, но любой WAL, закрытый до такой замены, был «заархивирован» командой archive_command=/bin/true и потерян безвозвратно. В резервной копии, снятой с такого кластера, отсутствует WAL, на который указывает её begin_wal, и последующее восстановление завершается ошибкой WAL not found. Если позволить чарту инициализировать archive_command с момента установки через helm, эта гонка устраняется.

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

Сквозные манифесты находятся в examples/backups/postgres/. Вкратце, ключевые составные части:

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

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

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

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

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

## @param backup.enabled Включить archive_command + формировать управляемый чартом 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 целевого приложения снимком спецификации источника, сохранённым в Backup.status.underlyingResources, поэтому post-install init-job чарта не удаляет восстановленные роли или базы данных.

Покрытие e2e: CI-тест e2e (hack/e2e-apps/backup-postgres.bats) проверяет восстановление в копию в пределах одного пространства имён (шаги 0–7), при котором источник остаётся работающим и поэтому даёт детерминированный сквозной сигнал. Межтенантный вариант (целевой Postgres в тенанте, отличном от тенанта seaweedfs источника) остаётся ручным сценарием / упражнением на dev-кластере — доступность блокируется политикой egress 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.backup.barmanObjectStore остаётся НЕ заданным в формируемом чартом Cluster — драйвер BackupClass cozy-default применяет destinationPath/endpointURL/учётные данные к работающему Cluster через SSA-патч при запуске первого BackupJob. Следствие: archive_command НЕ активен до запуска этого первого 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.backup.barmanObjectStore формируется, когда backup.enabled=true И useSystemBucket=false И destinationPath непуст И предоставлены встроенные или внешние учётные данные; в платформенном потоке useSystemBucket=true чарт не формирует barmanObjectStore, а драйвер CNPG применяет его к работающему Cluster через SSA-патч в момент запуска первого BackupJob.string""
backup.retentionPolicyПолитика хранения (например, “30d”).string30d
backup.destinationPathУСТАРЕЛО. Индивидуальная для тенанта конфигурация S3 заменена управляемым платформой BackupClass cozy-default и системным bucket-ом cozy-backups. Для новых установок оставьте пустым; драйвер BackupClass подхватывает управляемые системой параметры. Сохранено для совместимости при обновлении на месте.strings3://bucket/path/to/folder/
backup.endpointURLУСТАРЕЛО. См. destinationPath.stringhttp://minio-gateway-service:9000
backup.s3AccessKeyУСТАРЕЛО. Тенанты больше не предоставляют ключи S3; системный Secret bucket-а проецируется в пространство имён тенанта контроллером резервного копирования. Игнорируется, когда задан 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, содержащий идентификатор ключа доступа (access key ID). По умолчанию AWS_ACCESS_KEY_ID.string""
backup.s3CredentialsSecret.secretAccessKeyKeyКлюч в Secret, содержащий секретный ключ доступа (secret access key). По умолчанию AWS_SECRET_ACCESS_KEY.string""
backup.endpointCAУСТАРЕЛО. Существующий Secret с CA-бандлом, которому Barman должен доверять при обращении к самоподписанному эндпоинту S3. Используется как для резервного копирования, так и для восстановления (bootstrap) в устаревшем потоке, управляемом чартом.object{}
backup.endpointCA.nameИмя Secret в пространстве имён приложения. Пусто означает, что endpointCA не формируется (Barman использует системное хранилище доверенных сертификатов).string""
backup.endpointCA.keyКлюч в Secret, содержащий CA-бандл. По умолчанию ca.crt.string""

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

ИмяОписаниеТипЗначение
bootstrapКонфигурация начальной загрузки.object{}
bootstrap.enabledВосстанавливать ли из резервной копии.boolfalse
bootstrap.recoveryTimeМетка времени (RFC3339) для восстановления на момент времени; пусто означает последнюю.string""
bootstrap.oldNameПредыдущее имя кластера до удаления.string""
bootstrap.serverNameИмя сервера Barman (префикс пути S3), использовавшееся исходным кластером при записи резервных копий. Задавайте это только если у исходного кластера был явный barmanObjectStore.serverName, отличавшийся от имени его ресурса Kubernetes.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