gendesign/ops/gendesign-backup-forgejo.default.example
bot-backend 7d98a674c9
All checks were successful
CI Trade-In / changes (pull_request) Successful in 8s
CI / changes (pull_request) Successful in 11s
CI Trade-In / browser-tests (pull_request) Has been skipped
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI Trade-In / backend-tests (pull_request) Has been skipped
CI / backend-tests (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Has been skipped
feat(ops): лёгкий Postgres на Beget под forgejo и glitchtip
Переезд ломает сам Forgejo. Базы forgejo (265 МБ) и glitchtip (729 МБ)
физически лежат внутри gendesign-postgres-1, который уезжает целиком вместе
с 15 ГБ Site Finder. В момент переезда легли бы веб, git по HTTP и Actions —
то есть CI, из которого мы деплоим, ровно тогда, когда он нужнее всего.
Отдельно обидно, что Forgejo заявлен компенсацией риска «всё в одном
аккаунте Selectel»: падая вместе с переездом, он эту роль не выполняет.

Владелец выбрал вариант А — отдельный лёгкий кластер на Beget. PostGIS тут
не нужен, обе схемы обычные реляционные: postgres:16-alpine против
postgis/postgis:16-3.4 ради одного гигабайта данных.

Мерж и деплой ничего не меняют. Сервис под профилем "infra", которого нет
ни в одном окружении на Beget, поэтому docker compose его не видит;
GLITCHTIP_DB_HOST имеет дефолт postgres, то есть строка подключения
GlitchTip рендерится байт-в-байт как сегодня. Переключение — правка одной
переменной на VM после переноса данных, откат — правка обратно.

Первая версия этого не держала: сервис стоял в профиле glitchtip, который
на Beget уже активен, и мерж поднял бы кластер с пустым INFRA_PG_PASSWORD
в вечный restart-loop при зелёном деплое. Поймано ревью.

Healthcheck проверяет не приём соединений, а наличие ОБЕИХ баз. Иначе
отравленный том — когда bootstrap-скрипт упал уже после инициализации
PGDATA и больше никогда не запустится — выглядел бы здоровым, а
обнаружился бы в ночь переезда как «role forgejo does not exist».

backup-forgejo.sh больше не хардкодит контейнер: ищет тот, где база forgejo
реально есть, и при двух кандидатах останавливается с exit 1. Это ровно
окно миграции, когда база лежит в обоих: молча выбрать один означает с
вероятностью 1/2 бэкапить труп, а порог MIN_DB_DUMP_BYTES протухший дамп
не поймает — он нормального размера.

ops/split-infra-postgres.sh — перенос: dry-run по умолчанию, снимок числа
строк после останова писателей, восстановление целевой ролью (суперюзер
забрал бы таблицы себе и выдал permission denied уже после переключения),
сверка построчных карт из pg_class. Ревью нашло два сценария бесшумной
потери, оба закрыты: писатель с несовпавшим именем больше не пропускается
молча (имя контейнера Forgejo — догадка, его compose вне репозитория), а
--force-restore отказывается работать, если переключение уже сделано.
Дампы под umask 077 и chmod 600: в дампе forgejo хеши паролей и токены.

Проверено на живом Docker, не по YAML: матрица профилей через compose
config, healthcheck исполнен в postgres:16-alpine (обе базы → 0, после DROP
одной → 1), bootstrap реально завёл роли и базы, пароль со спецсимволами
логинится, в docker logs его нет.

Refs #3061, #3057, #3075, #2989, #2203
2026-08-24 19:28:59 +03:00

80 lines
6.3 KiB
Text
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.

# Environment file for ops/backup-forgejo.sh (Forgejo code + DB backup, #2203).
#
# Install on the prod VM as a ROOT-OWNED, chmod-600 file that is NOT in git:
# sudo cp /opt/gendesign/ops/gendesign-backup-forgejo.default.example /etc/default/gendesign-backup-forgejo
# sudo chmod 600 /etc/default/gendesign-backup-forgejo
# sudo $EDITOR /etc/default/gendesign-backup-forgejo # fill in real S3 credentials
#
# DELIBERATELY a separate file from /etc/default/gendesign-backup (used by
# ops/backup.sh and tradein-mvp/deploy/backup-tradein-db.sh) — this backup
# uses a SEPARATE, narrower-scoped S3 service user that can ONLY PutObject
# under s3://gendsgn-backups/forgejo/*, nothing else. The prod-host writer
# key used by the other two backups (`gendsgn-backup-writer`) has root-of-
# bucket access; that key must NOT be reused here, and this key must NOT be
# put in /etc/default/gendesign-backup.
#
# THE KEY DOES NOT EXIST YET (as of #2203). Until a human creates the
# `gendsgn-backup-forgejo-writer` service user + bucket policy in the
# Selectel panel (see the #2203 PR description for the exact policy JSON —
# PutObject-only on arn:aws:s3:::gendsgn-backups/forgejo/*, explicit Deny on
# GetObject/ListBucket/Delete*) and fills in the four FORGEJO_S3_* vars
# below, ops/backup-forgejo.sh refuses to run and exits non-zero loudly. That
# is expected, not a bug.
#
# --- S3 off-site upload (Selectel S3-compatible). All four required. ---
#FORGEJO_S3_ENDPOINT=https://s3.ru-1.storage.selcloud.ru
#FORGEJO_S3_BUCKET=gendsgn-backups
#FORGEJO_S3_ACCESS_KEY=REPLACE_WITH_REAL_ACCESS_KEY_ONCE_CREATED
#FORGEJO_S3_SECRET_KEY=REPLACE_WITH_REAL_SECRET_KEY_ONCE_CREATED
# --- optional overrides (defaults are sensible; uncomment only to change) ---
#FORGEJO_S3_PREFIX=forgejo/ # key prefix inside the bucket; must match
# the policy's Resource path exactly
#KEEP=7 # how many recent local copies to retain
# (DB dumps and repo bundles are separate series)
#MIN_DB_DUMP_BYTES=2048 # sanity floor for the DB dump (small DB —
# config/issues/PRs/users, no git blobs)
#MIN_REPOS_BUNDLE_BYTES=10240 # sanity floor for the repo tar.gz
#LOCAL_BACKUP_DIR=/opt/gendesign/backups/forgejo
#FORGEJO_DIR=/home/gendesign/forgejo # Forgejo compose dir on the VM (NOT
# part of the gendesign git checkout)
#FORGEJO_REPOS_DIR=/home/gendesign/forgejo/data/forgejo/git/repositories
# --- контейнер с БД Forgejo (#3061) ---
# ЗАДАВАТЬ НЕ НУЖНО почти никогда: ops/backup-forgejo.sh находит контейнер сам.
# Он перебирает PG_CONTAINER_CANDIDATES, оставляет запущенные и берёт тот, в
# чьей базе `forgejo` реально ЕСТЬ ТАБЛИЦЫ.
#
# ПОЧЕМУ НЕ КОНСТАНТА. Раньше имя было зашито (gendesign-postgres-1 — общий
# кластер основного приложения). Переезд продукта на Selectel переносит базу
# forgejo оттуда в лёгкий инфраструктурный кластер (gendesign-infra-postgres,
# сервис infra-postgres в docker-compose.prod.yml), а старый контейнер после
# переноса какое-то время ещё жив и держит свою — с этого момента протухающую —
# копию базы. Зашитое имя означало бы, что бэкап продолжает исправно дампить
# именно её: дамп непустой, маркер завершения на месте, размер выше порога — и
# ни одной новой строки с момента cutover. Порогов на СВЕЖЕСТЬ здесь нет, так
# что выяснилось бы это только при восстановлении.
#
# КОГДА ЗАДАВАТЬ ЯВНО — ровно один случай: окно переезда, когда непустая база
# forgejo есть СРАЗУ В ДВУХ запущенных контейнерах. Автоопределение тогда не
# гадает, а останавливается с ошибкой (выбрать наугад = 50% шанс забэкапить
# труп) и ждёт прямого указания, какой контейнер боевой. Момент, с которого это
# нужно, наступает НЕ на cutover'е, а РАНЬШЕ — с первой же заливки дампа в новый
# кластер (шаг 3a в комментарии к сервису infra-postgres,
# docker-compose.prod.yml): с этой секунды кандидатов двое, и ночной бэкап
# git-хоста иначе просто не снимется. Поэтому значение здесь живёт двумя фазами:
# до cutover'а — боевой ещё старый кластер:
#PG_CONTAINER=gendesign-postgres-1
# после — новый:
#PG_CONTAINER=gendesign-infra-postgres
# Строку УБРАТЬ, как только старый контейнер погашен: дальше автоопределение
# снова однозначно и переживёт любое следующее переименование само.
#
# Явное значение берётся КАК ЕСТЬ, без проверок — это ручной override, и тот,
# кто его выставил, знает про свою инсталляцию больше. Пустое значение
# (PG_CONTAINER=) равносильно незаданному: включается автоопределение.
# Где искать, когда PG_CONTAINER пуст. Имена через пробел; КАВЫЧКИ ОБЯЗАТЕЛЬНЫ —
# файл читается через `source`, и без них второе имя bash попытается выполнить
# как команду. Трогать только если контейнеры называются иначе, чем здесь.
#PG_CONTAINER_CANDIDATES="gendesign-postgres-1 gendesign-infra-postgres"