DispelEvilBot/DEPLOY.md

207 lines
11 KiB
Markdown
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.

# Развёртывание
Клонируем репозиторий и поднимаем контур Compose-ом. Контуры `test` и `prod`
разделяются именем проекта (`-p`) и файлом окружения (`--env-file`) — у каждого
свои БД и volume'ы, на одной машине не пересекаются.
## 1. Клонировать
```bash
git clone ssh://git@<gitea-host>:2222/mpakobecex/DispelEvilBot.git
cd DispelEvilBot
```
## 2. Подготовить окружение
```bash
cp env/test.env.example env/test.env
# заполнить: BOT_TOKEN, DB_PASSWORD, CHAT_ID (основной чат), ALERT_CHAT_ID (опц.),
# ADMIN_IDS (опц.), CLICKHOUSE_PASSWORD (аналитика событий)
# плюс для админки: BOT_USERNAME, ADMIN_PUBLIC_URL (HTTPS-домен),
# ADMIN_SESSION_SECRET (openssl rand -hex 32)
```
Порт ClickHouse на хосте (`CLICKHOUSE_HTTP_PORT`) у контуров должен различаться:
`prod` — `8123`, `test` — `8124` (иначе контейнеры конфликтуют на одной VPS).
Порт админки (`ADMIN_HTTP_PORT`) — так же: `prod` — `8101`, `test` — `8100`
(8000 на VPS занят Portainer). `ADMIN_BIND_IP` по умолчанию `127.0.0.1`; чтобы
nginx-proxy-manager (контейнер) видел админку, ставьте `172.17.0.1`.
## 3. Логин в registry (если пакет приватный)
```bash
docker login git.mpakosrv.ru -u mpakobecex -p <ТОКЕН с read:package>
```
Если пакет сделать публичным — логин не нужен.
## 4. Запуск
```bash
# тестовый контур
docker compose -p test --env-file env/test.env -f docker-compose.prod.yml up -d
# прод (когда понадобится)
cp env/prod.env.example env/prod.env
docker compose -p prod --env-file env/prod.env -f docker-compose.prod.yml up -d
```
Логи:
```bash
docker compose -p test --env-file env/test.env -f docker-compose.prod.yml logs -f
```
## Что происходит при старте
1. `postgres` поднимается и проходит healthcheck;
2. `migrate` (**Flyway**) накатывает миграции из `sql/` — для пустой БД с нуля;
3. `clickhouse` поднимается и применяет `clickhouse/init/001_events.sql` —
**только при первой инициализации** пустого data-каталога;
4. `bot` стартует (`Запуск DispelEvilBot v<версия> (APP_ENV=...)`), аналитика и рассылки — в нём же;
5. `admin` стартует и отвечает на `GET /healthz` (порт на loopback хоста).
Проверка контура (сервисы, миграции, Postgres, ClickHouse):
```bash
make check E=test
```
## ClickHouse (аналитика событий)
События всех действий бота пишутся в `analytics.events` (контракт — [`docs/events.md`](docs/events.md)).
ClickHouse наружу **не публикуется**: только loopback хоста (`127.0.0.1:${CLICKHOUSE_HTTP_PORT}`).
```bash
make ch E=test # clickhouse-client контура
# пример: события за сегодня по типам
docker compose -p test --env-file env/test.env -f docker-compose.prod.yml exec -T clickhouse \
sh -lc 'clickhouse-client --user "$CLICKHOUSE_USER" --password "$CLICKHOUSE_PASSWORD" \
--query "SELECT event_type, count() FROM analytics.events WHERE toDate(event_time) = today() GROUP BY event_type"'
```
Если ClickHouse недоступен, бот продолжает работать: события уходят в stdout (WARNING),
полностью выключить можно флагом `CLICKHOUSE_ENABLED=false`.
Изменения схемы: `clickhouse/init/*.sql` применяются лишь на пустом data-каталоге,
поэтому на существующем контуре DDL нужно применить вручную (`make ch E=...`).
## Админка (FastAPI)
Сервис `admin` — отдельный образ `mpakobecex/dispelevilbot-admin`, общий пакет `common`.
Наружу пока **не публикуется**: по умолчанию только loopback хоста
(`127.0.0.1:${ADMIN_HTTP_PORT}`). Для nginx-proxy-manager (он в контейнере) задайте
`ADMIN_BIND_IP=172.17.0.1` и указывайте в NPM цель `172.17.0.1:8100` (test) /
`172.17.0.1:8101` (prod). Домен и TLS — Блок 4.2.
```bash
# проверка живости
curl -s http://127.0.0.1:8100/healthz # test
curl -s http://127.0.0.1:8101/healthz # prod
```
Локально без Docker: `pip install -r requirements-admin.txt && python -m admin`
(по умолчанию `0.0.0.0:8000`, переопределяется `ADMIN_HOST`/`ADMIN_PORT`).
### Вход через Telegram
Админка закрыта Telegram Login Widget. Что нужно на контуре:
- `BOT_TOKEN` — токен бота этого контура (им же проверяется подпись `hash`);
- `BOT_USERNAME` — username бота без `@` (виджет рисует кнопку);
- `ADMIN_PUBLIC_URL` — публичный HTTPS-адрес (`https://debot-test.mpakosrv.ru`);
- `ADMIN_SESSION_SECRET` — `openssl rand -hex 32`;
- `ADMIN_IDS` — Telegram-ID администраторов: ими засевается `bot.admin_users` при старте
(`ON CONFLICT DO NOTHING`, существующих не трогает).
Домен должен быть привязан к боту в `@BotFather` (`/setdomain`), а TLS — настроен на
reverse proxy. Сессия живёт 12 часов (`ADMIN_SESSION_MAX_AGE`), cookie подписана и
`HttpOnly`; в проде ставится флаг `Secure`. Все POST-формы проверяют CSRF-токен.
Роль администратора перечитывается из БД на каждом запросе — отключение пользователя
(`enabled = false`) или удаление записи сразу закрывает доступ.
## Бэкап событий (забирает домашний сервер)
Сырые события живут на VPS ограниченное время: раз в сутки домашний сервер **сам забирает**
вчерашнюю партицию (`pull`), складывает её в `Parquet.gz`, импортирует в свой ClickHouse и
сверяет количество строк — **только после сверки** на VPS удаляются партиции старше
`BACKUP_GRACE_DAYS` (по умолчанию 7). На VPS остаются агрегаты (`events_daily`, `money_daily`).
Входящий SSH на домашний сервер не нужен: он инициирует соединение сам.
Разовая настройка:
```bash
# на домашнем сервере
git clone ssh://git@<gitea-host>:2222/mpakobecex/DispelEvilBot.git
cd DispelEvilBot
cp env/backup.env.example env/backup.env # VPS_SSH, VPS_PORT, VPS_PROJECT_DIR, VPS_SSH_KEY, BACKUP_DIR, CLICKHOUSE_*
```
Требования: на VPS — пользователь с доступом к `docker` и каталог с репозиторием
(`VPS_PROJECT_DIR`, там запускается `docker compose`); на домашнем сервере — `clickhouse-client`
и локальный ClickHouse. Настройка SSH-доступа — `deploy/ssh-access.md`.
Если ставить `clickhouse-client` на хост не хочется, укажите в `env/backup.env` переменную
`CLICKHOUSE_CLIENT` — путь к обёртке, которая выполняет клиент внутри контейнера:
```bash
#!/usr/bin/env bash
set -euo pipefail
cd "$(dirname "$0")/.."
exec docker compose --env-file env/backup.env -f docker-compose.yml exec -T clickhouse clickhouse-client "$@"
```
Проверочный прогон (без cron): `make backup E=test` или `./scripts/clickhouse_backup.sh test`.
Cron на домашнем сервере (вчерашний день уже закрыт, время московское):
```cron
0 4 * * * cd /srv/DispelEvilBot && ./scripts/clickhouse_backup.sh test >> /var/log/clickhouse-backup-test.log 2>&1
```
Ошибка на любом шаге оставляет данные на VPS (партиции не удаляются) и пишет понятный лог.
Импорт дня идемпотентен: повторный прогон не дублирует строки.
Если контур на VPS не запущен (нет работающего `clickhouse` или не найден каталог проекта),
скрипт пишет об этом в лог и завершается с кодом 0 — для cron это не ошибка.
## Обновление и откат
```bash
# обновление: поменять IMAGE_TAG в env/<контур>.env, затем
docker compose -p <контур> --env-file env/<контур>.env -f docker-compose.prod.yml pull
docker compose -p <контур> --env-file env/<контур>.env -f docker-compose.prod.yml up -d
```
Откат — вернуть предыдущий `IMAGE_TAG` (например, commit-sha) и повторить.
## Контуры
| Контур | env-файл | БД | APP_ENV | Бот |
| --- | --- | --- | --- | --- |
| test | `env/test.env` | `dispel_evil_test` | `dev` | тестовый |
| prod | `env/prod.env` | `dispel_evil` | `prod` | боевой |
## Переменные окружения
См. `env/prod.env.example` / `env/test.env.example` (контуры) и `env/backup.env.example` (бэкап событий).
## Дашборд (позже)
Superset поднимается на домашнем сервере и читает **локальный** ClickHouse с полной
историей событий (сюда ежедневно приезжают архивы). Отдельный read-only пользователь
ClickHouse и сам дашборд — финальный этап, в этот контур не входят.
## Перенос данных на прод (позже, короткое окно простоя)
1. Остановить старого бота.
2. `pg_dump -Fc -f dump.pgc <старая_бд>`.
3. Поднять прод-контур (миграции создадут чистую схему).
4. Загрузить данные: `pg_restore --data-only --disable-triggers -d dispel_evil dump.pgc`.
5. Сверить: число пользователей, `SUM(money.amount)`, число сообщений.
6. Переключить бота, проверить команды.
Бэкап старой БД обязателен. Дубли в `tg_users`/`de_users`/`unsubscribe` при загрузке
нужно устранить до создания PK (миграция `V12` делает дедуп, но при загрузке поверх
миграций дедуп — вручную).