gendesign/ops/gendesign-backup.default.example
bot-backend 1cb14c6a35
All checks were successful
CI Trade-In / changes (pull_request) Successful in 7s
CI Trade-In / backend-tests (pull_request) Has been skipped
CI Trade-In / browser-tests (pull_request) Has been skipped
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI / changes (pull_request) Successful in 8s
CI / frontend-tests (pull_request) Has been skipped
CI / backend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Has been skipped
fix(ops): бэкап без выгрузки в S3 падает, а не рапортует успех
Проверка хостов за шесть дней до переезда показала, что на Poincare пустой
crontab: ни бэкапов, ни сторожей, ни конфигов. Сегодня это неважно — базы ещё
на Beget, где всё работает. Тридцатого августа они переезжают, и бэкапы там
просто не начнутся.

Хуже, что отказ был бы тихим. Выгрузка в S3 была опциональной: без четырёх
переменных скрипт писал "backup stays local only" и выходил с кодом ноль.
Не ошибка, не алерт — успешный прогон. Сторож свежести на новом хосте тоже
не установлен, так что промолчали бы оба.

Теперь отсутствие любой из S3_ENDPOINT / S3_BUCKET / S3_ACCESS_KEY /
S3_SECRET_KEY роняет прогон с ненулевым кодом — до снятия дампа, потому что
незачем тратить время и место на дамп, которому некуда уехать. Образец
поведения взят из backup-forgejo.sh, где так было с самого начала.

BACKUP_ALLOW_LOCAL_ONLY=1 оставляет прежний путь для машины без ключей.

Ревью показало, что первая версия этой форточки возвращала ровно ту дыру,
которую чинит PR: локальный прогон всё равно писал сентинел и выходил нулём,
поэтому сторож свежести оставался зелёным, а единственным следом был WARNING
в логе, который никто не читает. Достаточно было раскомментировать одну
строку в /etc/default — и «настроено» выглядело бы неотличимо от рабочего.
Теперь такой прогон сентинел НЕ пишет и сразу зовёт notify().

Попутно найден живой баг: в lib-backup.sh внутри блока после ||
литеральное `\n` разбиралось шеллом как команда `n`, поэтому запасной канал
оповещения по почте не срабатывал никогда. То есть при недоступности Telegram
мы теряли уведомления молча — тот же класс тишины.

restore-drill.sh поставлен в расписание Poincare: он существовал, но не
запускался нигде, и восстановимость дампов автоматически не проверялась ни
разу. gzip -t и трейлер pg_dump говорят «файл не битый», а это не то же
самое, что «база поднимается». У самой дрели своего сторожа нет и MAILTO в
cron не задан, поэтому её падение теперь тоже уходит в notify(). Заодно
docker rm -f получил -v: регулярный запуск иначе оставлял бы dangling-том
размером с базу до воскресной чистки.

Имя бакета в примере исправлено на gendsgn-backups — на хостах настроен
именно он, forgejo пишет туда же под префиксом forgejo/.

Две находки ревью намеренно не закрыты. Фолбэк на общий конфиг в
tradein-скрипте остался elif: если появится /etc/default/tradein-backup без
S3-переменных, прогон упадёт вместо чтения общего файла — под новой политикой
это и есть желаемое поведение. Дрель проверяет только серию gendesign:
restore-drill.sh не умеет батч, а расширять его в этом PR значит смешивать
задачи.

Хостовая часть — установка crontab и конфига на Poincare — в PR не входит.
Конфиг уже положен вручную и выгрузка с Poincare проверена живым объектом;
crontab ставится в окно переезда, потому что продуктового postgres там пока
нет и ночные прогоны падали бы впустую.

Refs #3085, #3008
2026-08-24 20:48:45 +03:00

60 lines
3.5 KiB
Text

# Environment file for ops/backup.sh (MAIN gendesign DB backup, #71).
#
# Install on the prod VM as a ROOT-OWNED, chmod-600 file that is NOT in git:
# sudo cp /opt/gendesign/ops/gendesign-backup.default.example /etc/default/gendesign-backup
# sudo chmod 600 /etc/default/gendesign-backup
# sudo $EDITOR /etc/default/gendesign-backup # fill in real S3 credentials
#
# backup.sh sources this file if present. With NO S3 vars set, dumps stay
# local-only under /opt/gendesign/backups (retention KEEP=7). Fill these in to
# also push each dump off-box to S3 (recommended — local-only dies with the VM).
# backup.sh now also dumps cluster globals (roles/GRANTs, via `pg_dumpall
# --globals-only`) alongside the main dump — same S3 vars, same bucket, no
# separate config needed (#2203).
#
# tradein-mvp/deploy/backup-tradein-db.sh (separate DB, separate cron job)
# prefers its OWN env file at /etc/default/tradein-backup, but falls back to
# THIS file if that one doesn't exist — so filling in the S3 vars here also
# enables off-box upload for the tradein DB backup, unless you want the two
# DBs going to different buckets/creds (then create /etc/default/tradein-backup
# with its own S3_* vars instead).
#
# To rehearse a restore from a dump this script produced (safe, throwaway
# container, never touches prod) see ops/restore-drill.sh.
#
# Missed-run alerting (#2203): ops/check-backup-staleness.sh (separate cron
# entry, see its header) alerts when a backup's sentinel goes stale. It reads
# TELEGRAM_BOT_TOKEN/TELEGRAM_CHAT_ID from THIS file (not from
# /etc/default/gendesign-uptime — deliberately a separate config so backup
# alerting doesn't depend on the uptime watchdog's env file existing).
# SAME variable names as ops/gendesign-uptime.default.example — point both
# files at the same bot/chat if you want one Telegram destination for
# everything, or use different bots/chats if you'd rather split the noise.
# Without these two set, ops/check-backup-staleness.sh still logs, just
# doesn't send a Telegram alert.
# --- S3 off-site upload (Selectel S3-compatible). All four REQUIRED (#3085) —
# without them, backup.sh and backup-tradein-db.sh refuse to run unless
# BACKUP_ALLOW_LOCAL_ONLY=1 is also set (see below). ---
#S3_ENDPOINT=https://s3.ru-1.storage.selcloud.ru
#S3_BUCKET=gendsgn-backups
#S3_ACCESS_KEY=REPLACE_WITH_REAL_ACCESS_KEY
#S3_SECRET_KEY=REPLACE_WITH_REAL_SECRET_KEY
# --- missed-run alerting (Telegram, shared bot with uptime watchdog) ---
#TELEGRAM_BOT_TOKEN=123456789:AA-REPLACE_WITH_REAL_BOT_TOKEN
#TELEGRAM_CHAT_ID=123456789
# --- optional overrides (defaults are sensible; uncomment only to change) ---
#BACKUP_ALLOW_LOCAL_ONLY=1 # explicit escape hatch: run without S3 creds and
# accept a local-only backup (still logs a loud
# WARNING each run). Leave unset in prod.
#KEEP=7 # how many recent local dumps to retain (applies to both
# the main dump series and the globals dump series)
#MIN_DUMP_BYTES=51200 # sanity floor for the MAIN dump; a dump smaller than
# this is treated as failed. No equivalent floor for the
# globals dump — a handful of roles legitimately gzips to
# well under this. tradein-backup.sh has its own,
# separate MIN_DUMP_BYTES (default 10240) — set in
# /etc/default/tradein-backup, not here.
#LOCAL_BACKUP_DIR=/opt/gendesign/backups