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
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.
110 lines
5.3 KiB
Markdown
110 lines
5.3 KiB
Markdown
# Бэкап и восстановление
|
||
|
||
Артефакты в репозитории (`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` и пути ниже).
|
||
|
||
```bash
|
||
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
|
||
```
|
||
|
||
## Ручной бэкап
|
||
|
||
```bash
|
||
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, чтобы не было
|
||
> записи во время восстановления.
|
||
|
||
```bash
|
||
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()` идемпотентен) и поднимется. Проверить:
|
||
|
||
```bash
|
||
curl -fsS http://localhost:8080/health/ready
|
||
```
|
||
|
||
## Восстановление uploads
|
||
|
||
```bash
|
||
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`.)
|
||
|
||
## Проверка дампа без восстановления
|
||
|
||
```bash
|
||
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` читает.
|