Перейти к основному содержимому

Резервное копирование и восстановление

Система записи Planeon — это PostgreSQL. Всё, что платформа знает о вашем развёртывании — пулы, сессии, ролевые привязки, история аудита, состояние лицензирования и все хранимые учётные данные — находится там, запечатанное в состоянии покоя ключом SECRETS_MASTER_KEY. Резервируйте все три вещи ниже вместе, по одному расписанию — и вы сможете восстановить рабочее развёртывание из ничего, кроме бэкапа и работающего кластера Proxmox.

Этот runbook предполагает раскладку эталонного production-стека: compose.yaml и файл .env в одном каталоге, с заданными в .env POSTGRES_DB, POSTGRES_USER, POSTGRES_PASSWORD и SECRETS_MASTER_KEY. Скорректируйте пути, если ваша установка отличается.

Что резервировать

Три вещи, каждый раз — ни одна из них не опциональна:

  1. База данных Postgres, через pg_dump. Это всё состояние платформы целиком: desktop-пулы, владение управляемыми ВМ, сессии, RBAC, история аудита, состояние активации лицензии и каждый зашифрованный секрет (API-токены Proxmox, учётные данные гостя/присоединения к домену).
  2. SECRETS_MASTER_KEY, резервируемый отдельно от дампа базы данных. Это ключ конверта AES, шифрующий каждое учётное данное выше. Резервная копия базы данных бесполезна без точного ключа, который был активен на момент её снятия — восстановление дампа под неверным ключом не деградирует мягко, оно падает прямо (см. предостережение ниже). Поскольку ключ может меняться со временем (окно ротации задаёт SECRETS_MASTER_KEY_PREVIOUS и позднее выводит старый ключ из обращения), храните ключ, соответствующий каждому дампу, вместе с ним — не полагайтесь на то, какое значение случайно окажется в текущем .env на момент восстановления. Храните его в менеджере секретов или парольном хранилище, а не только внутри копии .env, лежащей рядом с SQL-дампом.
  3. Файл .env — домен, конфигурация OIDC-клиента, учётные данные Postgres и всё остальное, что нужно стеку для запуска. Без него вы всё ещё сможете восстановить базу данных, но вам придётся вручную заново вводить каждую настройку.

Что восстановимо из PVE, а что нет

Не всё, что хранит Planeon, обходится одинаково дорого, если бэкап к моменту восстановления устарел на несколько часов или дней (частичная потеря — гораздо более частый сценарий, чем полная потеря Postgres):

  • Восстановимо: владение управляемыми ВМ и членство в desktop-пулах выводятся из инвентаря Proxmox VE. Как только вы восстановите Postgres и снова направите worker на живой кластер, следующий цикл сверки пересканирует PVE и пересинхронизирует состояние пулов/ВМ — любое расхождение между бэкапом и фактическим состоянием кластера само устраняется в пределах одного интервала сверки (POOL_RECONCILE_INTERVAL, по умолчанию 30s).
  • Не восстановимо — Postgres единственная копия: история аудита, ролевые привязки RBAC, состояние активации лицензии и каждое зашифрованное учётное данное (API-токены Proxmox, учётные данные гостя/присоединения к домену). Ничего из этого не существует больше нигде. Восстановление дампа старше вашего последнего изменения конфигурации безвозвратно теряет всё, что произошло после снятия дампа — ни со стороны PVE, ни где-либо ещё нет резервного источника, откуда это можно было бы воспроизвести.

Токены подключения Guacamole — особый случай, о котором стоит сказать отдельно именно потому, что резервировать их не нужно: они короткоживущие, выпускаются в Redis (не в Postgres) и истекают сами. Их потеря стоит пользователю одного переподключения, не больше.

Процедура резервного копирования

Запускайте это по расписанию (cron, systemd timer или ваш инструмент резервного копирования) из каталога, где лежат compose.yaml и .env:

# Экспортируем .env в shell, чтобы $POSTGRES_USER/$POSTGRES_DB ниже
# разрешились — перенаправление вывода pg_dump происходит на хосте,
# а не внутри контейнера.
set -a
source .env
set +a

docker compose exec -T postgres pg_dump -U "$POSTGRES_USER" "$POSTGRES_DB" \
> "planeon-$(date +%F).sql"

date +%F даёт реальную календарную дату на момент запуска (например, planeon-2026-07-12.sql) — ставьте метку времени на каждом дампе, чтобы отличать бэкапы друг от друга и выбирать нужный для восстановления.

Вместе с дампом скопируйте текущее значение SECRETS_MASTER_KEY и сам файл .env в защищённое хранилище резервных копий. Храните все три — SQL-дамп, мастер-ключ, активный на момент его снятия, и .env — как один согласованный набор.

Процедура восстановления

  1. Остановите сервисы приложения, чтобы ничто не писало в Postgres во время восстановления и ни один сервис не стартовал против наполовину восстановленной схемы:

    docker compose stop api worker gateway web caddy
  2. Восстановите в чистый Postgres. Если вы восстанавливаетесь на хосте, где уже есть том Postgres (аварийное восстановление на той же машине), сначала очистите его — docker compose down -v удаляет именованные тома стека, включая postgres_data, поэтому делайте это только тогда, когда уверены, что хотите его перезаписать. Затем поднимите пустую базу данных:

    docker compose up -d postgres redis

    Загрузите в неё дамп:

    set -a
    source .env
    set +a

    docker compose exec -T postgres psql -U "$POSTGRES_USER" "$POSTGRES_DB" \
    < planeon-2026-07-12.sql
  3. Восстановите .env и SECRETS_MASTER_KEY — перезапишите рабочий .env резервной копией, соответствующей этому дампу (или как минимум убедитесь, что SECRETS_MASTER_KEY в текущем .env — это точное значение, которое было активно на момент снятия дампа).

  4. Убедитесь, что схема актуальна, прежде чем запускать сервисы приложения:

    docker compose run --rm migrate status

    Каждая миграция должна показывать Applied. Восстановленный дамп уже несёт собственную строку goose_db_version, поэтому обычно она уже актуальна; если вы восстанавливаетесь на более старую PLANEON_VERSION, чем та, на которой был снят дамп, сначала примените недостающие миграции командой docker compose run --rm migrate.

  5. Запустите остальной стек:

    docker compose up -d

    Проверьте готовность так же, как при свежей установке — см. Наблюдаемость → Семантика readiness.

Предостережение: несовпадающий мастер-ключ падает громко

Каждое приложение (api, worker, gateway) перед началом обслуживания запросов выполняет стартовую проверку, которая пытается открыть каждый хранимый конверт секрета сконфигурированным SECRETS_MASTER_KEY. Если ключ не совпадает с тем, которым были запечатаны секреты в дампе, запуск падает прямо с ошибкой, называющей таблицу и строку, которую не удалось расшифровать — процесс не стартует в деградированном или тихо сломанном состоянии.

Неверный ключ — не мягкая деградация

Это сделано намеренно: тихо работать с нерасшифровываемыми учётными данными было бы намного хуже, чем отказаться запускаться. Если вы видите эту ошибку после восстановления, значит SECRETS_MASTER_KEY в восстановленном .env не совпадает с ключом, который был активен на момент снятия дампа — восстановите правильный ключ (а если восстановление пришлось на окно ротации — и его пару SECRETS_MASTER_KEY_PREVIOUS) и перезапустите сервисы.

Это самая частая ошибка при восстановлении: восстановить базу данных, не восстановив соответствующий ей мастер-ключ. Храните их вместе в процессе резервного копирования, а не только в .env — см. «Что резервировать» выше.