Проверка хостов за шесть дней до переезда показала, что на 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
Deliverable 1: ops/backup.sh and tradein-mvp/deploy/backup-tradein-db.sh now
write a sentinel file on every verified-good run. A new ops/check-backup-
staleness.sh (separate cron entry, hourly) alerts via the existing Telegram
channel (same TELEGRAM_BOT_TOKEN/TELEGRAM_CHAT_ID idiom as ops/uptime-
healthcheck.sh, transition-tracked so it doesn't spam) if a sentinel goes
stale. Shared logic (notify/sentinel/state) factored into ops/lib-backup.sh
so it isn't triplicated; the two existing scripts' own hardening (integrity
checks, retention, etc.) is untouched.
Deliverable 2: ops/backup-forgejo.sh — Forgejo (git.gendsgn.ru) has no backup
today. Dumps the shared-postgres `forgejo` DB + tars the bare-repo tree, both
off-box to s3://gendsgn-backups/forgejo/ under a SEPARATE, narrower S3 key
(root-of-bucket writer key stays out of this). The key doesn't exist yet —
the script refuses to run and exits non-zero, loudly, until the four
FORGEJO_S3_* vars are filled in (see the example env file and PR description
for the exact bucket policy JSON to create it with).
Refs #2203, #2989
- ops/backup.sh и tradein-mvp/deploy/backup-tradein-db.sh теперь дампят
globals (pg_dumpall --globals-only) отдельным файлом с той же ретенцией
и той же S3-выгрузкой — pg_dump по определению не включает роли/GRANT.
- Обе выгрузки проходят gzip -t + проверку трейлера дампа перед тем как
считаться успешными; при провале файл удаляется, ретенция не трогается,
выход ненулевой.
- Убран 2>/dev/null у pg_dump в обоих скриптах — ошибка дампа теперь
видна в логе, а не глотается молча.
- tradein-backup.sh получил S3-выгрузку (по образцу ops/backup.sh, те же
4 переменные, тот же способ через aws-cli контейнер) и env-переопределяемый
порог минимального размера дампа; источник переменных —
/etc/default/tradein-backup с фолбэком на /etc/default/gendesign-backup.
- Новый ops/restore-drill.sh — учебное восстановление в одноразовый
postgis-контейнер без прод-томов, никогда не трогает боевую БД (в отличие
от ops/restore.sh, который восстанавливает В БОЕВУЮ базу).