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
Переезд ломает сам 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
80 lines
6.3 KiB
Text
80 lines
6.3 KiB
Text
# 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"
|