Рецепт 5. Миграция PostgreSQL в Kubernetes с ~0 downtime

Как через логическую репликацию и плагин cnpg перевезти PostgreSQL из RDS, виртуалки или другого Kubernetes-оператора в CloudNativePG практически без простоя приложения.
Опубликовано:

О логической репликации

В PostgreSQL 10 появилась встроенная поддержка PUBLICATION и SUBSCRIPTION через логическую репликацию. У неё есть существенные ограничения — PostgreSQL не реплицирует:

Плагин cnpg для CloudNativePG закрывает первые два пункта. Если используете large objects — нужен «офлайн»-подход.

Миграция с почти нулевым простоем

Цель — свести к минимуму время простоя приложения по записи. В индустрии такие миграции называют «~0 cutover time» или flash-cut.

Стратегия — change data capture через непрерывную логическую репликацию из исходной БД в целевую под управлением CloudNativePG. Шаги успешной миграции:

  1. Полностью завершить первичную настройку.
  2. Убедиться, что целевая БД синхронизирована с исходной.
  3. Остановить запись из приложения.
  4. Дождаться, пока очередь изменений будет применена на приёмнике.
  5. Синхронизировать sequences.
  6. Удалить подписку и публикацию.
  7. Раскатить версию приложения, которая ходит в новую БД.

Плюсы подхода:

Первичная настройка миграции

День 0 — самый ответственный этап. Что нужно подготовить:

Для простоты в примере используется суперпользователь, в проде — отдельный пользователь миграции с минимально необходимыми правами.

Основные сценарии

Техника «~0 cutover» применима, когда нужно:

Пример миграции из AWS RDS

Перевозим PostgreSQL 16 из RDS в EKS.

Включаем логическую репликацию в RDS

  1. Создаём parameter group rds-logical-replication.
  2. Включаем rds.logical_replication.
  3. Применяем parameter group к нужной БД.
  4. Перезагружаем БД, чтобы wal_level стал logical.
  5. Убеждаемся, что есть сетевой доступ из Kubernetes-кластера.

Разворачиваем целевую БД

Секрет с суперпользователем:

BASH
kubectl create secret \
  --dry-run=client -o yaml generic rds-superuser \
  --from-literal=username=postgres \
  --from-literal=password=AngusYoung1955
Нажмите, чтобы развернуть и увидеть больше

Целевой кластер с импортом только схемы (schemaOnly: true):

YAML
apiVersion: postgresql.cnpg.io/v1
kind: Cluster
metadata:
  name: angus
spec:
  instances: 1
  storage:
    size: 50Gi
  bootstrap:
    initdb:
      import:
        type: microservice
        schemaOnly: true
        databases:
          - app
        source:
          externalCluster: rds
  externalClusters:
  - name: rds
    connectionParameters:
      host: <HOST>.rds.amazonaws.com
      user: postgres
      dbname: app
    password:
      name: rds-superuser
      key: password
Нажмите, чтобы развернуть и увидеть больше

Создаём публикацию

BASH
kubectl cnpg publication create angus \
  --external-cluster rds \
  --publication migration \
  --all-tables
Нажмите, чтобы развернуть и увидеть больше

Под капотом:

SQL
CREATE PUBLICATION "migration" FOR ALL TABLES;
Нажмите, чтобы развернуть и увидеть больше

Создаём подписку

BASH
kubectl cnpg subscription create angus \
  --external-cluster rds \
  --publication migration \
  --subscription migration
Нажмите, чтобы развернуть и увидеть больше

CloudNativePG соберёт строку подключения и выполнит:

SQL
CREATE SUBSCRIPTION "migration"
  CONNECTION 'dbname="app" host="<HOST>.rds.amazonaws.com" passfile="/controller/external/rds/pgpass" user="postgres"'
  PUBLICATION migration;
Нажмите, чтобы развернуть и увидеть больше

Пароль в БД не утекает — лежит в Kubernetes Secret, подкладывается через passfile.

Лаг репликации:

SQL
SELECT confirmed_flush_lsn, pg_current_wal_lsn(),
       (pg_current_wal_lsn() - confirmed_flush_lsn) AS lsn_distance
FROM pg_replication_slots
WHERE slot_name = 'migration';
Нажмите, чтобы развернуть и увидеть больше

Синхронизация sequences:

BASH
kubectl cnpg subscription sync-sequences \
  --subscription migration \
  angus
Нажмите, чтобы развернуть и увидеть больше

Cutover

Перед боевым переключением:

  1. Прорепетируйте cutover хотя бы один раз.
  2. Поднимите количество реплик до 3.
  3. Настройте физические бэкапы новой БД.
  4. Проверьте восстановление из бэкапа.

Сам cutover:

  1. Останавливаете запись из приложения.
  2. Дожидаетесь полной синхронизации подписки.
  3. Обновляете sequences.
  4. Проверяете целостность данных.
  5. Раскатываете новую версию приложения, которая ходит на сервис angus-rw.
  6. Удаляете подписку и публикацию.

Выводы

Подход работает с любым PostgreSQL 10+, доступным по сети и поддерживающим логическую репликацию: DBaaS, VM, bare metal, другой Kubernetes-оператор. Та же методика подходит и для major-апгрейдов. Особое внимание — на расширения PostgreSQL: их совместимость надо проверять отдельно.

Начать поиск

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

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