Оригинал: Making the world my single point of failure with PostgreSQL — 27 июня 2024 (обновлено 24 августа 2024).
В CloudNativePG 1.24 появилась прорывная фича — декларативное управление switchover между Kubernetes-кластерами в разных регионах. Без переклонирования бывшего primary.
CloudNativePG, который раньше жил в рамках одного Kubernetes-кластера, теперь поддерживает распределённые топологии PostgreSQL через несколько регионов. Декларативная координация promotion/demotion гарантирует, что primary в каждый момент только один.
Реальный сценарий
Не абстрактный «кластер на трёх континентах», а реалистичный кейс — банк в Европе. Два Kubernetes-кластера: в Центральной Европе и в Западной Европе, каждый по три AZ. У каждого — shared-nothing PostgreSQL. Каждые 3-6 месяцев primary-роль перебрасывается из одного дата-центра в другой — это и есть controlled / planned switchover.
Primary-дата-центр
Stateless-приложения работают рядом с локальным CloudNativePG. Кластер: primary + две hot-standby реплики. Одна реплика — синхронная. Continuous backup + WAL-архив идут в близкий object store. Доступ к приложениям — через DNS, Ingress / LoadBalancer.
DR-дата-центр
Зеркало primary в «симметричной» схеме. Цель — бесшовное переключение с минимумом простоя. Stateless-приложения можно скейлить в 0 в idle-периоды или направлять на DR read-only.
CloudNativePG вводит понятие replica cluster — Postgres-кластер в continuous recovery. В нём одна реплика — designated primary (готова стать primary при switchover), остальные синхронятся через cascading streaming replication.
Designated primary синхронится с источником через continuous backup storage — каждый PostgreSQL-кластер видит чужой object store в read-only. Типичный RPO — 5 минут, без необходимости постоянного streaming-канала между кластерами.
Первичная настройка: Центральная Европа
Primary-кластер pg16-eu-central:
apiVersion: postgresql.cnpg.io/v1
kind: Cluster
metadata:
name: pg16-eu-central
spec:
imageCatalogRef:
apiGroup: postgresql.cnpg.io
kind: ImageCatalog
name: postgresql
major: 16
instances: 3
minSyncReplicas: 1
maxSyncReplicas: 1
storage:
size: 100Gi
walStorage:
size: 50Gi
backup:
barmanObjectStore:
destinationPath: s3://pg16-eu-central/
externalClusters:
- name: pg16-eu-central
barmanObjectStore:
destinationPath: s3://pg16-eu-central/
- name: pg16-eu-western
barmanObjectStore:
destinationPath: s3://pg16-eu-western/
replica:
primary: pg16-eu-central
source: pg16-eu-westernИспользуется новый image catalog для PostgreSQL 16. 3 инстанса с синхронной репликацией кворумом → RPO = 0 локально. PGDATA и WAL разнесены. Локальный object store для continuous backup.
replica.primary указывает, какой кластер сейчас является primary в распределённой топологии. Если metadata.name == replica.primary — этот кластер primary; иначе — replica cluster.
replica.source определяет источник, когда этот кластер перестаёт быть primary.
Best practice — сразу настроить расписание бэкапов:
apiVersion: postgresql.cnpg.io/v1
kind: ScheduledBackup
metadata:
name: pg16-eu-central-backup
spec:
schedule: '0 0 0 * * *'
backupOwnerReference: self
cluster:
name: pg16-eu-central
immediate: trueimmediate: true снимает первый full backup сразу при создании.
Первичная настройка: Западная Европа
DR-кластер pg16-eu-western:
apiVersion: postgresql.cnpg.io/v1
kind: Cluster
metadata:
name: pg16-eu-western
spec:
imageCatalogRef:
apiGroup: postgresql.cnpg.io
kind: ImageCatalog
name: postgresql
major: 16
instances: 3
minSyncReplicas: 1
maxSyncReplicas: 1
storage:
size: 100Gi
walStorage:
size: 50Gi
backup:
barmanObjectStore:
destinationPath: s3://pg16-eu-western/
externalClusters:
- name: pg16-eu-central
barmanObjectStore:
destinationPath: s3://pg16-eu-central/
- name: pg16-eu-western
barmanObjectStore:
destinationPath: s3://pg16-eu-western/
replica:
primary: pg16-eu-central
source: pg16-eu-centralБольшая часть конфига идентична. Отличия — имя кластера, локальный object store и replica.source: pg16-eu-central.
Поскольку replica.primary ≠ metadata.name, CloudNativePG считает этот кластер replica cluster и тянет изменения из WAL-архива в Центральной Европе.
Поток: приложения пишут в Центральную Европу → WAL льётся локально → архивируется в S3 каждые 5 минут или по заполнению → designated primary в Западной Европе подтягивает WAL через restore command → реплеит → архивирует в свой S3. Получается continuous backup на двух регионах, без зависимости от вендора storage.
Demotion бывшего primary
В каждый момент primary только один. Перед сменой — старый должен сложить с себя полномочия.
Меняем replica.primary в pg16-eu-central на pg16-eu-western:
replica:
primary: pg16-eu-western
source: pg16-eu-westernCloudNativePG видит, что pg16-eu-central больше не primary, и понижает его до replica cluster, ожидающего изменений.
Этот шаг = downtime по записи: распределённого primary в этот момент нет.
При demotion CloudNativePG делает две важные операции:
- Архивирует REDO-WAL контрольной точки (включая shutdown-checkpoint) в виде
.partial-файла. - Генерирует
demotionTokenв статусе кластера — base64-JSON изpg_controldata(system identifier, latest checkpoint TimeLineID, REDO location, REDO WAL file, время чекпоинта).
Designated primary в Центральной Европе ждёт нового WAL из S3 Западной Европы. Но Западная Европа пока ещё реплика — нового WAL нет.
Promotion бывшей реплики
Берём токен:
kubectl get cluster pg16-eu-central \
-o jsonpath='{.status.demotionToken}'Посмотреть содержимое:
kubectl get cluster pg16-eu-central \
-o jsonpath='{.status.demotionToken}' | base64 -d | jqОбновляем pg16-eu-western:
replica:
primary: pg16-eu-western
source: pg16-eu-central
promotionToken: <DEMOTION_TOKEN_VALUE>⚠️ Менять primary и promotionToken нужно одновременно. Без токена будет failover, и старый primary потом придётся пересобрать с нуля.
CloudNativePG аккуратно дотягивает designated primary до LSN из токена и только потом делает promotion: меняет timeline, архивирует history-file и новый WAL → разблокирует репликацию в Центральной Европе (теперь она в роли replica).
Расширения
То, что описано — лишь часть возможного. Можно добавить третий кластер и сделать круговую топологию:
externalClusters:
- name: pg16-eu-central
barmanObjectStore:
destinationPath: s3://pg16-eu-central/
- name: pg16-eu-western
barmanObjectStore:
destinationPath: s3://pg16-eu-western/
- name: pg16-eu-south
barmanObjectStore:
destinationPath: s3://pg16-eu-south/replica.source:
- для
pg16-eu-central→pg16-eu-south; - для
pg16-eu-west→pg16-eu-central; - для
pg16-eu-south→pg16-eu-west.
Дополнительно к WAL-архиву можно поднять streaming replication между внешними кластерами — для лучшей устойчивости.
Выводы
CloudNativePG — первый инструмент, способный сделать controlled switchover между ЦОДами полностью декларативно, опираясь только на WAL-архив.
Алгоритм сводится к трём шагам:
- Обновить конфиг текущего primary, понизив его и указав нового primary в распределённой топологии.
- Снять
demotionTokenиз статуса понижённого кластера. - Обновить конфиг новой реплики, повысив её и передав
promotionToken.
Всё ложится в GitOps / IaC. Это решение сразу годится не только для public/private cloud, но и для hybrid/multi-cloud.