food-market/docs/backup-restore.md
nns c4a59d7bf2
Some checks failed
Auto-tag / Create date-tag (push) Waiting to run
CI / Backend (.NET 8) (push) Waiting to run
CI / Web (React + Vite) (push) Waiting to run
CI / POS (WPF, Windows) (push) Waiting to run
Docker API / Build + push API (push) Waiting to run
Docker API / Deploy API on stage (push) Blocked by required conditions
Docker Public / Build + push Public (test + latest) (push) Has been cancelled
Docker Web / Build + push Web (push) Has been cancelled
Docker Public / Deploy :test → test.food-market.kz (push) Has been cancelled
Docker Web / Deploy Web on stage (push) Has been cancelled
docs(prod): репа догоняет новую раскладку postgres на prod-vm
05.10.2026 общий postgres-инстанс с чужими боевыми базами (sklad, purchase,
calcman, calcman_staging, makesales + мусорная старая food_market) вынесен
из compose прода food-market в отдельный compose-проект shared-postgres
(контейнер shared-postgres, порт 5434, том food-market-postgres-data external).
Прод food-market поднимается в собственный контейнер food-market-prod-postgres
(порт 5437, том food-market-prod-postgres-data). Простой при разделении — 12с,
соседи живы. См. docs/shared-postgres-followup-progress.md.

Этот коммит синхронизирует репозиторий, чтобы следующий деплой из main не
воскресил старую раскладку:

- deploy/docker-compose.yml: container_name/port/volume приведены к прод-файлу
  (food-market-prod-postgres, 127.0.0.1:5437, food-market-prod-postgres-data).
- deploy/shared-postgres/ (новое): документационная копия соседнего compose'а,
  с явной пометкой «food-market им не управляет».
- deploy/food-market-backup.sh / anonymize-prod.sh / db-schema-diff.sh:
  defaults FM_PG_CONTAINER / FM_PROD_CONT = food-market-prod-postgres.
- docs/RUNBOOK.md: новый раздел «Postgres на prod-vm» с таблицей трёх
  инстансов и явным запретом трогать shared-postgres из наших скриптов.
- docs/backup-restore.md: контейнер в таблице и примерах — новый.

Guards (не в репе — живут в ~/deploy-stage.sh и ~/quality-watchdog.sh)
расширены симметрично: стенду запрещены shared-postgres, food-market-prod-
postgres[-data], 5434/5437; проду — food-market-postgres-data (чужие данные).
Проверка: docker compose config/ps на prod-vm shared-postgres не упоминают,
grep по репе — ни один скрипт не делает docker exec в shared-postgres.
2026-10-06 00:35:54 +05:00

5.3 KiB
Raw Blame History

Бэкап и восстановление

Артефакты в репозитории (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-prod-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).

Контейнер прод-БД — food-market-prod-postgres (с 2026-10-05, порт 127.0.0.1:5437, том food-market-prod-postgres-data). Соседний контейнер shared-postgres (другой compose-проект, порт 5434) обслуживает чужие боевые базы соседних систем и food-market не принадлежит. Все pg_dump, pg_restore, docker exec в наших скриптах — только в food-market-prod-postgres. См. docs/shared-postgres-followup-progress.md и раздел «Postgres на prod-vm» в docs/RUNBOOK.md.

Установка таймера на сервере (деплой-шаг)

Предполагается, что репозиторий выложен в /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-prod-postgres:/tmp/restore.dump

# Вариант A — восстановить в чистую БД (рекомендуется):
docker exec food-market-prod-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-prod-postgres pg_restore -U food_market -d food_market --no-owner /tmp/restore.dump

# Вариант B — в существующую БД, заменив объекты (без пересоздания БД):
# docker exec food-market-prod-postgres pg_restore -U food_market -d food_market --clean --if-exists --no-owner /tmp/restore.dump

docker exec food-market-prod-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-prod-postgres:/tmp/v.dump
docker exec food-market-prod-postgres pg_restore --list /tmp/v.dump | head   # TOC валидного архива
docker exec food-market-prod-postgres rm -f /tmp/v.dump

Скрипт и формат проверены локально (2026-05-27): дамп PGDMP, custom-format, 248 TOC-записей, pg_restore --list читает.