DispelEvilBot/DEPLOY.md

8.0 KiB
Raw Blame History

Развёртывание

Клонируем репозиторий и поднимаем контур Compose-ом. Контуры test и prod разделяются именем проекта (-p) и файлом окружения (--env-file) — у каждого свои БД и volume'ы, на одной машине не пересекаются.

1. Клонировать

git clone ssh://git@<gitea-host>:2222/mpakobecex/DispelEvilBot.git
cd DispelEvilBot

2. Подготовить окружение

cp env/test.env.example env/test.env
# заполнить: BOT_TOKEN, DB_PASSWORD, CHAT_ID (основной чат), ALERT_CHAT_ID (опц.),
# ADMIN_IDS (опц.)
# плюс для админки: BOT_USERNAME, ADMIN_PUBLIC_URL (HTTPS-домен),
# ADMIN_SESSION_SECRET (openssl rand -hex 32)

Порт админки (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 (если пакет приватный)

docker login git.mpakosrv.ru -u mpakobecex -p <ТОКЕН с read:package>

Если пакет сделать публичным — логин не нужен.

4. Запуск

# тестовый контур
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

Логи:

docker compose -p test --env-file env/test.env -f docker-compose.prod.yml logs -f

Что происходит при старте

  1. postgres поднимается и проходит healthcheck;
  2. migrate (Flyway) накатывает миграции из sql/ — для пустой БД с нуля;
  3. bot стартует (Запуск DispelEvilBot v<версия> (APP_ENV=...)), аналитика и рассылки — в нём же;
  4. admin стартует и отвечает на GET /healthz (порт на loopback хоста).

Проверка контура (сервисы, миграции, Postgres, схема аналитики):

make check E=test

Аналитика (Postgres)

События и дневные счётчики сообщений пишутся в схему analytics той же БД Postgres (контракт — docs/events.md). Отдельного сервиса нет.

make psql E=test
# пример: события за сегодня по типам
SELECT event_type, count(*) FROM analytics.events
WHERE event_time >= current_date GROUP BY event_type;

# сообщения по дням
SELECT day, sum(count) FROM analytics.messages_daily
WHERE day >= current_date - 7 GROUP BY day ORDER BY day;

Если Postgres недоступен, бот продолжает работать: батч аналитики уходит в stdout (WARNING) и отбрасывается. Полностью выключить можно флагом ANALYTICS_ENABLED=false.

Партиции создаются приложением при старте и раз в 6 часов; старые не удаляются. Схема создаётся миграцией V26__analytics_schema.sql.

Админка (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.

# проверка живости
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) или удаление записи сразу закрывает доступ.

Бэкап

Аналитика теперь лежит в Postgres, поэтому отдельного бэкапа событий больше нет. Бэкап БД — стандартными средствами Postgres (pg_dump/pg_basebackup); при росте истории следите за размером analytics и диском.

Обновление и откат

# обновление: поменять 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 (контуры).

Дашборд (позже)

Superset поднимается отдельно и читает Postgres (схема analytics) с полной историей событий. Отдельный read-only пользователь Postgres и сам дашборд — финальный этап, в этот контур не входят.

Перенос данных на прод (позже, короткое окно простоя)

  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 делает дедуп, но при загрузке поверх миграций дедуп — вручную).