Доки Pi: полная инструкция с нуля под деплой из 2 файлов; ключ туннеля генерируется на Pi
- deploy/pi/README: пошагово от чистого Pi (Docker → SSH-ключ туннеля на самом Pi → 2 файла → up), с объяснением назначения ключа и кодирования в base64 (TUNNEL_KEY_B64). - .env.example/README: PowerShell-команда base64 через Resolve-Path (абсолютный путь — иначе [IO.File]::ReadAllBytes искал относительно домашнего каталога .NET и падал). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+1
-1
@@ -31,7 +31,7 @@ VPS_TUNNEL_USER=tunnel
|
|||||||
# (без файла deploy/tunnel/id_tunnel). Нужен ТОЛЬКО для прод-контейнера на Pi; для dev/test
|
# (без файла deploy/tunnel/id_tunnel). Нужен ТОЛЬКО для прод-контейнера на Pi; для dev/test
|
||||||
# на ПК ключ берётся из файла. Сгенерируй ключ на ПК и закодируй БЕЗ переносов строк:
|
# на ПК ключ берётся из файла. Сгенерируй ключ на ПК и закодируй БЕЗ переносов строк:
|
||||||
# Git Bash / Linux: base64 -w0 deploy/tunnel/id_tunnel
|
# Git Bash / Linux: base64 -w0 deploy/tunnel/id_tunnel
|
||||||
# PowerShell: [Convert]::ToBase64String([IO.File]::ReadAllBytes("deploy/tunnel/id_tunnel"))
|
# PowerShell: [Convert]::ToBase64String([IO.File]::ReadAllBytes((Resolve-Path "deploy/tunnel/id_tunnel")))
|
||||||
# Pubkey (deploy/tunnel/id_tunnel.pub) добавь в authorized_keys у tunnel@VPS.
|
# Pubkey (deploy/tunnel/id_tunnel.pub) добавь в authorized_keys у tunnel@VPS.
|
||||||
TUNNEL_KEY_B64=
|
TUNNEL_KEY_B64=
|
||||||
|
|
||||||
|
|||||||
+68
-43
@@ -7,47 +7,69 @@
|
|||||||
|
|
||||||
`docker compose up` поднимает два контейнера: `app` (FastAPI+SPA, портов на хост нет) и
|
`docker compose up` поднимает два контейнера: `app` (FastAPI+SPA, портов на хост нет) и
|
||||||
`tunnel` (`ssh -R 9000:app:8000` к VPS). Публичная точка — VPS, домен `forbiddenstars.ru`
|
`tunnel` (`ssh -R 9000:app:8000` к VPS). Публичная точка — VPS, домен `forbiddenstars.ru`
|
||||||
(Pi за CGNAT, туннель стучится наружу сам).
|
(Pi за CGNAT — туннель стучится наружу сам).
|
||||||
|
|
||||||
> Предпосылки: VPS настроен (Caddy `forbiddenstars.ru` → :9000, юзер `tunnel`) — см.
|
|
||||||
> `deploy/vps/README.md`.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## A. На ПК (один раз + при каждом релизе)
|
## 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 не найдёт образы в реестре.
|
||||||
|
|
||||||
**Ключ туннеля и его base64 для `.env`** (один раз):
|
## 1. Система и Docker на Pi
|
||||||
```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 # 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"))
|
|
||||||
```
|
|
||||||
|
|
||||||
**Сборка и пуш образов под arm64** (при каждом релизе):
|
|
||||||
```powershell
|
|
||||||
docker login gitea.arseniev.info
|
|
||||||
.\scripts\build-push.ps1 # *nix: bash scripts/build-push.sh
|
|
||||||
```
|
|
||||||
|
|
||||||
## B. На Pi
|
|
||||||
|
|
||||||
**1. Docker (один раз):**
|
|
||||||
```bash
|
```bash
|
||||||
|
ssh pi@<ip-пая>
|
||||||
sudo apt update && sudo apt -y full-upgrade
|
sudo apt update && sudo apt -y full-upgrade
|
||||||
curl -fsSL https://get.docker.com | sudo sh
|
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 usermod -aG docker $USER
|
||||||
sudo systemctl enable --now docker # автозапуск после ребута
|
sudo systemctl enable --now docker # автозапуск после ребута
|
||||||
newgrp docker # применить группу (или перезайти по SSH)
|
newgrp docker # применить группу (или перезайти по SSH)
|
||||||
|
docker version && docker compose version # проверка
|
||||||
```
|
```
|
||||||
|
|
||||||
**2. Положить два файла** (любой каталог, напр. `~/forbidden-stars`):
|
## 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
|
```bash
|
||||||
mkdir -p ~/forbidden-stars && cd ~/forbidden-stars
|
mkdir -p ~/forbidden-stars && cd ~/forbidden-stars
|
||||||
# docker-compose.yml из Gitea (raw); branch — main (или dev, если ещё не смёржено):
|
# branch — main (или dev, если ещё не смёржено в main):
|
||||||
curl -fsSLO https://gitea.arseniev.info/NotBigGhost/ForbiddenStarsApp/raw/branch/main/docker-compose.yml
|
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
|
curl -fsSL https://gitea.arseniev.info/NotBigGhost/ForbiddenStarsApp/raw/branch/main/.env.example -o .env
|
||||||
nano .env
|
nano .env
|
||||||
```
|
```
|
||||||
@@ -56,24 +78,28 @@ nano .env
|
|||||||
SECRET_KEY=... # python3 -c "import secrets;print(secrets.token_urlsafe(48))"
|
SECRET_KEY=... # python3 -c "import secrets;print(secrets.token_urlsafe(48))"
|
||||||
ADMIN_USERNAME=... # логин/пароль секретной админки
|
ADMIN_USERNAME=... # логин/пароль секретной админки
|
||||||
ADMIN_PASSWORD=...
|
ADMIN_PASSWORD=...
|
||||||
TELEGRAM_BOT_TOKEN=... # бот @BotFather; /setdomain → forbiddenstars.ru
|
TELEGRAM_BOT_TOKEN=... # бот @BotFather; затем /setdomain → forbiddenstars.ru
|
||||||
TELEGRAM_BOT_USERNAME=...
|
TELEGRAM_BOT_USERNAME=...
|
||||||
VPS_TUNNEL_HOST=186.246.51.17
|
VPS_TUNNEL_HOST=186.246.51.17
|
||||||
TUNNEL_KEY_B64=... # base64 ключа из шага A (одной строкой)
|
TUNNEL_KEY_B64=... # длинная строка base64 из шага 2 (целиком, одной строкой)
|
||||||
IMAGE_REGISTRY=gitea.arseniev.info/notbigghost # уже значение по умолчанию
|
IMAGE_REGISTRY=gitea.arseniev.info/notbigghost # уже значение по умолчанию
|
||||||
IMAGE_TAG=latest
|
IMAGE_TAG=latest
|
||||||
```
|
```
|
||||||
`APP_ENV` менять не нужно (контейнер форсит `production`); `VPS_TUNNEL_PORT` — тоже (прод → 9000).
|
`APP_ENV` (форсится в `production`) и `VPS_TUNNEL_PORT` (прод → 9000) не трогать.
|
||||||
|
|
||||||
**3. Запуск:**
|
## 4. Запуск
|
||||||
```bash
|
```bash
|
||||||
docker login gitea.arseniev.info # один раз (доступ к реестру)
|
docker login gitea.arseniev.info # один раз, доступ к реестру
|
||||||
docker compose up -d # pull_policy: always → сам тянет образы, без сборки
|
docker compose up -d # pull_policy: always → тянет образы из реестра, без сборки
|
||||||
docker compose ps # app healthy → поднимется tunnel
|
docker compose ps # app healthy → поднимется tunnel
|
||||||
docker compose logs -f tunnel # ждём: [tunnel] -R 9000:app:8000 -> tunnel@...
|
docker compose logs -f tunnel # ждём строку: [tunnel] -R 9000:app:8000 -> tunnel@...
|
||||||
```
|
```
|
||||||
На старте контейнер сам применит миграции, засидит справочники и создаст админа из `.env`.
|
На старте контейнер сам применит миграции, засидит справочники и создаст админа из `.env`.
|
||||||
Открой `https://forbiddenstars.ru`. У @BotFather `/setdomain` → добавь `forbiddenstars.ru`.
|
|
||||||
|
## 5. Проверка
|
||||||
|
- Открой `https://forbiddenstars.ru` — должно отдать приложение (не заглушку).
|
||||||
|
- У @BotFather `/setdomain` → добавь `forbiddenstars.ru` (иначе Telegram-вход не заработает).
|
||||||
|
- Админка: удержать «Меню» 10 с → `/admin/login`, войти логином/паролём из `.env`.
|
||||||
|
|
||||||
## Обновление
|
## Обновление
|
||||||
```bash
|
```bash
|
||||||
@@ -82,18 +108,17 @@ docker compose logs -f tunnel # ждём: [tunnel] -R 9000:app:8000 -> tu
|
|||||||
```
|
```
|
||||||
|
|
||||||
## Автозапуск после перезагрузки
|
## Автозапуск после перезагрузки
|
||||||
Обеспечен: `systemctl enable docker` + `restart: unless-stopped`. После `sudo reboot`
|
Уже обеспечен: `systemctl enable docker` + `restart: unless-stopped`. После `sudo reboot`
|
||||||
контейнеры поднимаются сами (используют локальный образ, без повторного pull).
|
контейнеры поднимутся сами (используют локальный образ, без повторного pull).
|
||||||
|
|
||||||
## Бэкап (опционально)
|
## Бэкап (опционально)
|
||||||
Бэкап-скриптам нужен доступ к Docker и к самому `docker-compose.yml` (он уже на Pi). Скопируй
|
Бэкап-скриптам нужен сам `docker-compose.yml` (он на Pi) + скрипты. Скопируй рядом
|
||||||
рядом ещё `scripts/backup.sh` и `scripts/restore.sh` (или склонируй репозиторий для бэкапов).
|
`scripts/backup.sh` и `scripts/restore.sh`, настрой `BACKUP_*` в `.env` и cron. Подробно —
|
||||||
Затем — отдельный ключ `backup@VPS`, `BACKUP_VPS_HOST` в `.env`, cron. Подробнее — комментарии
|
комментарии в `scripts/backup.sh` и `deploy/vps/README.md` §8.
|
||||||
в `scripts/backup.sh` и `deploy/vps/README.md` §8. Восстановление: `scripts/restore.sh <архив>`.
|
|
||||||
|
|
||||||
## Если что-то не так
|
## Если что-то не так
|
||||||
- `https://forbiddenstars.ru` отдаёт заглушку/502 → туннель не поднят: `docker compose logs tunnel`
|
- `https://forbiddenstars.ru` отдаёт заглушку/502 → туннель не поднят: `docker compose logs tunnel`
|
||||||
(чаще: pubkey не в `authorized_keys` на VPS, пустой `TUNNEL_KEY_B64`, или на VPS занят слот 9000
|
(чаще: pubkey не в `authorized_keys` на VPS, пустой/битый `TUNNEL_KEY_B64`, либо на VPS занят
|
||||||
→ `sudo fuser -k 9000/tcp` на VPS, затем `docker compose restart tunnel`).
|
слот 9000 → на VPS `sudo fuser -k 9000/tcp`, затем `docker compose restart tunnel`).
|
||||||
- `pull` не проходит → проверь `docker login gitea.arseniev.info` и что реестр по HTTPS с валидным
|
- `pull` не проходит → проверь `docker login gitea.arseniev.info` и что реестр по HTTPS с валидным
|
||||||
сертификатом (иначе хост в `/etc/docker/daemon.json` → `insecure-registries`, `systemctl restart docker`).
|
сертификатом (иначе хост в `/etc/docker/daemon.json` → `insecure-registries`, `systemctl restart docker`).
|
||||||
|
|||||||
Reference in New Issue
Block a user