Оригинал: 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, настраиваем логическую репликацию между ними и убеждаемся, что данные синхронизируются.
Требования к подходу:
- Повторяемость — процесс должен надёжно воспроизводиться.
- Тестируемость — разработчики и QA должны иметь возможность прогнать апгрейд до прода.
- Production-ready — миграция «с нуля» с минимальным downtime.
Два пути major-апгрейда
Классические варианты major-апгрейда PostgreSQL:
- Offline in-place —
pg_upgradeконвертирует data-файлы старой версии в формат новой. Требует простоя приложения. - Online через logical replication — данные льются параллельно из старой БД в новую, переключение на новую версию без долгого окна.
CloudNativePG 1.25 пока не поддерживает pg_upgrade (это запланировано на 1.26), но отлично умеет второй сценарий.
Что нужно
CloudNativePG 1.25.0:
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:
apiVersion: postgresql.cnpg.io/v1
kind: Cluster
metadata:
name: pg-15
spec:
instances: 3
storage:
size: 1GiЗаливаем тестовые данные
pgbench --initialize через плагин:
kubectl cnpg pgbench \
--db-name app \
--job-name pgbench-init \
pg-15 \
-- --initializeПолучаем порядка 22 МБ данных в стандартных таблицах pgbench:
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:
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:
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:
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Проверяем привилегии:
kubectl cnpg psql pg-15 -- app -c '\du app'В выводе у роли app должен быть атрибут Replication.
Настраиваем subscriber
Создаём Subscription на стороне pg-17, указывая на externalCluster pg-15 и публикацию publisher:
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 должно появиться сообщение вида:
logical replication apply worker for subscription "subscriber" has startedПроверяем, что данные действительно приехали:
kubectl cnpg psql pg-17 -- app -qAt -c 'SELECT count(*) FROM pgbench_accounts'Получаем 100000 — подписка работает, данные синхронизированы.
Cutover и финал
Дальше — стандартный сценарий cutover (см. Рецепт 5):
- Останавливаем запись в
pg-15. - Дожидаемся, пока подписка догонит источник.
- Синхронизируем sequences — это единственный кусок, который logical replication не реплицирует автоматически:BASH
kubectl cnpg subscription sync-sequences \ --subscription subscriber pg-17 - Раскатываем приложение на сервис
pg-17-rw. - Удаляем объекты
SubscriptionиPublication(они сreclaimPolicy: deleteснимут и физические объекты в PostgreSQL).
Выводы
- Декларативные
PublicationиSubscriptionсильно упрощают управление logical replication в Kubernetes. - Sequences по-прежнему придётся синхронизировать отдельно — это ограничение самого PostgreSQL.
Subscriptionработает и с источниками за пределами CloudNativePG — то есть подойдёт не только для major-апгрейдов, но и для миграций из RDS, виртуалок и т. д. Тот же подход см. в Рецепте 5.- Процесс повторяемый, тестируемый и production-ready.