Appearance
Резервное копирование
Три слоя данных, которые нужно сохранять:
| Слой | Где живёт | Чем бэкапить |
|---|---|---|
| База данных | volume Postgres | pg_dump (+ встроенный tenant:backup) |
| Файлы (вложения) | volume SeaweedFS | копия каталога данных / s3-синхронизация |
| Конфигурация | env-файл, traefik/*, compose | копия файлов (секреты!) |
Поисковый индекс Meilisearch бэкапить не нужно — он полностью восстанавливается командой search:rebuild.
База данных
Полный дамп (основной механизм)
bash
docker exec orbita_onpremise_db pg_dump -U orbita orbita > backup_$(date +%Y%m%d_%H%M).sqlРекомендации:
- снимать ежедневно по cron хост-системы + обязательно перед каждым обновлением;
- сжимать (
| gzip) и уносить с сервера (s3/объектное хранилище/другой хост) — бэкап на том же диске не переживёт отказ диска; - хранить минимум 14 суточных копий и несколько недельных.
Пример cron на хосте:
bash
# /etc/cron.d/orbita-backup
30 2 * * * root docker exec orbita_onpremise_db pg_dump -U orbita orbita | gzip > /backup/orbita_$(date +\%Y\%m\%d).sql.gz && find /backup -name 'orbita_*.sql.gz' -mtime +14 -deleteВстроенный бэкап тенантов
Приложение само делает логические бэкапы данных тенантов — планировщик запускает tenant:backup ежедневно в 03:00:
- формат — SQL-файл из
INSERT ... ON CONFLICT DO NOTHING(переносим между стендами, см. перенос тенанта); - складывается в
backups/tenants/<tenant_id>/на диске изfilesystems.backups_disk— задаётся переменнойBACKUPS_DISKв.env(по умолчаниюlocal— это storage внутри backend-контейнера, дампы не переживают его пересоздание; рекомендуетсяBACKUPS_DISK=s3, тогда дампы лежат в SeaweedFS и попадают в бэкап файлов); - хранятся последние 30 копий на тенанта, старые удаляются автоматически;
- ручной запуск:
php artisan tenant:backup --tenant=<ID>.
Восстановление дампа тенанта — командой tenant:restore (не сырым psql: на SaaS-схеме дамп требует переписывания имён динамических таблиц, команда делает это сама):
bash
php artisan tenant:restore latest --from-disk --tenant=<ID> --wipe # свежайший дамп, со сносом текущих данных
php artisan tenant:restore /path/to/tenant_5_20260726_030000.sql # из файла, merge-режим
php artisan search:rebuild # после восстановленияБез --wipe действует merge-семантика: существующие строки не изменяются, созданные после снятия дампа — не удаляются. Восстановление возможно только в тот же tenant_id. Данные pivot-таблиц связей входят в дамп начиная с формата v3 (при --wipe pivot пересоздаются из дампа); дампы старого формата v2 восстанавливаются без затрагивания pivot-таблиц. Дамп формата v3 требует актуальной версии tenant:restore — старая сборка его отклонит.
Это дополнение к pg_dump, а не замена: тенант-бэкап не содержит платформенных таблиц (пользователи других тенантов, настройки, лицензия).
Файлы (SeaweedFS)
bash
# Простой способ — копия каталога данных контейнера
docker cp orbita_onpremise_s3:/data ./backup_files_$(date +%Y%m%d)Для регулярного бэкапа удобнее монтировать volume и синхронизировать его rsync-ом/restic-ом на внешнее хранилище. Файлы иммутабельны (вложения не перезаписываются), поэтому инкрементальные бэкапы дёшевы.
Конфигурация
Сохраните вне сервера (в менеджере секретов):
- env-файл стенда (
onpremise.env/prod.env) — особенноAPP_KEY: без него зашифрованные данные из дампа БД не расшифровать; traefik/(включаяacme/acme.json, иначе после восстановления сертификаты будут выпускаться заново);- локальные правки compose-файлов, если они есть.
Восстановление
Порядок на чистом сервере:
- Развернуть стенд по инструкции с тем же env-файлом (тот же
APP_KEY!), но не выполнять инициализацию данных (сиды/tenant:init). - Остановить всё, кроме БД:
docker compose ... stop backend queue queue-reports scheduler reverb frontend. - Залить дамп:bash
cat backup.sql | docker exec -i orbita_onpremise_db psql -U orbita -d orbita - Восстановить файлы в volume SeaweedFS (обратный
docker cp/ rsync). - Поднять остальные сервисы, затем:bash
docker exec orbita_onpremise_backend php artisan optimize:clear docker exec orbita_onpremise_backend php artisan search:rebuild - Проверить: логин, открытие записей, скачивание старого вложения, поиск.
Репетиция восстановления
Бэкап, который ни разу не восстанавливали, — это гипотеза, а не бэкап. Прогоните восстановление на тестовой VM хотя бы раз в квартал.