Рецепт 15. Major-апгрейды PostgreSQL онлайн через логическую репликацию

Декларативный online major-апгрейд PostgreSQL 15 → 17 средствами CloudNativePG 1.25: новые ресурсы Publication и Subscription, импорт схемы через bootstrap, проверка синхронизации данных.
Опубликовано:

Оригинал: CNPG Recipe 15 — PostgreSQL Major Online Upgrades with Logical Replication — 11 декабря 2024 (обновлено 23 декабря 2024).

В CloudNativePG 1.25 появились декларативные ресурсы Publication и Subscription для PostgreSQL logical replication. Это позволяет описывать online major-апгрейды (например, PostgreSQL 15 → 17) только через CRD, без императивных шагов плагином cnpg. В этом рецепте — пошаговая инструкция.

Задача: онлайн major-апгрейд PostgreSQL

Поднимаем кластер PostgreSQL 15 под управлением CloudNativePG, заливаем в него тестовые данные, поднимаем рядом кластер PostgreSQL 17, настраиваем логическую репликацию между ними и убеждаемся, что данные синхронизируются.

Требования к подходу:

Два пути major-апгрейда

Классические варианты major-апгрейда PostgreSQL:

  1. Offline in-placepg_upgrade конвертирует data-файлы старой версии в формат новой. Требует простоя приложения.
  2. Online через logical replication — данные льются параллельно из старой БД в новую, переключение на новую версию без долгого окна.

CloudNativePG 1.25 пока не поддерживает pg_upgrade (это запланировано на 1.26), но отлично умеет второй сценарий.

Что нужно

CloudNativePG 1.25.0:

BASH
kubectl apply --server-side -f \
  https://raw.githubusercontent.com/cloudnative-pg/cloudnative-pg/release-1.25/releases/cnpg-1.25.0.yaml
Нажмите, чтобы развернуть и увидеть больше

Плюс свежий плагин cnpg.

Источник: PostgreSQL 15

Минимальный кластер pg-15:

YAML
apiVersion: postgresql.cnpg.io/v1
kind: Cluster
metadata:
  name: pg-15
spec:
  instances: 3
  storage:
    size: 1Gi
Нажмите, чтобы развернуть и увидеть больше

Заливаем тестовые данные

pgbench --initialize через плагин:

BASH
kubectl cnpg pgbench \
  --db-name app \
  --job-name pgbench-init \
  pg-15 \
  -- --initialize
Нажмите, чтобы развернуть и увидеть больше

Получаем порядка 22 МБ данных в стандартных таблицах pgbench:

PLAINTEXT
dropping old tables...
creating tables...
generating data (client-side)...
100000 of 100000 tuples (100%) of pgbench_accounts done
vacuuming...
creating primary keys...
done in 0.16 s
Нажмите, чтобы развернуть и увидеть больше

Приёмник: PostgreSQL 17

Целевой кластер pg-17 с импортом только схемы из pg-15:

YAML
apiVersion: postgresql.cnpg.io/v1
kind: Cluster
metadata:
  name: pg-17
spec:
  instances: 3
  storage:
    size: 1Gi
  bootstrap:
    initdb:
      import:
        type: microservice
        schemaOnly: true
        databases:
          - app
        source:
          externalCluster: pg-15
  externalClusters:
  - name: pg-15
    connectionParameters:
      host: pg-15-rw.default.svc
      user: app
      dbname: app
    password:
      name: pg-15-app
      key: password
Нажмите, чтобы развернуть и увидеть больше

После применения в pg-17 — пустая схема app без данных.

Настраиваем publisher

Включаем replication-привилегии у пользователя app в pg-15 через секцию managed.roles:

YAML
apiVersion: postgresql.cnpg.io/v1
kind: Cluster
metadata:
  name: pg-15
spec:
  instances: 3
  storage:
    size: 1Gi
  managed:
    roles:
      - name: app
        login: true
        replication: true
Нажмите, чтобы развернуть и увидеть больше

Создаём Publication:

YAML
apiVersion: postgresql.cnpg.io/v1
kind: Publication
metadata:
  name: pg-15-publisher
spec:
  cluster:
    name: pg-15
  dbname: app
  name: publisher
  target:
    allTables: true
  publicationReclaimPolicy: delete
Нажмите, чтобы развернуть и увидеть больше

Проверяем привилегии:

BASH
kubectl cnpg psql pg-15 -- app -c '\du app'
Нажмите, чтобы развернуть и увидеть больше

В выводе у роли app должен быть атрибут Replication.

Настраиваем subscriber

Создаём Subscription на стороне pg-17, указывая на externalCluster pg-15 и публикацию publisher:

YAML
apiVersion: postgresql.cnpg.io/v1
kind: Subscription
metadata:
  name: pg-17-subscription
spec:
  cluster:
    name: pg-17
  dbname: app
  name: subscriber
  externalClusterName: pg-15
  publicationName: publisher
  subscriptionReclaimPolicy: delete
Нажмите, чтобы развернуть и увидеть больше

После применения в логах pg-17 должно появиться сообщение вида:

PLAINTEXT
logical replication apply worker for subscription "subscriber" has started
Нажмите, чтобы развернуть и увидеть больше

Проверяем, что данные действительно приехали:

BASH
kubectl cnpg psql pg-17 -- app -qAt -c 'SELECT count(*) FROM pgbench_accounts'
Нажмите, чтобы развернуть и увидеть больше

Получаем 100000 — подписка работает, данные синхронизированы.

Cutover и финал

Дальше — стандартный сценарий cutover (см. Рецепт 5):

  1. Останавливаем запись в pg-15.
  2. Дожидаемся, пока подписка догонит источник.
  3. Синхронизируем sequences — это единственный кусок, который logical replication не реплицирует автоматически:
    BASH
    kubectl cnpg subscription sync-sequences \
      --subscription subscriber pg-17
    Нажмите, чтобы развернуть и увидеть больше
  4. Раскатываем приложение на сервис pg-17-rw.
  5. Удаляем объекты Subscription и Publication (они с reclaimPolicy: delete снимут и физические объекты в PostgreSQL).

Выводы

Начать поиск

Введите ключевые слова для поиска статей

↑↓
ESC
⌘K Горячая клавиша