В prod пароль админа задавался только при первом создании: обычный bootstrap его из .env не берёт, панель не меняет — комментарии отсылали друг к другу. Теперь `python -m app.bootstrap --reset-admin-password` применяет ADMIN_PASSWORD к существующему админу через user_service.set_password: пароль проверяется, token_version растёт, и все админские сессии (в том числе чужие) отзываются. Обычный старт по-прежнему пароль не трогает. Процедура на Pi — в deploy/pi/README.md (правка .env → docker compose up -d → exec команды), кратко — в README. #73 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LqSoRj99iwVEH5U5fnZgsd
157 lines
10 KiB
Markdown
157 lines
10 KiB
Markdown
# Raspberry Pi — ПРОД (`forbiddenstars.ru`)
|
||
|
||
На Pi нужны только **ДВА файла**: `docker-compose.yml` и `.env`. Репозиторий, сборка и файл
|
||
ключа туннеля не нужны:
|
||
- образы (`app` + `tunnel` + `backup`) тянутся из Gitea-реестра (`pull_policy: always`);
|
||
- приватный ключ туннеля лежит в `.env` как `TUNNEL_KEY_B64` (base64).
|
||
|
||
`docker compose up` поднимает три контейнера: `app` (FastAPI+SPA, портов на хост нет),
|
||
`tunnel` (`ssh -R 9000:app:8000` к VPS, стартует после `healthy` у `app`) и `backup` (restic,
|
||
см. раздел «Бэкапы»). Публичная точка — VPS, домен `forbiddenstars.ru` (Pi за CGNAT — туннель
|
||
стучится наружу сам).
|
||
|
||
---
|
||
|
||
## 0. Предпосылки (один раз, не на Pi)
|
||
1. **VPS настроен**: Caddy `forbiddenstars.ru` → `127.0.0.1:9000`, пользователь `tunnel`,
|
||
сертификаты, страница-заглушка — см. `deploy/vps/README.md`.
|
||
2. **Образы собраны и запушены в реестр** (на ПК с Docker Desktop):
|
||
```powershell
|
||
docker login gitea.arseniev.info
|
||
.\scripts\build-push.ps1
|
||
```
|
||
Без этого `docker compose up` на Pi не найдёт образы в реестре.
|
||
|
||
## 1. Система и Docker на Pi
|
||
```bash
|
||
ssh pi@<ip-пая>
|
||
sudo apt update && sudo apt -y full-upgrade
|
||
sudo apt -y install curl openssh-client # base64/ssh-keygen уже есть в системе
|
||
|
||
curl -fsSL https://get.docker.com | sudo sh # Docker Engine + compose-плагин (arm64)
|
||
sudo usermod -aG docker $USER
|
||
sudo systemctl enable --now docker # автозапуск после ребута
|
||
newgrp docker # применить группу (или перезайти по SSH)
|
||
docker version && docker compose version # проверка
|
||
```
|
||
|
||
## 2. SSH-ключ для туннеля
|
||
**Что это.** Отдельная пара ключей **только для туннеля** — ею контейнер `tunnel` логинится на
|
||
`tunnel@VPS`, чтобы открыть `ssh -R`. Это не системный ключ Pi, ты создаёшь его сам. Распределение:
|
||
- **приватный** ключ → в `.env` как `TUNNEL_KEY_B64` (base64, одной строкой);
|
||
- **публичный** (`.pub`) → в `authorized_keys` пользователя `tunnel` на VPS.
|
||
|
||
Сгенерировать прямо на Pi:
|
||
```bash
|
||
ssh-keygen -t ed25519 -f ~/fs_tunnel -N "" # создаст ~/fs_tunnel (приватный) и ~/fs_tunnel.pub
|
||
```
|
||
Добавить публичный ключ на VPS (Pi ходит наружу — это работает даже за CGNAT):
|
||
```bash
|
||
ssh-copy-id -i ~/fs_tunnel.pub tunnel@186.246.51.17
|
||
# Если у tunnel нет пароля (только ключ) — добавь вручную через свой админ-доступ к VPS:
|
||
# cat ~/fs_tunnel.pub # скопируй строку
|
||
# на VPS: echo '<строка>' >> /home/tunnel/.ssh/authorized_keys
|
||
```
|
||
Закодировать приватный ключ в base64 **одной строкой** — это значение для `TUNNEL_KEY_B64`:
|
||
```bash
|
||
base64 -w0 ~/fs_tunnel; echo # выведет длинную строку без переносов — скопируй её целиком
|
||
```
|
||
> Альтернатива: если ключ уже есть на ПК (`deploy/tunnel/id_tunnel`) и его pubkey уже на VPS —
|
||
> не плоди новый, закодируй тот:
|
||
> `[Convert]::ToBase64String([IO.File]::ReadAllBytes((Resolve-Path "deploy\tunnel\id_tunnel")))`
|
||
|
||
После того как base64 вставлен в `.env`, файлы ключа на Pi можно удалить — ключ теперь в `.env`,
|
||
а pubkey уже на VPS:
|
||
```bash
|
||
shred -u ~/fs_tunnel ~/fs_tunnel.pub # или просто rm
|
||
```
|
||
|
||
## 3. Два файла: docker-compose.yml + .env
|
||
```bash
|
||
mkdir -p ~/forbidden-stars && cd ~/forbidden-stars
|
||
# branch — main (или dev, если ещё не смёржено в main):
|
||
curl -fsSLO https://gitea.arseniev.info/NotBigGhost/ForbiddenStarsApp/raw/branch/main/docker-compose.yml
|
||
curl -fsSL https://gitea.arseniev.info/NotBigGhost/ForbiddenStarsApp/raw/branch/main/.env.example -o .env
|
||
nano .env
|
||
```
|
||
Заполнить в `.env`:
|
||
```
|
||
SECRET_KEY=... # python3 -c "import secrets;print(secrets.token_urlsafe(48))"
|
||
ADMIN_USERNAME=... # логин/пароль секретной админки
|
||
ADMIN_PASSWORD=...
|
||
TELEGRAM_BOT_TOKEN=... # бот @BotFather; затем /setdomain → forbiddenstars.ru
|
||
TELEGRAM_BOT_USERNAME=...
|
||
VPS_TUNNEL_HOST=186.246.51.17
|
||
TUNNEL_KEY_B64=... # длинная строка base64 из шага 2 (целиком, одной строкой)
|
||
IMAGE_REGISTRY=gitea.arseniev.info/notbigghost # уже значение по умолчанию
|
||
IMAGE_TAG=latest
|
||
```
|
||
`APP_ENV` (форсится в `production`) и `VPS_TUNNEL_PORT` (прод → 9000) не трогать.
|
||
Блок `BACKUP_*` можно оставить пустым: бэкапы включаются позже, по
|
||
[`deploy/backup/README.md`](../backup/README.md).
|
||
|
||
> В production приложение **не стартует**, если `SECRET_KEY` дефолтный или короче 32
|
||
> символов, а `ADMIN_PASSWORD` пустой или дефолтный (при `ADMIN_BOOTSTRAP_ENABLED=true`).
|
||
> `ADMIN_USERNAME`/`ADMIN_PASSWORD` применяются автоматически **только при первом создании**
|
||
> админа: если потом поменять их в `.env`, у существующего админа само ничего не изменится.
|
||
>
|
||
> **Смена пароля админа** (плановая или при утечке) — осознанной командой, через панель
|
||
> его не сменить:
|
||
> 1. поменять `ADMIN_PASSWORD` в `.env` на Pi;
|
||
> 2. `docker compose up -d` — пересоздаст `app` с новым `.env` (контейнер читает `.env`
|
||
> только при создании; без этого шага команда ниже увидит старый пароль);
|
||
> 3. `docker compose exec app python -m app.bootstrap --reset-admin-password`.
|
||
>
|
||
> Все админские сессии, в том числе чужие, если пароль утёк, после этого завершаются.
|
||
> Логин (`ADMIN_USERNAME`) команда не меняет.
|
||
|
||
## 4. Запуск
|
||
```bash
|
||
docker login gitea.arseniev.info # один раз, доступ к реестру
|
||
docker compose up -d # pull_policy: always → тянет образы из реестра, без сборки
|
||
docker compose ps # app healthy → поднимется tunnel
|
||
docker compose logs -f tunnel # ждём строку: [tunnel] -R 9000:app:8000 -> tunnel@...
|
||
```
|
||
На старте контейнер сам применит миграции, засидит справочники и создаст админа из `.env`
|
||
(если админа ещё нет).
|
||
|
||
## 5. Проверка
|
||
- Открой `https://forbiddenstars.ru` — должно отдать приложение (не заглушку).
|
||
- У @BotFather `/setdomain` → добавь `forbiddenstars.ru` (иначе Telegram-вход не заработает).
|
||
- Админка: удержать «Меню» 10 с → `/admin/login`, войти логином/паролём из `.env`.
|
||
|
||
## Обновление
|
||
```bash
|
||
# Pi: docker compose exec backup fs-backup run --tag before-update # по желанию, если бэкапы включены
|
||
# ПК: .\scripts\build-push.ps1
|
||
# Pi: docker compose up -d # always-pull подтянет свежие образы
|
||
```
|
||
Если в новой версии менялся `docker-compose.yml` или `.env.example`, сначала скачайте
|
||
свежий `docker-compose.yml` (шаг 3) и допишите новые переменные в `.env`.
|
||
|
||
## Автозапуск после перезагрузки
|
||
Уже обеспечен: `systemctl enable docker` + `restart: unless-stopped`. После `sudo reboot`
|
||
контейнеры поднимутся сами (используют локальный образ, без повторного pull).
|
||
|
||
## Бэкапы
|
||
Отдельный контейнер `backup` в том же `docker-compose.yml`: каждую ночь делает зашифрованный
|
||
снимок БД, `uploads` и `achievements` на Pi и на VPS. Дополнительных файлов на Pi не нужно,
|
||
всё настраивается блоком `BACKUP_*` в `.env`. Пока `BACKUP_PASSWORD` пуст, бэкапы выключены.
|
||
|
||
Пошаговая настройка, восстановление и действия при гибели Pi —
|
||
[`deploy/backup/README.md`](../backup/README.md).
|
||
|
||
```bash
|
||
docker compose exec backup fs-backup status # состояние
|
||
docker compose exec backup fs-backup list # хронология снимков
|
||
```
|
||
|
||
> Не выполняйте `docker compose down -v`: флаг `-v` удаляет тома с данными и локальными бэкапами.
|
||
|
||
## Если что-то не так
|
||
- `https://forbiddenstars.ru` отдаёт заглушку/502 → туннель не поднят: `docker compose logs tunnel`
|
||
(чаще: pubkey не в `authorized_keys` на VPS, пустой/битый `TUNNEL_KEY_B64`, либо на VPS занят
|
||
слот 9000 → на VPS `sudo fuser -k 9000/tcp`, затем `docker compose restart tunnel`).
|
||
- `pull` не проходит → проверь `docker login gitea.arseniev.info` и что реестр по HTTPS с валидным
|
||
сертификатом (иначе хост в `/etc/docker/daemon.json` → `insecure-registries`, `systemctl restart docker`).
|