Files

12 KiB
Raw Permalink Blame History

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):
    docker login gitea.arseniev.info
    .\scripts\build-push.ps1
    
    Без этого docker compose up на Pi не найдёт образы в реестре.

1. Система и Docker на Pi

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      # проверка

Лимиты памяти (memory cgroup)

Прошивка Raspberry Pi сама добавляет ядру cgroup_disable=memory. Без контроллера memory Docker молча игнорирует mem_limit из docker-compose.yml (app 512m, tunnel 64m, backup 384m) и на каждый контейнер пишет «Your kernel does not support memory limit capabilities or the cgroup is not mounted. Limitation discarded.». Тогда утечка или тяжёлый бэкап могут занять всю RAM Pi, и OOM-killer прибьёт что попало.

Проверка — если есть вывод, лимиты не работают:

docker info 2>&1 | grep -i "no memory limit"
cat /sys/fs/cgroup/cgroup.controllers         # в списке должно быть слово memory

Включить — дописать параметр в ту же единственную строку cmdline.txt (перевод строки в этом файле ломает загрузку) и перезагрузить Pi. Прод на время перезагрузки недоступен, контейнеры поднимутся сами:

sudo cp /boot/firmware/cmdline.txt /boot/firmware/cmdline.txt.bak
grep -q "cgroup_enable=memory" /boot/firmware/cmdline.txt || \
  sudo sed -i '1 s/$/ cgroup_enable=memory/' /boot/firmware/cmdline.txt
cat /boot/firmware/cmdline.txt                # одна строка, в конце cgroup_enable=memory
sudo reboot

После перезагрузки cgroup.controllers содержит memory, а docker info не пишет No memory limit support. Если Pi не загрузился, верните бэкап: вставьте карту в ПК и на разделе bootfs замените cmdline.txt содержимым cmdline.txt.bak.

2. SSH-ключ для туннеля

Что это. Отдельная пара ключей только для туннеля — ею контейнер tunnel логинится на tunnel@VPS, чтобы открыть ssh -R. Это не системный ключ Pi, ты создаёшь его сам. Распределение:

  • приватный ключ → в .env как TUNNEL_KEY_B64 (base64, одной строкой);
  • публичный (.pub) → в authorized_keys пользователя tunnel на VPS.

Сгенерировать прямо на Pi:

ssh-keygen -t ed25519 -f ~/fs_tunnel -N ""    # создаст ~/fs_tunnel (приватный) и ~/fs_tunnel.pub

Добавить публичный ключ на VPS (Pi ходит наружу — это работает даже за CGNAT):

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:

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:

shred -u ~/fs_tunnel ~/fs_tunnel.pub   # или просто rm

3. Два файла: docker-compose.yml + .env

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.

В 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. Запуск

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.

Обновление

# 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.

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).
  • docker compose up пишет «Your kernel does not support memory limit capabilities… Limitation discarded.» → лимиты памяти не работают: включите memory cgroup, раздел 1, «Лимиты памяти».