food-market/deploy/food-market-backup.sh
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

71 lines
3.5 KiB
Bash
Executable file
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env bash
#
# food-market: ежедневный бэкап БД + загруженных файлов с ротацией.
#
# Дампит Postgres (контейнер food-market-prod-postgres) в custom-формат pg_dump
# (-Fc, пригоден для pg_restore с параллелизмом/выборочным восстановлением) и
# архивирует каталог uploads. Удаляет бэкапы старше RETENTION_DAYS дней.
#
# Запускается из systemd-таймера food-market-backup.timer (ежедневно), либо
# вручную. Конфигурируется переменными окружения (значения по умолчанию
# совпадают с deploy/docker-compose.yml):
#
# FM_PG_CONTAINER имя контейнера Postgres (food-market-prod-postgres)
# FM_PG_DB имя БД (food_market)
# FM_PG_USER пользователь БД (food_market)
# FM_BACKUP_DIR куда складывать бэкапы (/opt/food-market-data/backups)
# FM_UPLOADS_DIR каталог изображений (/opt/food-market-data/uploads)
# FM_BACKUP_RETENTION_DAYS срок хранения, дней (30)
#
# ВАЖНО: НЕ `food-market-postgres` и НЕ `shared-postgres` — это контейнер
# соседнего compose-проекта с ЧУЖИМИ боевыми базами (sklad, purchase, calcman,
# calcman_staging, makesales). См. docs/shared-postgres-followup-progress.md.
set -euo pipefail
CONTAINER="${FM_PG_CONTAINER:-food-market-prod-postgres}"
DB="${FM_PG_DB:-food_market}"
DB_USER="${FM_PG_USER:-food_market}"
BACKUP_DIR="${FM_BACKUP_DIR:-/opt/food-market-data/backups}"
UPLOADS_DIR="${FM_UPLOADS_DIR:-/opt/food-market-data/uploads}"
RETENTION_DAYS="${FM_BACKUP_RETENTION_DAYS:-30}"
TS="$(date +%Y%m%d-%H%M%S)"
DB_FILE="$BACKUP_DIR/db-$TS.dump"
UPLOADS_FILE="$BACKUP_DIR/uploads-$TS.tgz"
log() { echo "[$(date -Is)] $*"; }
mkdir -p "$BACKUP_DIR"
if ! docker ps --format '{{.Names}}' | grep -qx "$CONTAINER"; then
log "ОШИБКА: контейнер '$CONTAINER' не запущен — бэкап невозможен." >&2
exit 1
fi
log "Дамп БД '$DB' → $DB_FILE"
# Дамп пишем во временный файл и переименовываем по успеху — частичный/битый
# дамп при падении pg_dump не попадёт в ротацию как валидный.
if docker exec "$CONTAINER" pg_dump -U "$DB_USER" -d "$DB" -Fc > "$DB_FILE.tmp"; then
mv "$DB_FILE.tmp" "$DB_FILE"
log "Готово: $(du -h "$DB_FILE" | cut -f1)"
else
rm -f "$DB_FILE.tmp"
log "ОШИБКА: pg_dump завершился с ошибкой." >&2
exit 1
fi
if [ -d "$UPLOADS_DIR" ]; then
log "Архив uploads '$UPLOADS_DIR' → $UPLOADS_FILE"
tar czf "$UPLOADS_FILE.tmp" -C "$(dirname "$UPLOADS_DIR")" "$(basename "$UPLOADS_DIR")" \
&& mv "$UPLOADS_FILE.tmp" "$UPLOADS_FILE"
log "Готово: $(du -h "$UPLOADS_FILE" | cut -f1)"
else
log "Каталог uploads '$UPLOADS_DIR' отсутствует — пропуск."
fi
log "Ротация: удаляю бэкапы старше $RETENTION_DAYS дн."
find "$BACKUP_DIR" -maxdepth 1 -type f -name 'db-*.dump' -mtime +"$RETENTION_DAYS" -print -delete
find "$BACKUP_DIR" -maxdepth 1 -type f -name 'uploads-*.tgz' -mtime +"$RETENTION_DAYS" -print -delete
log "Бэкап завершён."