Рецепт 17. In-place major-апгрейды PostgreSQL через pg_upgrade

CloudNativePG 1.26: декларативный in-place major-апгрейд PostgreSQL. Меняешь imageName в манифесте — оператор сам прогоняет pg_upgrade --link. Тест на 2.2 ТБ: апгрейд за 33 секунды.
Опубликовано:

Оригинал: CNPG Recipe 17 — PostgreSQL In-Place Major Upgrades — 3 апреля 2025.

В CloudNativePG 1.26 появилась декларативная поддержка in-place major-апгрейдов PostgreSQL через pg_upgrade. Major-апгрейд теперь делается ровно так же, как minor: меняешь imageName в манифесте кластера — оператор делает остальное. Требуется короткий downtime, но операционно это сильно проще, чем blue/green и логическая репликация.

Существующие подходы

CloudNativePG предлагает три декларативных способа major-апгрейдов. Два из них требуют новый кластер и blue/green:

  1. Импорт через pg_dump/pg_restore — практично для маленьких БД и тестирования версий, требует downtime на финальный cutover.
  2. Логическая репликация — нулевой downtime независимо от размера БД; правильный выбор для критичных БД и миграций из RDS / других сред (см. Рецепт 5 и Рецепт 15).
  3. In-place через pg_upgrade — offline-подход с минимальными операционными хлопотами. Об этом — этот рецепт.

Когда in-place оправдан

Главная мотивация — убрать операционную разницу между minor- и major-апгрейдами для GitOps. Чтобы major выкатить, надо просто отредактировать spec кластера — обновить образ для primary и standby. Особенно ценно, когда у вас «десятки или сотни PostgreSQL-кластеров в одном Kubernetes».

Перед началом

Пробовать in-place можно на любом Kubernetes — включая локальный kind (см. Рецепт 1).

Как это работает

Operand-образ PostgreSQL задаётся через:

В рецепте — через imageName.

Стартовая конфигурация (PG 13)

YAML
apiVersion: postgresql.cnpg.io/v1
kind: Cluster
metadata:
  name: pg
spec:
  imageName: ghcr.io/cloudnative-pg/postgresql:13.21-minimal-bullseye
  instances: 3

  storage:
    size: 1Gi
  walStorage:
    size: 1Gi
Нажмите, чтобы развернуть и увидеть больше

Шаги апгрейда

  1. Корректное выключение. Кластер останавливается, чтобы данные были консистентны. Это и есть downtime.
  2. Запись статуса. CloudNativePG фиксирует в статусе Cluster текущий образ — это необходимо для отката при провале.
  3. Подготовка данных. Запускается Job, который:
    • временно копирует бинарники старого PostgreSQL для нужд pg_upgrade;
    • инициализирует новый PGDATA через initdb для целевой версии;
    • проверяет, что апгрейд действительно нужен (сравнивает on-disk версии);
    • аккуратно ремаппит volume для WAL и tablespaces.
  4. Запуск pg_upgrade --link. Используются hard-links — это резко ускоряет миграцию данных и почти не требует доп. места и I/O.
  5. Завершение или откат:
    • Успех — оператор заменяет старые data-директории новыми, удаляет PVC реплик, перезапускает кластер.
    • Провал — придётся откатить вручную: вернуть старый манифест и удалить upgrade-job.

Пример апгрейда

Проверяем стартовую версию:

BASH
kubectl cnpg status pg
kubectl cnpg psql pg -- -qAt -c 'SELECT version()'
Нажмите, чтобы развернуть и увидеть больше
PLAINTEXT
PostgreSQL 13.21 (Debian 13.21-1.pgdg110+1) on x86_64-pc-linux-gnu, ...
Нажмите, чтобы развернуть и увидеть больше

Меняем imageName на PostgreSQL 17:

YAML
apiVersion: postgresql.cnpg.io/v1
kind: Cluster
metadata:
  name: pg
spec:
  imageName: ghcr.io/cloudnative-pg/postgresql:17.5-minimal-bullseye
  instances: 3

  storage:
    size: 1Gi
  walStorage:
    size: 1Gi
Нажмите, чтобы развернуть и увидеть больше
BASH
kubectl apply -f pg.yaml
Нажмите, чтобы развернуть и увидеть больше

Проверяем версию:

PLAINTEXT
PostgreSQL 17.5 (Debian 17.5-1.pgdg110+1) on x86_64-pc-linux-gnu, ...
Нажмите, чтобы развернуть и увидеть больше

Поды после апгрейда:

PLAINTEXT
NAME   READY   STATUS    RESTARTS   AGE
pg-1   1/1     Running   0          62s
pg-4   1/1     Running   0          36s
pg-5   1/1     Running   0          15s
Нажмите, чтобы развернуть и увидеть больше

Реплики pg-2 / pg-3 пересоздаются под новыми именами pg-4 / pg-5 — оператор пересобирает их через pg_basebackup.

Ограничения и нюансы

Пересоздание реплик

Реплики восстанавливаются через pg_basebackup. Пока новые реплики не готовы, если упадёт primary — придётся восстанавливаться из бэкапа. А этот бэкап может быть от предыдущей версии PostgreSQL — то есть major-апгрейд придётся повторять. Ситуация маловероятная, но кластер какое-то время в degraded-состоянии по HA.

Митигация: сразу после апгрейда снять физический бэкап.

Совместимость расширений

Все используемые расширения и их версии должны быть в operand-образе целевой версии PostgreSQL. Отсутствие расширения — провал апгрейда. Перед прод-апгрейдом обязательно проверьте совместимость.

Тест на большой БД (2.2 ТБ)

Автор протестировал апгрейд PostgreSQL 16 на тестовой БД из pgbench со scale 150 000 — это 2.2 ТБ.

Стартовый статус:

PLAINTEXT
Cluster Summary
Name                 default/pg
PostgreSQL Image:    ghcr.io/cloudnative-pg/postgresql:16
Primary instance:    pg-1
Status:              Cluster in healthy state
Instances:           1
Ready instances:     1
Size:                2.2T
Current Write LSN:   1D0/8000000 (Timeline: 1 - WAL File: 00000001000001D000000001)
Нажмите, чтобы развернуть и увидеть больше

Апгрейд на PostgreSQL 17 завершился за 33 секунды, кластер восстановил полную работоспособность меньше чем за минуту.

После:

PLAINTEXT
Cluster Summary
Name                 default/pg
PostgreSQL Image:    ghcr.io/cloudnative-pg/postgresql:17
Status:              Cluster in healthy state
Size:                2.2T
Current Write LSN:   1D0/F404F9E0 (Timeline: 1 - WAL File: 00000001000001D00000003D)
Нажмите, чтобы развернуть и увидеть больше

Главный вывод: так как используется pg_upgrade --link (hard-links), время апгрейда зависит в основном от количества таблиц, а не от объёма БД.

Выводы

In-place major-апгрейды через pg_upgrade приносят классический PostgreSQL-апгрейд в Kubernetes — декларативно и с минимальными операционными накладными. Downtime есть, но он невелик; зато не нужен blue/green-кластер. Особенно хорошо подходит для «парков» из небольших и средних PostgreSQL.

Начать поиск

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

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