← Все статьи

Перевод CloudNativePG на плагин Barman Cloud без разрыва архива

CNPG убирает встроенную поддержку barmanObjectStore. У переключения нет валидного промежуточного состояния, у базы не было ни одного базового бэкапа, а один путь решает, работает ли ещё point-in-time recovery.

В домашней лаборатории кластер PostgreSQL 16 из двух инстансов под управлением CloudNativePG обслуживает приложение пет-проекта и архивирует в MinIO на отдельной VM. CloudNativePG объявил spec.backup.barmanObjectStore устаревшим, и оператор прямо говорит об этом в admission-предупреждении: встроенную поддержку Barman Cloud удалят в 1.31.0. Замена — плагин Barman Cloud.

Ещё до того, как я тронул Postgres, разбор задачи вскрыл две проблемы.

Проблема первая: cert-manager

Плагин поставляет самоподписанный Issuer и два Certificate для mTLS с оператором, так что cert-manager — жёсткое условие. Он не был установлен.

Проблема вторая: бэкапов не было

Архивация WAL показывала ContinuousArchiving: True, и была задана политика хранения на семь дней. Но ни одного ScheduledBackup и ни одного объекта Backup никогда не существовало. Архивация WAL без базового бэкапа — это не бэкап: сегменты накатываются вперёд от базы, а накатывать было не на что. Оба сигнала были зелёными.

Валидного промежуточного состояния нет

Форму изменения задаёт сам API:

spec.plugins: Invalid value: ["barman-cloud.cloudnative-pg.io"]:
Cannot enable a WAL archiver plugin when barmanObjectStore is configured

Старое поле и плагин-архиватор WAL не могут сосуществовать, поэтому переключение нельзя растянуть на этапы. ObjectStore, Cluster.spec.plugins, удаление spec.backup и ScheduledBackup.method: plugin должны приехать одним apply.

Порядок

  1. Сначала сделать так, чтобы бэкапы существовали. Ежедневный ScheduledBackup с immediate: true. Переносить механизм бэкапов базы, у которой ни разу не было восстановимого бэкапа, — значит остаться без отступления, если что-то пойдёт не так, и без способа доказать, что миграция что-то сохранила.
  2. Поставить cert-manager, затем плагин и проверить каждый функционально: одноразовый самоподписанный Certificate должен дойти до Ready=True.
  3. Один атомарный коммит на переключение.
  4. Доказать результат свежим бэкапом, снятым плагином, а не условием в статусе.

Решение, которое значило больше всего

Я оставил destinationPath идентичным до байта. Barman раскладывает объектное хранилище как <destinationPath>/<serverName>/, а имя сервера по умолчанию равно имени кластера, так что сохранение пути означает, что плагин продолжает тот же архив. Статус ObjectStore в итоге показал одно непрерывное окно восстановления через оба механизма: первая точка восстановления — из бэкапа, снятого старым кодом, последний успешный бэкап — от плагина.

Если бы плагин смотрел на новый префикс, базовые бэкапы остались бы в одной папке, а все новые сегменты WAL — в другой. Об этом ничто не сообщает. Обе половины выглядят здоровыми, а разрыв всплывает, только когда пытаешься сделать восстановление на момент времени.

Как я проверял

  • Фаза кластера прошла через «Primary instance is being restarted without a switchover» и вернулась в здоровое состояние за четыре-пять минут. На трафик приложения это не повлияло, но это окно обслуживания, а не правка конфига.
  • pg_stat_archiver из самого Postgres после pg_switch_wal(), чтобы проверка была про «сейчас»: счётчик заархивированных растёт, счётчик ошибок стоит.
  • Backup с METHOD plugin и PHASE completed, запрошенный по полному имени backups.postgresql.cnpg.io. Когда установлен Longhorn, короткое backup разрешается в его CRD.

Ловушки по дороге

  • kubectl apply --dry-run=server падал на корректном манифесте с той же ошибкой «cannot enable». Объект создал Argo CD через server-side apply, и клиентский merge не знал, что старое поле нужно удалить. Server-side dry-run с field manager’ом Argo прошёл.
  • Sidecar плагина — нативный init-контейнер с restartPolicy: Always. Проверка, читающая только spec.containers, решит, что его не внедрили.
  • ScheduledBackup.method по умолчанию — barmanObjectStore. Забудете указать — и расписание будет смотреть на механизм, которого у кластера больше нет, а объект при этом будет выглядеть нормально.
  • Политика хранения переезжает в ObjectStore. Оставите её в Cluster — и она молча потеряется.

Что взять с собой

  • Архивация WAL без базовых бэкапов — не бэкап, как бы зелено она ни выглядела.
  • Когда две конфигурации не могут сосуществовать, изменение должно быть одним атомарным apply.
  • Сохраните путь назначения, чтобы продолжить тот же архив.
  • Доказывайте механизм бэкапов бэкапом, который он сделал, а не условием в статусе.
  • Бэкап, который никто не восстанавливал, — всё ещё гипотеза. Следующий шаг — учение по восстановлению.