176 lines
13 KiB
Markdown
176 lines
13 KiB
Markdown
# Развёртывание открытой альфы rf4spotter.ru
|
||
|
||
Production-контур рассчитан на один Linux-сервер с Docker Compose. Наружу публикуются только Caddy `80/443`; PostgreSQL, FastAPI и MinIO не имеют host-портов. Административные страницы защищены Caddy Basic Auth, административный API — Bearer-токеном FastAPI. API-запросы не требуют Basic: обе схемы используют заголовок Authorization и не могут накладываться на один запрос.
|
||
|
||
Текущее состояние публичных DNS/TLS и незакрытые инфраструктурные действия ведутся в [`docs/deployment-status.md`](../docs/deployment-status.md).
|
||
|
||
## 1. DNS и сервер
|
||
|
||
Создайте A-записи `rf4spotter.ru` и `files.rf4spotter.ru` на публичный IPv4 сервера. При наличии рабочего IPv6 добавьте AAAA для обоих имён. До запуска убедитесь, что извне доступны TCP 80/443 и UDP 443; SSH ограничьте своим IP или VPN. Порты 4321, 8000, 9000, 9001 и 5432 открывать нельзя.
|
||
|
||
Минимум для открытой альфы: 2 vCPU, 4 ГБ RAM и 40 ГБ SSD. Рекомендуется 4 vCPU, 8 ГБ RAM и отдельное внешнее место для резервных копий.
|
||
|
||
## 2. Секреты
|
||
|
||
На сервере:
|
||
|
||
```bash
|
||
cp .env.production.example .env.production
|
||
chmod 600 .env.production
|
||
openssl rand -base64 36 # отдельные значения для БД, ADMIN_TOKEN, RATE_LIMIT_SECRET, MinIO root и S3 app
|
||
docker run --rm caddy:2.10.2-alpine caddy hash-password --plaintext 'ОТДЕЛЬНЫЙ ADMIN-ПАРОЛЬ'
|
||
```
|
||
|
||
Заполните `.env.production`. Хеш Caddy содержит символы `$`, поэтому значение `ADMIN_BASIC_PASSWORD_HASH` в env-файле заключите в одинарные кавычки. `MINIO_ROOT_*` и `S3_*` обязаны быть разными: init-контейнер root-доступом создаёт `S3_BUCKET`, назначает приложению только list/location этого bucket и get/put/delete его объектов, затем проверяет отсутствие глобального list. API получает только application credentials и не может создавать bucket. Если пароль PostgreSQL содержит специальные символы, в `DATABASE_URL` нужна URL-кодированная форма того же пароля. `.env.production` нельзя коммитить или пересылать вместе с логами.
|
||
|
||
## 3. Проверка и первый запуск
|
||
|
||
Перед первым запуском проверьте секреты и итоговую Compose-конфигурацию, не выводя значения в лог:
|
||
|
||
```bash
|
||
./deploy/preflight.sh
|
||
```
|
||
|
||
Перед сборкой запишите текущий `git rev-parse --short HEAD` в `APP_REVISION` файла `.env.production`, чтобы `/ready` однозначно показывал развёрнутый commit.
|
||
|
||
`PUBLIC_CACHE_SECONDS` задаёт короткий in-process кэш публичного агрегата активности (по умолчанию 20 секунд). Не увеличивайте его без повторной проверки свежести после публикации.
|
||
|
||
```bash
|
||
docker compose --env-file .env.production -f compose.production.yaml config --quiet
|
||
docker compose --env-file .env.production -f compose.production.yaml build
|
||
docker compose --env-file .env.production -f compose.production.yaml run --rm migrate
|
||
docker compose --env-file .env.production -f compose.production.yaml up -d --no-deps api web community-scheduler
|
||
docker compose --env-file .env.production -f compose.production.yaml ps
|
||
curl -fsS https://rf4spotter.ru/health
|
||
curl -fsS https://rf4spotter.ru/ready
|
||
```
|
||
|
||
Безопасный диагностический снимок для администратора:
|
||
|
||
```bash
|
||
curl -fsS -H "Authorization: Bearer $ADMIN_TOKEN" -o rf4spotter-diagnostics.json https://rf4spotter.ru/api/v1/admin/diagnostics
|
||
```
|
||
|
||
После обновления DNS и получения сертификатов выполните внешний этап той же проверки:
|
||
|
||
```bash
|
||
./deploy/preflight.sh --online
|
||
```
|
||
|
||
До запуска на сервере можно воспроизвести полный production bootstrap на пустых изолированных volumes. Скрипт собирает образы, применяет миграции, проверяет отсутствие демо-уловов, readiness и отправку заявки:
|
||
|
||
```bash
|
||
./deploy/test-production-bootstrap.sh
|
||
```
|
||
|
||
Отдельный upgrade-drill проверяет путь от предыдущей Alembic-ревизии к текущему head и сохранность контрольных данных:
|
||
|
||
```bash
|
||
./deploy/test-release-upgrade.sh
|
||
```
|
||
|
||
По умолчанию временно используются только loopback-порты `14321` и `18000`; PostgreSQL и MinIO наружу не публикуются. Контур и volumes удаляются после проверки. Drill успешно пройден 6 сентября 2026 года.
|
||
|
||
Одноразовый `migrate` применяет Alembic до rollout API; при ошибке новый runtime не запускается. API при старте выполняет только идемпотентный seed: в production он добавляет минимальные справочники и точки, а демонстрационные уловы жёстко отключены `SEED_DEMO_DATA=false`.
|
||
|
||
Проверка TLS и маршрутизации:
|
||
|
||
```bash
|
||
curl -I https://rf4spotter.ru/
|
||
curl -I https://files.rf4spotter.ru/minio/health/live
|
||
```
|
||
|
||
MinIO health URL допустим для диагностики, но Console наружу не публикуется. Объекты доступны только по временным подписанным ссылкам.
|
||
|
||
## 4. Первичные данные
|
||
|
||
Официальный импорт запускается вручную после успешного readiness:
|
||
|
||
```bash
|
||
docker compose --env-file .env.production -f compose.production.yaml exec api python -m app.cli import-records
|
||
```
|
||
|
||
Автоматический scheduler не входит в production-файл. RF4MAP и RF4 Posts нельзя опрашивать чаще одного раза в 30 минут; до отдельной эксплуатационной задачи используйте только контролируемые ручные запуски и staging.
|
||
|
||
Официальный импорт защищён PostgreSQL advisory lock на комбинацию source/region/category. Параллельный admin-запрос получает `409`, а scheduler записывает безопасный skip и не делает второй HTTP-запрос к источнику.
|
||
|
||
## 5. Обновление
|
||
|
||
```bash
|
||
git pull --ff-only
|
||
docker compose --env-file .env.production -f compose.production.yaml build
|
||
./deploy/backup.sh /srv/rf4-backups
|
||
docker compose --env-file .env.production -f compose.production.yaml run --rm migrate
|
||
docker compose --env-file .env.production -f compose.production.yaml up -d --no-deps api web community-scheduler
|
||
docker compose --env-file .env.production -f compose.production.yaml ps
|
||
curl -fsS https://rf4spotter.ru/ready
|
||
```
|
||
|
||
Перед обновлением со сменой схемы обязателен backup PostgreSQL. Если миграция не началась или схема обратно совместима, верните предыдущий Git commit/images и повторите rollout без downgrade Alembic. Если миграция изменила данные несовместимо, остановите API/web и восстановите сделанный перед release backup через `restore.sh`, затем запустите предыдущую ревизию. Не удаляйте volumes, не используйте `down -v` и не выполняйте `alembic downgrade` без отдельно проверенного плана конкретной миграции.
|
||
|
||
## 6. Резервное копирование и восстановление
|
||
|
||
Храните копии вне диска приложения. Скрипт создаёт PostgreSQL dump, архив MinIO и контрольные суммы в новом каталоге с UTC-временем:
|
||
|
||
```bash
|
||
./deploy/backup.sh /srv/rf4-backups
|
||
```
|
||
|
||
Восстановление заменяет содержимое PostgreSQL и MinIO данными из выбранной копии, временно останавливая API и web. Это намеренно защищённая подтверждением операция:
|
||
|
||
```bash
|
||
CONFIRM_RESTORE=rf4-spotter ./deploy/restore.sh /srv/rf4-backups/20260906T120000Z
|
||
curl -fsS https://rf4spotter.ru/ready
|
||
```
|
||
|
||
Репозиторий содержит изолированный drill, который создаёт отдельные Compose volumes, портит тестовые данные, восстанавливает их и сравнивает PostgreSQL и MinIO:
|
||
|
||
```bash
|
||
./deploy/test-backup-restore.sh
|
||
```
|
||
|
||
Drill успешно пройден 6 сентября 2026 года. На целевом сервере всё равно проведите учебное восстановление с реальной зашифрованной копией перед приглашением пользователей. Затем настройте ежедневный запуск `backup.sh`, выгрузку копий во внешнее хранилище и уведомление при ошибке; храните минимум 7 ежедневных и 4 еженедельных копии.
|
||
|
||
## 7. Ежедневное обслуживание
|
||
|
||
После успешного backup сначала проверьте план очистки, затем примените его:
|
||
|
||
```bash
|
||
docker compose --env-file .env.production -f compose.production.yaml exec -T api python -m app.cli cleanup-retention
|
||
docker compose --env-file .env.production -f compose.production.yaml exec -T api python -m app.cli cleanup-retention --apply
|
||
```
|
||
|
||
Сроки и состав данных описаны в [`docs/data-retention.md`](../docs/data-retention.md). Автоматизацию включайте только после проверки dry-run на рабочем наборе.
|
||
|
||
После ручной проверки установите ежедневный timer. `maintenance.sh` использует lock от параллельного запуска и применяет retention только после успешного backup:
|
||
|
||
```bash
|
||
sudo cp deploy/systemd/rf4spotter-maintenance.service /etc/systemd/system/
|
||
sudo cp deploy/systemd/rf4spotter-maintenance.timer /etc/systemd/system/
|
||
sudo systemctl daemon-reload
|
||
sudo systemctl enable --now rf4spotter-maintenance.timer
|
||
systemctl list-timers rf4spotter-maintenance.timer
|
||
```
|
||
|
||
Шаблон рассчитан на пользователя `rf4spotter`, каталог `/opt/rf4-spotter` и наличие `flock` из `util-linux`. Пользователь должен иметь доступ к Docker socket и каталогу `BACKUP_ROOT`. Результат каждого запуска хранится в systemd journal. Docker JSON-логи production-сервисов ограничены пятью файлами по 10 МБ на контейнер.
|
||
|
||
## 8. Мониторинг
|
||
|
||
После настройки DNS, TLS и первого backup выполните:
|
||
|
||
```bash
|
||
./deploy/monitor.sh
|
||
```
|
||
|
||
Проверка контролирует контейнеры, `/ready`, диск, объём PostgreSQL/MinIO, свежесть backup и срок TLS. Установка systemd timer, пороги и порядок реакции описаны в [`docs/production-monitoring.md`](../docs/production-monitoring.md). До подключения реального канала уведомлений одного журнала systemd недостаточно.
|
||
|
||
## 9. Что ещё блокирует публичное открытие альфы
|
||
|
||
- подключение уведомлений о сбоях мониторинга;
|
||
- проверка DNS/TLS и полного запуска на целевом сервере;
|
||
- внешнее зашифрованное хранилище резервных копий.
|
||
- действующие `privacy@rf4spotter.ru` и `abuse@rf4spotter.ru`, публичные правила и privacy notice;
|
||
- проверка rate limit и очереди модерации под ожидаемой публичной нагрузкой.
|
||
|
||
До закрытия этих пунктов контур можно поднять для технической проверки домена, но не следует открывать форму без ограничения доступа.
|