food-market-backup.sh: pg_dump -Fc контейнера + tar uploads, ротация 30 дней,
атомарная запись через .tmp+mv. food-market-backup.{service,timer} — ежедневно
03:00 с догоном пропущенных. docs/backup-restore.md — установка таймера, ручной
бэкап, восстановление БД (drop+create / --clean) и uploads, проверка дампа.
Скрипт проверен против food-market-postgres: дамп PGDMP custom-format,
248 TOC, pg_restore --list читает. Установку на prod-vm не делаем — только артефакты.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
4.6 KiB
Бэкап и восстановление
Артефакты в репозитории (deploy/):
food-market-backup.sh— скрипт бэкапа БД + uploads с ротацией.food-market-backup.service— systemd oneshot-юнит, запускающий скрипт.food-market-backup.timer— ежедневный таймер (03:00, с догоном пропущенных).
Установку на prod-vm выполняет отдельный деплой-шаг (см. ниже) — здесь только подготовленные артефакты.
Что бэкапится
| Что | Как | Файл |
|---|---|---|
| База данных | pg_dump -Fc из контейнера food-market-postgres |
db-<TS>.dump (custom-format) |
| Загруженные файлы (картинки товаров) | tar czf каталога uploads |
uploads-<TS>.tgz |
Папка назначения по умолчанию — /opt/food-market-data/backups. Хранение —
30 дней (FM_BACKUP_RETENTION_DAYS), старые удаляются ротацией. Конфиг —
переменными FM_* (см. шапку food-market-backup.sh).
Установка таймера на сервере (деплой-шаг)
Предполагается, что репозиторий выложен в /opt/food-market (иначе скорректировать
ExecStart/EnvironmentFile в .service и пути ниже).
sudo install -m 0755 /opt/food-market/deploy/food-market-backup.sh /opt/food-market/deploy/food-market-backup.sh
sudo cp /opt/food-market/deploy/food-market-backup.service /etc/systemd/system/
sudo cp /opt/food-market/deploy/food-market-backup.timer /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable --now food-market-backup.timer
# Проверить расписание и последний запуск
systemctl list-timers food-market-backup.timer
# Прогнать бэкап немедленно (разово)
sudo systemctl start food-market-backup.service
journalctl -u food-market-backup.service --no-pager | tail -20
Ручной бэкап
sudo /opt/food-market/deploy/food-market-backup.sh
# или с переопределением каталога:
FM_BACKUP_DIR=/mnt/backups sudo -E /opt/food-market/deploy/food-market-backup.sh
Восстановление БД
⚠️ Восстановление перезаписывает данные. Сначала остановить API, чтобы не было записи во время восстановления.
cd /opt/food-market/deploy
docker compose stop api web
DUMP=/opt/food-market-data/backups/db-YYYYMMDD-HHMMSS.dump
# Скопировать дамп внутрь контейнера БД
docker cp "$DUMP" food-market-postgres:/tmp/restore.dump
# Вариант A — восстановить в чистую БД (рекомендуется):
docker exec food-market-postgres psql -U food_market -d postgres -c \
"DROP DATABASE IF EXISTS food_market WITH (FORCE); CREATE DATABASE food_market OWNER food_market;"
docker exec food-market-postgres pg_restore -U food_market -d food_market --no-owner /tmp/restore.dump
# Вариант B — в существующую БД, заменив объекты (без пересоздания БД):
# docker exec food-market-postgres pg_restore -U food_market -d food_market --clean --if-exists --no-owner /tmp/restore.dump
docker exec food-market-postgres rm -f /tmp/restore.dump
docker compose start api web
После старта API применит миграции (Migrate() идемпотентен) и поднимется. Проверить:
curl -fsS http://localhost:8080/health/ready
Восстановление uploads
TGZ=/opt/food-market-data/backups/uploads-YYYYMMDD-HHMMSS.tgz
# tar содержит каталог uploads/ — распаковать в родителя смонтированного пути
sudo tar xzf "$TGZ" -C /opt/food-market-data/
(Каталог /opt/food-market-data/uploads смонтирован в контейнер api как /app/uploads.)
Проверка дампа без восстановления
docker cp <dump> food-market-postgres:/tmp/v.dump
docker exec food-market-postgres pg_restore --list /tmp/v.dump | head # TOC валидного архива
docker exec food-market-postgres rm -f /tmp/v.dump
Скрипт и формат проверены локально (2026-05-27): дамп PGDMP, custom-format,
248 TOC-записей, pg_restore --list читает.