Files
ForbiddenStarsApp/deploy/README.md
T
NotBigGhostandClaude Opus 5 63fd90e017 Документация: сверка README и deploy/*.md с кодом
- README: test без порта на хосте (только через домен, Secure-cookie), прод из реестра
  (build-push + docker compose up -d), лимиты перебора и регистраций, dev_admin.py в списке
  dev-кода, структура репозитория, раздел о бэкапах, отличия test от prod; отмечены
  известные проблемы (#69, #71, #72, #73).
- deploy/README.md: три сервиса (app + tunnel + backup), источники ключа туннеля для Pi,
  test и dev-туннеля, слот 9000 у временного прода.
- deploy/pi/README.md: контейнер backup, fail-fast по секретам, ADMIN_PASSWORD только при
  первом создании админа (#73), порядок обновления.
- deploy/vps/README.md: туннель-контейнер вместо autossh, сниппет (edge), единые имена
  файлов в примере сборки сертификатов, дописывать authorized_keys через >>.
- deploy/backup/README.md: первый бэкап на новом Pi, выбор снимка с данными при
  восстановлении (#74), метка keep, --no-pre-restore, служебные команды, причины unhealthy.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LqSoRj99iwVEH5U5fnZgsd
2026-09-14 20:06:54 +03:00

76 lines
6.7 KiB
Markdown
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.
# Публикация: домены, VPS, туннели
Приложение крутится дома (Pi — прод) и на твоём ПК (dev/test). Дома белого IP нет
(CGNAT), поэтому наружу выставляем через **VPS-привратник**: на нём Caddy терминирует
HTTPS твоими сертификатами и проксирует трафик в SSH reverse-туннели, которые
приложение само открывает к VPS.
```
┌──────────── VPS 186.246.51.17 (Caddy) ───────────┐
forbiddenstars.ru ───►│ :443 (cert твой) → 127.0.0.1:9000 │
│ ▲ туннель-КОНТЕЙНЕР │
│ └── Pi : app:8000 PROD │
forbidden-stars.ru ──►│ :443 (cert твой) → 127.0.0.1:9001 │
│ ▲ контейнер (test) ИЛИ │
│ ▲ ssh с ПК (dev) │
│ ├── ПК test : app:8000 │
│ └── ПК dev : vite:5173 │
└───────────────────────────────────────────────────┘
```
- **PROD** — Pi. `docker compose up -d` поднимает три сервиса: `app` + `tunnel` + `backup`
(образы из Gitea-реестра, собираются на ПК `scripts/build-push.ps1`). У `app` **портов на
хост нет** — наружу его выставляет только туннель-контейнер (`ssh -R 9000:app:8000` к VPS).
Работает постоянно: при обрыве `ssh` завершается, и Docker перезапускает контейнер
(`restart: unless-stopped`). См. [`pi/`](pi/README.md).
- **TEST** — ПК. То же самое: `docker compose -f docker-compose.test.yml up` собирает образы
локально и поднимает `app` + `tunnel` (`ssh -R 9001:app:8000`) + `backup` (без расписания и
без VPS). Портов на хост нет — тест виден только на `forbidden-stars.ru`. Обычно
запускается лаунчером при `APP_ENV=test`.
- **DEV** — ПК, нативно (`uvicorn`+`vite`). По умолчанию только на localhost; при
`LOCAL_PUBLIC=vps` лаунчер (`run.ps1` / `run.sh`) дополнительно поднимает SSH-туннель с ПК
(`ssh -R 9001:localhost:5173`) → дев виден на `forbidden-stars.ru`.
- DEV и TEST делят слот **9001** (`forbidden-stars.ru`) → поднимай что-то **одно за раз**.
PROD на отдельном слоте **9000** (`forbiddenstars.ru`) — работает независимо.
Временный прод на ПК (`docker-compose.temp.yml`) тоже занимает **9000** — одновременно с Pi нельзя.
Ключи туннеля (приватные, в git не идут; публичные части — в `authorized_keys` пользователя
`tunnel` на VPS):
- **Pi** — `TUNNEL_KEY_B64` (base64 приватного ключа) в `.env`; файла ключа на Pi нет.
- **ПК, test и временный прод** — файл `deploy/tunnel/id_tunnel`, монтируется в туннель-контейнер.
- **ПК, dev** — `run.ps1`/`run.sh` зовут системный `ssh` без `-i`, то есть с ключом по
умолчанию из `~/.ssh`. Он должен быть в `authorized_keys` (можно тем же, что `id_tunnel`).
Настройка по шагам:
1. **VPS** — [`vps/README.md`](vps/README.md): Caddy, файрвол, пользователь `tunnel`, сертификаты, `Caddyfile`.
2. **Pi (прод)** — [`pi/README.md`](pi/README.md): ключ туннеля в `TUNNEL_KEY_B64`, `.env`, `docker compose up -d`.
3. **ПК (dev/test)** — ключ в `deploy/tunnel/id_tunnel` (для test-контейнера) и/или ключ по
умолчанию в `~/.ssh` (для dev-туннеля); pubkey — в `authorized_keys` у `tunnel@VPS`.
4. **Бэкапы** — [`backup/README.md`](backup/README.md): контейнер `backup` (restic) делает
снимки на Pi и на VPS (`fsbackup@VPS`, только SFTP), скрипты ПК скачивают их и проверяют
восстановление на тест-клоне.
Секреты не в git: сертификаты/ключи (`*.pem`, `*.key`, `id_tunnel*`, `id_backup*`) живут на
VPS/Pi/ПК, в репозитории только `Caddyfile`, страница-заглушка, образы `deploy/tunnel/` и
`deploy/backup/` и шаблоны.
> Telegram-вход требует HTTPS-домен: у BotFather `/setdomain` укажи оба домена
> (`forbiddenstars.ru` и `forbidden-stars.ru`).
## Реал-тайм (SSE)
Приложение шлёт события «данные изменились» через SSE: `GET /api/events`
(`text/event-stream`). Это обычный HTTP — проходит через Caddy и SSH-туннель без доп.
настроек, КРОМЕ одного: для пути `/api/events` в `Caddyfile` выделен отдельный `handle`
**без `encode`** и с **`flush_interval -1`** (иначе сжатие/буферизация задержат поток).
После правки `Caddyfile` на VPS: `sudo systemctl reload caddy` (или `restart`).
Шина событий — **in-memory**, рассчитана на один процесс (uvicorn `--workers 1`, как в
контейнере). Если когда-нибудь поднимешь несколько воркеров/реплик — шину нужно вынести во
внешний брокер (Redis pub/sub), иначе события увидит только тот воркер, что принял мутацию.
SSE-поток сам не закрывается, поэтому uvicorn запускается с `--timeout-graceful-shutdown`
(10 с в `entrypoint.sh`, 2 с в `run.*`). Без него остановка ждёт закрытия всех соединений: в dev
`--reload` при открытой вкладке висит вечно, в контейнере остановку обрывает только SIGKILL
по `stop_grace_period`.