К содержанию
ActionPulse

Резервное копирование

Что копировать в on-prem-установке ActionPulse, какие средства входят в комплект, как проверить, что копия действительно восстанавливается, и что в неё не попадает.

Кому
Операторам установки
Проверено
На этой странице

Состояние установки ActionPulse лежит в нескольких независимых хранилищах, и потеря каждого стоит разного. Комплект поставки содержит готовый раннер полного копирования PostgreSQL и ClickHouse в S3-совместимое хранилище с подписанным манифестом и периодической проверкой восстановлением. Всё остальное — секреты, транспорт, наблюдаемость — в этот снимок не входит и требует вашего собственного решения.

Что именно надо копировать

Хранилище Что в нём Что теряется без него
PostgreSQL (том pg-data) пользователи, организации и проекты, роли, ключи, отчёты, сегменты, панели, оповещения, импорты, задачи и журнал удаления данных, запись лицензии, журналы миграций вся конфигурация установки. События в ClickHouse остаются, но их не к чему привязать: проектов, ключей и прав больше нет
ClickHouse (том ch-data) события, аналитические таблицы и материализации, фрагменты записанных сессий вся история продуктовой аналитики
NATS JetStream (том nats-data) транспортные потоки EVENTS, REPLAY, IMPORT_FENCES события, принятые но ещё не записанные в ClickHouse. Ретеншн EVENTS — 48 часов, REPLAY — 24 часа: старше этого в транспорте ничего нет
Redis (том redis-data) кэш связок личности, квот и дедупликации сами данные не теряются, кэш перестраивается. Но при восстановлении Redis обязательно сбрасывается — см. Восстановление
Том import-uploads загруженные CSV незавершённых импортов незавершённый импорт нельзя продолжить, файл придётся загрузить заново. Чекпоинт и история сопоставления остаются в PostgreSQL
.env, каталог secrets/, TLS-ключи, ключ лицензии секреты, конфигурация, ключи шифрования установка не поднимается, а часть данных в PostgreSQL перестаёт читаться навсегда
Том caddy-data сертификаты и состояние ACME для реального домена сертификат будет выпущен заново; на время выпуска сайт недоступен по HTTPS
Тома Prometheus, Elasticsearch, Grafana, Kibana метрики, журналы, панели наблюдаемость за прошедший период. На работу продукта не влияет

Что есть в комплекте

Раннер копирования включается отдельным профилем Compose. Он не запускается обычным up -d — сначала настраивается хранилище, потом включается профиль.

docker compose -f docker-compose.onprem.yml --env-file .env --profile backup up -d
Сервис Что делает
backup последовательный планировщик: снимает полные копии PostgreSQL и ClickHouse и публикует подписанный манифест. Единственный контейнер, получающий приватный ключ подписи
backup-verify периодическое изолированное восстановление последнего снимка в отдельные, выбрасываемые базы. Держит только публичные ключи проверки
backup-init одноразовая подготовка томов (владелец и права). Запускается и без профиля backup

Исполняемые файлы раннера — backup.sh, restore.sh, verify.sh, prune.sh, scheduler.sh и утилита apbackupctl — лежат внутри образа, закреплённого по digest, в каталоге /opt/actionpulse-backup/. В комплект они отдельными файлами не входят, и собирать образ не нужно: он приезжает готовым.

В каталоге backup/ комплекта лежат ровно три файла:

  • clickhouse-named-collection.xml — именованная коллекция, которую Compose монтирует в ClickHouse: через неё сервер сам пишет свой бэкап в S3, а учётные данные берутся из окружения процесса и не попадают ни в SQL, ни в журнал запросов;
  • .env.example — перечень переменных раннера с пояснениями;
  • README.md — модель безопасности, контракт манифеста и согласованность между хранилищами.

Настройка

Переменные задаются в .env комплекта. Значений здесь нет намеренно: секреты генерируйте свои, адреса и имена — свои.

Переменная Назначение Обязательность
BACKUP_S3_BUCKET бакет для копий обязательна
BACKUP_S3_PREFIX префикс внутри бакета, отдельный для этой установки необязательна
BACKUP_S3_REGION регион необязательна
BACKUP_S3_ENDPOINT HTTPS-адрес S3-совместимого хранилища; пусто — AWS необязательна
BACKUP_S3_SSE_MODE режим шифрования на стороне хранилища необязательна
BACKUP_S3_KMS_KEY_ID полный ARN ключа при режиме aws:kms обязательна для KMS
AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY доступ принципала к бакету — сгенерируйте свои обязательны
BACKUP_CH_S3_URL базовый адрес для серверного бэкапа ClickHouse; заканчивается на /clickhouse/ внутри вашего префикса обязательна
BACKUP_MANIFEST_SIGNING_KEY приватный ключ подписи манифеста — сгенерируйте свой обязательна
BACKUP_MANIFEST_PUBLIC_KEYS список публичных ключей проверки через запятую обязательна
BACKUP_INSTALLATION_ID устойчивый идентификатор инсталляции: не даёт применить журнал удалений чужой установки необязательна
BACKUP_INTERVAL_SECONDS период планировщика необязательна
BACKUP_RETENTION_DAYS срок хранения снимков необязательна
BACKUP_MIN_COMPLETE_SNAPSHOTS сколько полных снимков не удалять никогда необязательна
BACKUP_RPO_SECONDS, BACKUP_RTO_SECONDS ваши цели по потере данных и времени восстановления, попадают в метрики необязательны
BACKUP_CH_HASH_MODE полное хеширование объектов ClickHouse необязательна
BACKUP_ABORT_MULTIPART_AFTER_DAYS через сколько прерывать незавершённые многочастевые загрузки необязательна
BACKUP_PRUNE_DRY_RUN холостой прогон удаления по сроку хранения необязательна
VERIFY_INTERVAL_SECONDS период изолированной проверки восстановлением необязательна

Пару ключей подписи манифеста создаёт утилита из того же образа:

docker run --rm --entrypoint /opt/actionpulse-backup/apbackupctl \
  "$PP_BACKUP_IMAGE_REF" keygen -key-id 2026-07

Требования к хранилищу, которые проверяются fail-closed:

  • принципал должен иметь ровно ListBucket, GetBucketLocation, PutObject, GetObject, HeadObject, DeleteObject, ListBucketMultipartUploads, AbortMultipartUpload; для KMS — только Encrypt, Decrypt, GenerateDataKey на одном ключе;
  • шифрование бакета по умолчанию должно быть включено и совпадать с BACKUP_S3_SSE_MODE; проверка выполняется и до, и после загрузки;
  • адрес хранилища — HTTPS, соединения с PostgreSQL и ClickHouse — по TLS. Незашифрованные режимы требуют явных небезопасных флагов и существуют только для локального изолированного прогона;
  • в правилах жизненного цикла бакета для вашего префикса отдельно настраиваются прерывание незавершённых загрузок, удаление неактуальных версий и маркеров удаления только после истечения срока соответствия, и срок истечения не короче BACKUP_RETENTION_DAYS. При включённом Object Lock жизненный цикл обязан дожидаться retain-until, а у принципала копирования не должно быть права обходить блокировку.

Что такое снимок

Один снимок — это три группы объектов под вашим префиксом:

<prefix>/postgresql/<snapshot>.dump      логический дамп PostgreSQL
<prefix>/clickhouse/<snapshot>/...       нативный полный бэкап ClickHouse
<prefix>/manifests/<snapshot>.json       подписанный манифест, публикуется последним

Манифест — точка доверия. Он подписан Ed25519 (схема 2; неподписанная схема 1 отвергается) и фиксирует идентификатор набора и снимка, время начала и завершения, бакет, префикс и режим шифрования, версии схем PostgreSQL и ClickHouse, водяной знак согласованности и каждый объект с его размером и SHA-256. Полное хеширование объектов ClickHouse — единственное, что обнаруживает подмену объекта под неизменившимся именем; оно стоит одного дополнительного чтения за прогон, и режим «только метаданные» на входе запрещён.

Копирование ClickHouse — всегда полное, без инкрементных цепочек. Это дороже по объёму и вводу-выводу, зато удаление одного снимка по сроку хранения не делает непригодными остальные.

Частота и хранение

По умолчанию планировщик снимает копию раз в сутки, снимки хранятся 30 дней и никогда не удаляется меньше двух полных. Удаление по сроку хранения идёт в безопасном порядке: сначала объекты ClickHouse, затем дамп PostgreSQL, манифест — последним, поэтому прерванное удаление не оставляет «манифест без данных».

Цели по допустимой потере данных и по времени восстановления — ваша конфигурация, а не обещание ActionPulse. Поставляемые значения по умолчанию — только стартовая точка: согласуйте их с объёмом, стоимостью и вашими бизнес-требованиями, а затем измерьте учебным восстановлением. До измерения это не цели, а предположения.

Проверка, что копия рабочая

Копия, которую ни разу не восстанавливали, копией не является: до первого восстановления неизвестно ни время восстановления, ни то, хватает ли прав на чтение объектов, ни доступен ли ключ проверки манифеста.

Автоматически. Сервис backup-verify берёт последний полный снимок, проверяет подпись манифеста, пересоздаёт выбрасываемые базы и восстанавливает снимок в них — по умолчанию раз в сутки. Живые базы он не трогает: сценарий восстановления отказывается работать «на месте». Результат попадает в метрики pp_backup_restore_*.

Метрики. Раннер атомарно пишет текстовый файл метрик, который читает node-exporter. Единственная метка — component со значением postgresql или clickhouse; ни текста ошибки, ни идентификатора снимка, ни имён объектов в метках нет.

pp_backup_last_attempt_timestamp_seconds    pp_backup_restore_last_attempt_timestamp_seconds
pp_backup_last_success_timestamp_seconds    pp_backup_restore_last_success_timestamp_seconds
pp_backup_last_duration_seconds             pp_backup_restore_last_duration_seconds
pp_backup_last_status                       pp_backup_restore_last_status
pp_backup_consecutive_failures              pp_backup_rto_target_seconds
pp_backup_rpo_target_seconds

Признак успеха строгий: pp_backup_last_status становится 1 только после загрузки, проверки размера и статуса объекта в хранилище, проверки шифрования и публикации полного манифеста. Любой сбой до манифеста оставляет 0, увеличивает счётчик последовательных отказов и завершает прогон с ошибкой. Отказ удаления по сроку уже после успешного манифеста подтверждённый снимок не аннулирует.

Фактическое отставание копии проверяется одним запросом — для обоих компонентов результат должен быть не больше цели:

time() - pp_backup_last_success_timestamp_seconds

Правила оповещений для этого уже есть в комплекте: ActionPulseBackupFailed, ActionPulseBackupRPOExceeded и ActionPulseBackupTelemetryMissing — последнее срабатывает, когда метрик нет вовсе, то есть когда раннер молча не работает.

Руками. Изолированное восстановление проводят не реже раза в квартал и обязательно перед опасным крупным обновлением. Порядок и проверки — на странице Восстановление из резервной копии. Записывайте фактические количества строк, контрольные суммы, время начала и конца и измеренное время восстановления: это и есть ваш план восстановления.

Что не входит в копию

  • NATS JetStream. Транспорт не часть снимка баз. Его ретеншн ограничен: 48 часов для потока событий и 24 часа для потока сессий. Снимок тома и экспорт конфигурации потоков и потребителей — отдельная процедура, и перед обновлением она обязательна.
  • .env, каталог secrets/, TLS-сертификаты и ключи, LICENSE_PUBKEY, материал KMS, ключ вебхуков. Их намеренно не кладут рядом с данными: хранилище копий данных не должно давать доступ к ключам, которыми эти данные зашифрованы.
  • Ключ лицензии как файл. Запись о лицензии есть в PostgreSQL и приезжает с дампом, но конфигурация установки — нет. После восстановления статус лицензии проверяют отдельно.
  • Redis. Это кэш; более того, при восстановлении «на месте» он принудительно очищается.
  • Том import-uploads. Сырые загруженные CSV в снимок не попадают.
  • Тома наблюдаемости — Prometheus, Elasticsearch, Grafana, Kibana.
  • Сертификаты Caddy (том caddy-data).
  • База GeoIP. Её поставляете вы, в комплект она не входит.

Согласованность между хранилищами

PostgreSQL и ClickHouse не снимаются одной распределённой транзакцией. Вместо этого манифест фиксирует явный барьер записи: consistency.watermark_at берётся непосредственно перед тем, как дамп PostgreSQL получает свой снимок. Оба хранилища гарантированно содержат всё, что записано до этого момента.

Копирование ClickHouse завершается позже, поэтому в нём есть события, о которых восстановленный контроль-план ничего не знает. При восстановлении сценарий удаляет из аналитических событий строки новее водяного знака — и оба хранилища оказываются в одной точке времени.

Одно исключение записано в самом манифесте: фрагменты записанных сессий не имеют серверной отметки времени, поэтому за барьером может остаться до (конец копирования ClickHouse − водяной знак) таких данных. Окно ограничено и зафиксировано в манифесте.

Отдельно живёт подписанный журнал удалений тенантов. Неизменяемое хранилище не умеет «забыть» удалённую организацию по требованию — байты остаются до истечения снимка. Поэтому каждое восстановление проигрывает записи журнала в восстановленные базы, прежде чем данные становятся доступны. Это логическое стирание в момент восстановления, а не физическое стирание внутри хранимых снимков: физически данные исчезают, когда удаляется последний снимок, их содержащий, — то есть не позже BACKUP_RETENTION_DAYS после удаления плюс любые сроки Object Lock и хранения неактуальных версий на стороне провайдера. Не обещайте своим пользователям срок короче того, который реально обеспечивает политика вашего бакета.

Что проверить прямо сейчас

  1. pp_backup_last_success_timestamp_seconds обновлялся не позже вашей цели — для обоих компонентов.
  2. pp_backup_restore_last_status равен 1: изолированное восстановление действительно проходило, а не только копирование.
  3. В бакете есть не меньше двух манифестов со состоянием «полный».
  4. Публичный ключ проверки манифеста доступен там, откуда вы будете восстанавливаться, — и это не только этот сервер.
  5. Секреты установки (.env, secrets/, TLS-ключи) лежат в вашем менеджере секретов и не в бакете копий.
  6. Дата последнего учебного восстановления вручную — не старше квартала.