Files
ForbiddenStarsApp/docker-compose.yml
T
NotBigGhostandClaude Opus 5 5b89dc9d2f Бэкапы: отчёты и команды в Telegram-боте
Каждый run и verify шлёт в чаты BACKUP_TELEGRAM_CHAT_ID одно сообщение: успех
со сводкой (игроки/партии, по репозиторию снимок, число снимков, размер) или
сбой с причиной — в том числе ранний отказ (нет пароля, занят lock, битая БД):
обработчик EXIT берёт текст последнего die. Недоступный Telegram бэкап не ломает.

Бот-слушатель (fs-backup telegram bot, long polling, стартует из entrypoint в
фоне) отвечает на /backups (хранящиеся снимки), /status и /help только чатам из
белого списка; чужие chat id пишет в журнал. telegram chats — узнать свой id,
telegram test — проверить связь. Токен по умолчанию — TELEGRAM_BOT_TOKEN
приложения (тот же бот), BACKUP_TELEGRAM_BOT_TOKEN переопределяет. В образ
добавлен curl. Настройка — deploy/backup/README.md, раздел 10. #83

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LqSoRj99iwVEH5U5fnZgsd
2026-09-19 02:38:49 +03:00

124 lines
7.5 KiB
YAML
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.
# ─────────────────────────────────────────────────────────────────────────────
# ПРОД на Raspberry Pi (forbiddenstars.ru) — единый compose: приложение + туннель.
# На Pi нужны только ДВА файла: этот docker-compose.yml и .env. Репозиторий, сборка и
# файл ключа не нужны: образы тянутся из реестра (pull_policy: always), а приватный ключ
# туннеля лежит в .env (TUNNEL_KEY_B64, base64). build: оставлен для сборки/пуша на ПК
# (scripts/build-push.*) — на Pi он не используется.
#
# Запуск/обновление: docker compose up -d (сам тянет свежие образы)
# Логи: docker compose logs -f app (или: ... tunnel / backup)
# Остановка: docker compose down (данные в томах сохраняются)
# Бэкапы: docker compose exec backup fs-backup status (help — все команды)
# инструкция: deploy/backup/README.md
#
# НИКОГДА не выполняйте на проде `docker compose down -v`: флаг -v удаляет тома — и данные
# приложения, и локальный репозиторий бэкапов (останется только копия на VPS).
#
# Контейнер ВСЕГДА production: APP_ENV форсится здесь и игнорирует значение из .env.
# Порты на хост НЕ публикуются — наружу приложение выставляет только сервис tunnel
# (SSH reverse-туннель к VPS). Из LAN/localhost оно недоступно.
# ─────────────────────────────────────────────────────────────────────────────
services:
app:
build: .
# Образ из реестра (Gitea): собирается под arm64 на ПК (scripts/build-push.sh) и тянется
# на Pi через `docker compose pull`. build: оставлен как локальный фолбэк (сборка на Pi).
image: ${IMAGE_REGISTRY:-gitea.arseniev.info/notbigghost}/forbidden-stars:${IMAGE_TAG:-latest}
pull_policy: always # на Pi всегда тянем образ из реестра (без сборки)
restart: unless-stopped
init: true # PID 1 пробрасывает сигналы → корректный SIGTERM
stop_grace_period: 30s # время на чек-пойнт WAL SQLite перед остановкой
env_file:
- .env # единый источник настроек/секретов всего приложения
environment:
APP_ENV: production # прод обособлен: игнорируем APP_ENV из .env
volumes:
- db-data:/data # БД SQLite + WAL-сайдкары
- uploads-data:/data/uploads # медиа партий
- achievements-data:/data/achievements # определения титулов (файлы)
healthcheck:
test: ["CMD", "python", "-c", "import urllib.request; urllib.request.urlopen('http://localhost:8000/api/health')"]
interval: 30s
timeout: 5s
retries: 3
start_period: 40s
mem_limit: ${APP_MEM_LIMIT:-512m} # лимиты под Pi; переопределяются в .env
cpus: ${APP_CPUS:-1.5}
security_opt:
- no-new-privileges:true
logging:
driver: json-file
options:
max-size: "10m"
max-file: "5"
# SSH reverse-туннель: VPS:${VPS_TUNNEL_PORT:-9000} → app:8000 (домен forbiddenstars.ru).
# Приватный ключ — в .env как TUNNEL_KEY_B64 (base64); pubkey — в authorized_keys у tunnel@VPS.
tunnel:
build: ./deploy/tunnel
image: ${IMAGE_REGISTRY:-gitea.arseniev.info/notbigghost}/forbidden-stars-tunnel:${IMAGE_TAG:-latest}
pull_policy: always
restart: unless-stopped
init: true
depends_on:
app:
condition: service_healthy
environment:
VPS_TUNNEL_HOST: ${VPS_TUNNEL_HOST} # обязателен; tunnel.sh упадёт с ошибкой, если пуст
VPS_TUNNEL_USER: ${VPS_TUNNEL_USER:-tunnel}
VPS_TUNNEL_PORT: ${VPS_TUNNEL_PORT:-9000} # прод-слот (forbiddenstars.ru)
UPSTREAM: app:8000
TUNNEL_KEY_B64: ${TUNNEL_KEY_B64:-} # приватный ключ туннеля (base64), из .env
mem_limit: 64m
security_opt:
- no-new-privileges:true
# Бэкапы (restic): снимки по расписанию в локальный репозиторий (том backup-data) и на VPS
# (SFTP, если задан BACKUP_VPS_HOST). От app не зависит и app не мешает. Переменные —
# только BACKUP_* (секреты приложения сюда не передаются; исключение — токен бота для
# отчётов в Telegram). Всё про настройку и восстановление — deploy/backup/README.md.
backup:
build: ./deploy/backup
image: ${IMAGE_REGISTRY:-gitea.arseniev.info/notbigghost}/forbidden-stars-backup:${IMAGE_TAG:-latest}
pull_policy: always
restart: unless-stopped
environment:
BACKUP_PASSWORD: ${BACKUP_PASSWORD:-} # пароль шифрования; пусто = бэкапы отключены
BACKUP_HOSTNAME: fs-prod # имя хоста в снимках
BACKUP_SCHEDULE: ${BACKUP_SCHEDULE:-0 4 * * *}
BACKUP_VERIFY_SCHEDULE: ${BACKUP_VERIFY_SCHEDULE:-30 5 * * 0}
BACKUP_KEEP_DAILY: ${BACKUP_KEEP_DAILY:-14}
BACKUP_KEEP_WEEKLY: ${BACKUP_KEEP_WEEKLY:-8}
BACKUP_KEEP_MONTHLY: ${BACKUP_KEEP_MONTHLY:-12}
BACKUP_COMPRESSION: ${BACKUP_COMPRESSION:-max}
BACKUP_MAX_AGE_HOURS: ${BACKUP_MAX_AGE_HOURS:-30}
BACKUP_VPS_HOST: ${BACKUP_VPS_HOST:-}
BACKUP_VPS_USER: ${BACKUP_VPS_USER:-fsbackup}
BACKUP_VPS_PORT: ${BACKUP_VPS_PORT:-22}
BACKUP_VPS_DIR: ${BACKUP_VPS_DIR:-/srv/fs-backups/restic}
BACKUP_SSH_KEY_B64: ${BACKUP_SSH_KEY_B64:-}
# Отчёты и бот в Telegram (#83). Единственное исключение из «секреты приложения сюда
# не передаются»: по умолчанию — токен бота приложения (тот же ForbidenStarsBot).
BACKUP_TELEGRAM_BOT_TOKEN: ${BACKUP_TELEGRAM_BOT_TOKEN:-${TELEGRAM_BOT_TOKEN:-}}
BACKUP_TELEGRAM_CHAT_ID: ${BACKUP_TELEGRAM_CHAT_ID:-}
TZ: ${BACKUP_TZ:-Europe/Moscow}
volumes:
- db-data:/fs-db # живая БД (снимается консистентно)
- uploads-data:/fs/uploads
- achievements-data:/fs/achievements
- backup-data:/backup # локальный репозиторий restic + состояние
mem_limit: ${BACKUP_MEM_LIMIT:-384m}
security_opt:
- no-new-privileges:true
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"
volumes:
db-data:
uploads-data:
achievements-data:
backup-data: