Прод на Pi: деплой из 2 файлов (compose + .env), ключ туннеля из .env
Теперь на Pi нужны только docker-compose.yml и .env — без репозитория, сборки и файла ключа. - docker-compose.yml: pull_policy: always (образы тянутся из реестра, сборка не запускается); ключ туннеля больше не монтируется томом — берётся из .env (TUNNEL_KEY_B64, base64); у VPS_TUNNEL_HOST убран :? (ломал бы buildx bake на ПК, где переменная не задана) — проверка обязательности перенесена в tunnel.sh (рантайм). - deploy/tunnel/tunnel.sh: приватный ключ из TUNNEL_KEY_B64 (base64) с фолбэком на смонтированный /key/id_tunnel (для dev/test, где репозиторий на хосте). - .env.example: TUNNEL_KEY_B64 + как сгенерировать base64 (Git Bash / PowerShell). - deploy/pi/README: полный поток «голый Pi → 2 файла»: Docker, curl compose+.env из Gitea, up; обновление, автозапуск, бэкап как опция. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+72
-62
@@ -1,89 +1,99 @@
|
||||
# Raspberry Pi — ПРОД (`forbiddenstars.ru`)
|
||||
|
||||
Pi запускает приложение **и туннель** в Docker: `docker compose up` поднимает два сервиса —
|
||||
`app` (FastAPI + SPA) и `tunnel` (ssh-контейнер). У `app` **портов на хост нет**, наружу он
|
||||
доступен только через туннель-контейнер (`ssh -R 9000:app:8000` к VPS). Публичная точка —
|
||||
VPS, домен `forbiddenstars.ru`. Снаружи Pi недоступен (CGNAT) — туннель сам стучится наружу.
|
||||
На Pi нужны только **ДВА файла**: `docker-compose.yml` и `.env`. Репозиторий, сборка и файл
|
||||
ключа туннеля не нужны:
|
||||
- образы (`app` + `tunnel`) тянутся из Gitea-реестра (`pull_policy: always`);
|
||||
- приватный ключ туннеля лежит в `.env` как `TUNNEL_KEY_B64` (base64).
|
||||
|
||||
## 1. Ключ для туннеля
|
||||
Туннель-контейнер ходит на `tunnel@VPS` по ключу из `deploy/tunnel/id_tunnel` (gitignored).
|
||||
Создай ключ и добавь его pubkey в `authorized_keys` пользователя `tunnel` на VPS:
|
||||
`docker compose up` поднимает два контейнера: `app` (FastAPI+SPA, портов на хост нет) и
|
||||
`tunnel` (`ssh -R 9000:app:8000` к VPS). Публичная точка — VPS, домен `forbiddenstars.ru`
|
||||
(Pi за CGNAT, туннель стучится наружу сам).
|
||||
|
||||
> Предпосылки: VPS настроен (Caddy `forbiddenstars.ru` → :9000, юзер `tunnel`) — см.
|
||||
> `deploy/vps/README.md`.
|
||||
|
||||
---
|
||||
|
||||
## A. На ПК (один раз + при каждом релизе)
|
||||
|
||||
**Ключ туннеля и его base64 для `.env`** (один раз):
|
||||
```bash
|
||||
ssh-keygen -t ed25519 -f deploy/tunnel/id_tunnel -N ""
|
||||
ssh-copy-id -i deploy/tunnel/id_tunnel.pub tunnel@186.246.51.17
|
||||
# (или вручную: содержимое deploy/tunnel/id_tunnel.pub -> /home/tunnel/.ssh/authorized_keys на VPS)
|
||||
ssh-copy-id -i deploy/tunnel/id_tunnel.pub tunnel@186.246.51.17 # pubkey → authorized_keys на VPS
|
||||
# base64 приватного ключа БЕЗ переносов строк (значение для TUNNEL_KEY_B64 в .env):
|
||||
# Git Bash / Linux: base64 -w0 deploy/tunnel/id_tunnel
|
||||
# PowerShell: [Convert]::ToBase64String([IO.File]::ReadAllBytes("deploy/tunnel/id_tunnel"))
|
||||
```
|
||||
|
||||
## 2. .env
|
||||
Все настройки приложения и туннеля — в одном `.env` (контейнер форсит `APP_ENV=production`):
|
||||
**Сборка и пуш образов под arm64** (при каждом релизе):
|
||||
```powershell
|
||||
docker login gitea.arseniev.info
|
||||
.\scripts\build-push.ps1 # *nix: bash scripts/build-push.sh
|
||||
```
|
||||
|
||||
## B. На Pi
|
||||
|
||||
**1. Docker (один раз):**
|
||||
```bash
|
||||
cp .env.example .env
|
||||
sudo apt update && sudo apt -y full-upgrade
|
||||
curl -fsSL https://get.docker.com | sudo sh
|
||||
sudo usermod -aG docker $USER
|
||||
sudo systemctl enable --now docker # автозапуск после ребута
|
||||
newgrp docker # применить группу (или перезайти по SSH)
|
||||
```
|
||||
Минимально заполнить на Pi:
|
||||
|
||||
**2. Положить два файла** (любой каталог, напр. `~/forbidden-stars`):
|
||||
```bash
|
||||
mkdir -p ~/forbidden-stars && cd ~/forbidden-stars
|
||||
# docker-compose.yml из Gitea (raw); branch — main (или dev, если ещё не смёржено):
|
||||
curl -fsSLO https://gitea.arseniev.info/NotBigGhost/ForbiddenStarsApp/raw/branch/main/docker-compose.yml
|
||||
# .env: можно взять шаблон оттуда же и заполнить
|
||||
curl -fsSL https://gitea.arseniev.info/NotBigGhost/ForbiddenStarsApp/raw/branch/main/.env.example -o .env
|
||||
nano .env
|
||||
```
|
||||
SECRET_KEY=... # python -c "import secrets;print(secrets.token_urlsafe(48))"
|
||||
Заполнить в `.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
|
||||
VPS_TUNNEL_USER=tunnel
|
||||
VPS_TUNNEL_HOST=186.246.51.17
|
||||
TUNNEL_KEY_B64=... # base64 ключа из шага A (одной строкой)
|
||||
IMAGE_REGISTRY=gitea.arseniev.info/notbigghost # уже значение по умолчанию
|
||||
IMAGE_TAG=latest
|
||||
```
|
||||
`VPS_TUNNEL_PORT` задавать не нужно — прод-контейнер по умолчанию берёт слот **9000**.
|
||||
Ресурсы (`APP_MEM_LIMIT`/`APP_CPUS`) и оффсайт-бэкап (`BACKUP_*`) — по желанию.
|
||||
`APP_ENV` менять не нужно (контейнер форсит `production`); `VPS_TUNNEL_PORT` — тоже (прод → 9000).
|
||||
|
||||
## 3. Запуск (образ из реестра)
|
||||
Образы (`app` + `tunnel`) собираются под arm64 на ПК и пушатся в Gitea-реестр, Pi их тянет
|
||||
(см. `IMAGE_REGISTRY`/`IMAGE_TAG` в `.env`). Pi 4/5 — мощный, но реестр держим как основной путь.
|
||||
|
||||
На ПК (Docker Desktop) — один раз `docker login gitea.arseniev.info`, далее при каждом релизе:
|
||||
```powershell
|
||||
.\scripts\build-push.ps1 # Windows (PowerShell); на *nix — bash scripts/build-push.sh
|
||||
```
|
||||
> `bash scripts/build-push.sh` в PowerShell уходит в WSL и падает, если нет дистрибутива —
|
||||
> используй `.ps1`. Команды внутри — обычный `docker buildx`, работают и напрямую в PowerShell.
|
||||
На Pi:
|
||||
**3. Запуск:**
|
||||
```bash
|
||||
docker login gitea.arseniev.info # один раз (доступ к пакету)
|
||||
git pull # нужны docker-compose.yml, .env, deploy/tunnel/id_tunnel
|
||||
docker compose pull # тянет свежие образы из реестра
|
||||
docker compose up -d
|
||||
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@...
|
||||
```
|
||||
Открой `https://forbiddenstars.ru` — должен отдать прод.
|
||||
|
||||
> Альтернатива без реестра (Pi 4/5 ≥4ГБ потянет сборку): `docker compose up -d --build` прямо на Pi.
|
||||
> Реестр Gitea должен быть по HTTPS с валидным сертификатом; иначе на Pi добавь его в
|
||||
> `/etc/docker/daemon.json` → `insecure-registries`.
|
||||
На старте контейнер сам применит миграции, засидит справочники и создаст админа из `.env`.
|
||||
Открой `https://forbiddenstars.ru`. У @BotFather `/setdomain` → добавь `forbiddenstars.ru`.
|
||||
|
||||
## Обновление
|
||||
```bash
|
||||
# ПК: scripts/build-push.sh
|
||||
# Pi: git pull && docker compose pull && docker compose up -d
|
||||
# ПК: .\scripts\build-push.ps1
|
||||
# Pi: docker compose up -d # always-pull подтянет свежий образ
|
||||
```
|
||||
|
||||
## Бэкап (полный + оффсайт на VPS)
|
||||
`scripts/backup.sh` делает консистентный онлайн-снимок и собирает **полный** архив
|
||||
`fs_<TS>.tar.gz` (БД + `uploads/` + `achievements/`) в `backups/` с ротацией. Если в `.env`
|
||||
задан `BACKUP_VPS_HOST` — копия дополнительно уходит на VPS (оффсайт; Pi → VPS push, т.к. CGNAT).
|
||||
## Автозапуск после перезагрузки
|
||||
Обеспечен: `systemctl enable docker` + `restart: unless-stopped`. После `sudo reboot`
|
||||
контейнеры поднимаются сами (используют локальный образ, без повторного pull).
|
||||
|
||||
Настройка оффсайта (один раз):
|
||||
```bash
|
||||
# 1) отдельный ключ бэкапа + его pubkey на VPS (юзер backup — см. deploy/vps/README.md):
|
||||
ssh-keygen -t ed25519 -f deploy/backup/id_backup -N ""
|
||||
ssh-copy-id -i deploy/backup/id_backup.pub backup@186.246.51.17
|
||||
# 2) в .env заполнить BACKUP_VPS_HOST=186.246.51.17 (по желанию — BACKUP_VPS_USER/DIR/KEEP_*)
|
||||
# 3) разовый прогон и проверка, что архив доехал на VPS:
|
||||
scripts/backup.sh
|
||||
# 4) cron (ежедневно в 04:00):
|
||||
crontab -e
|
||||
# 0 4 * * * /home/pi/ForbidenStarsApp/scripts/backup.sh >> /var/log/fs-backup.log 2>&1
|
||||
```
|
||||
## Бэкап (опционально)
|
||||
Бэкап-скриптам нужен доступ к Docker и к самому `docker-compose.yml` (он уже на Pi). Скопируй
|
||||
рядом ещё `scripts/backup.sh` и `scripts/restore.sh` (или склонируй репозиторий для бэкапов).
|
||||
Затем — отдельный ключ `backup@VPS`, `BACKUP_VPS_HOST` в `.env`, cron. Подробнее — комментарии
|
||||
в `scripts/backup.sh` и `deploy/vps/README.md` §8. Восстановление: `scripts/restore.sh <архив>`.
|
||||
|
||||
Восстановление: `scripts/restore.sh backups/fs_<TS>.tar.gz` (ДЕСТРУКТИВНО — заменяет текущие
|
||||
данные; сначала проверь на тест-контуре). Тома — `db-data` / `uploads-data` / `achievements-data`.
|
||||
|
||||
Логи: `docker compose logs -f app` / `... tunnel`. Если `https://forbiddenstars.ru` отдаёт
|
||||
502 — значит туннель не поднят: смотри `docker compose logs tunnel` (чаще всего ключ не в
|
||||
`authorized_keys` на VPS или не заданы `VPS_TUNNEL_*` в `.env`).
|
||||
## Если что-то не так
|
||||
- `https://forbiddenstars.ru` отдаёт заглушку/502 → туннель не поднят: `docker compose logs tunnel`
|
||||
(чаще: pubkey не в `authorized_keys` на VPS, пустой `TUNNEL_KEY_B64`, или на VPS занят слот 9000
|
||||
→ `sudo fuser -k 9000/tcp` на VPS, затем `docker compose restart tunnel`).
|
||||
- `pull` не проходит → проверь `docker login gitea.arseniev.info` и что реестр по HTTPS с валидным
|
||||
сертификатом (иначе хост в `/etc/docker/daemon.json` → `insecure-registries`, `systemctl restart docker`).
|
||||
|
||||
Reference in New Issue
Block a user