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.
71 lines
3.5 KiB
Bash
Executable file
71 lines
3.5 KiB
Bash
Executable file
#!/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 "Бэкап завершён."
|