CloudNativePG: продакшен-PostgreSQL в Kubernetes

Published: 2026-03-23

Раньше PostgreSQL в Kubernetes — это StatefulSet, ручной failover и самописные скрипты бэкапов. CloudNativePG (CNPG) — оператор, который декларативно управляет стриминговой репликацией, автоматическим failover, архивацией WAL, резервными копиями и point-in-time recovery. Весь кластер описывается одним CRD.


Установка оператора

yamlapiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
  name: cloudnative-pg
  namespace: cnpg-system
spec:
  chart:
    spec:
      chart: cloudnative-pg
      version: "0.x"
      sourceRef:
        kind: HelmRepository
        name: cnpg
        namespace: flux-system
  values:
    monitoring:
      podMonitorEnabled: true
      grafanaDashboard:
        create: true
        namespace: monitoring

Определение кластера

yamlapiVersion: postgresql.cnpg.io/v1
kind: Cluster
metadata:
  name: pg-main
  namespace: app
spec:
  instances: 3
  imageName: ghcr.io/cloudnative-pg/postgresql:16

  primaryUpdateStrategy: unsupervised

  storage:
    size: 20Gi
    storageClass: local-path

  resources:
    requests:
      cpu: 500m
      memory: 1Gi
    limits:
      cpu: 2000m
      memory: 2Gi

  postgresql:
    parameters:
      max_connections: "200"
      shared_buffers: "256MB"
      effective_cache_size: "768MB"
      work_mem: "4MB"
      wal_level: logical
    pg_hba:
      - host all all 10.0.0.0/8 scram-sha-256

  bootstrap:
    initdb:
      database: myapp
      owner: myapp
      secret:
        name: pg-main-app-secret

  backup:
    retentionPolicy: "30d"
    barmanObjectStore:
      destinationPath: s3://pg-backups/pg-main
      endpointURL: https://storage.yandexcloud.net
      s3Credentials:
        accessKeyId:
          name: pg-s3-secret
          key: ACCESS_KEY_ID
        secretAccessKey:
          name: pg-s3-secret
          key: SECRET_ACCESS_KEY
      wal:
        compression: gzip
      data:
        compression: gzip

  monitoring:
    enablePodMonitor: true

instances: 3 — это 1 primary + 2 реплики. CNPG управляет стриминговой репликацией автоматически. При падении primary наиболее актуальная реплика повышается до primary примерно за 30 секунд.


Расписание бэкапов

yamlapiVersion: postgresql.cnpg.io/v1
kind: ScheduledBackup
metadata:
  name: pg-main-daily
  namespace: app
spec:
  schedule: "0 2 * * *"
  cluster:
    name: pg-main
  backupOwnerReference: self

CNPG непрерывно отправляет WAL в S3, ежедневный бэкап служит базовым. Вместе они обеспечивают point-in-time recovery с точностью до секунды в пределах окна хранения.


Point-in-time recovery

yamlapiVersion: postgresql.cnpg.io/v1
kind: Cluster
metadata:
  name: pg-main-restored
  namespace: app
spec:
  instances: 1
  bootstrap:
    recovery:
      source: pg-main
      recoveryTarget:
        targetTime: "2026-10-17 14:30:00"
  externalClusters:
    - name: pg-main
      barmanObjectStore:
        destinationPath: s3://pg-backups/pg-main
        endpointURL: https://storage.yandexcloud.net
        s3Credentials:
          accessKeyId:
            name: pg-s3-secret
            key: ACCESS_KEY_ID
          secretAccessKey:
            name: pg-s3-secret
            key: SECRET_ACCESS_KEY

Используйте другое имя кластера, чтобы не конфликтовать с production. CNPG воспроизводит WAL до targetTime.


Пул соединений через PgBouncer

yamlapiVersion: postgresql.cnpg.io/v1
kind: Pooler
metadata:
  name: pg-main-pooler
  namespace: app
spec:
  cluster:
    name: pg-main
  instances: 2
  type: rw
  pgbouncer:
    poolMode: transaction
    parameters:
      max_client_conn: "1000"
      default_pool_size: "25"

type: rw направляет запросы на primary, type: ro — на реплики. Transaction pooling позволяет обслуживать 1000 клиентов при 25 backend-соединениях.


Подключение из приложения

CNPG создаёт сервисы для каждого кластера:

  • pg-main-rw — primary (read-write)
  • pg-main-ro — реплики (read-only)
  • pg-main-r — все экземпляры
postgresql://myapp:password@pg-main-rw.app.svc.cluster.local:5432/myapp

С PgBouncer:

postgresql://myapp:password@pg-main-pooler.app.svc.cluster.local:5432/myapp

Полезные команды

bash# Статус кластера
kubectl cnpg status pg-main -n app

# Список бэкапов
kubectl get backup -n app

# Ручной базовый бэкап
kubectl cnpg backup pg-main -n app

# Промоут реплики (для тестирования failover)
kubectl cnpg promote pg-main pg-main-2 -n app

# Архивация WAL
kubectl cnpg status pg-main -n app | grep -A5 "WAL Archiving"

# Гибернация (остановить поды, сохранить PVC)
kubectl cnpg hibernate pg-main -n app

Диагностика

Ошибки архивации WAL: проверьте учётные данные S3 и права на bucket. Ошибки видны в kubectl cnpg status.

Реплика отстаёт: проверьте pg_replication_slots на primary. Зависшие слоты блокируют очистку WAL и могут переполнить диск. Удаляются через pg_drop_replication_slot.

Failover не произошёл: CNPG требует кворума. При трёх экземплярах primary считается упавшим, если это подтверждают минимум две реплики.