Files
new_lottery_bot/docs/DRONE_DEPLOYMENT.md
Trevor1985 d5a9c13b68
All checks were successful
continuous-integration/drone/push Build is passing
Recover stalled database connections and skip unreachable chat recipients
2026-09-13 20:21:20 +09:00

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

# Автодеплой через Drone
Drone: `https://drone.smartsoltech.kr`, цель: `trevor@192.168.40.112:/opt/new_lottery_bot`, репозиторий `trevor/new_lottery_bot`, production-ветка `master`.
Подготовленный pipeline запускает деплой только для успешного `push` в `master` после проверок, PostgreSQL-тестов и сборки архива. Pull request запускает проверки без передачи deployment-секретов.
## Секреты репозитория
| Имя в Drone | Значение |
| --- | --- |
| `lottery_deploy_user` | SSH-пользователь сервера с доступом к Docker и каталогу приложения |
| `lottery_deploy_path` | Отдельный абсолютный каталог, например `/opt/new_lottery_bot` |
| `lottery_deploy_ssh_key` | Закрытый SSH-ключ этого пользователя, пригодный для неинтерактивного входа |
| `lottery_deploy_known_hosts` | Проверенная запись ключа сервера для `192.168.40.112` |
| `lottery_env` | Полный актуальный production env-файл с BOT_TOKEN, DATABASE_URL, ADMIN_IDS и остальными настройками |
Настройка использует [официальный API создания](https://docs.drone.io/api/secrets/secret_create/) и [обновления секретов](https://docs.drone.io/api/secrets/secret_update/). Скрипт передаёт `pull_request=false`, повторно проверяет наличие имён и не печатает значения. Учётной записи нужны права записи в репозиторий.
На Windows, из корня проекта, после заполнения файлов за пределами репозитория:
```powershell
.\.venv\Scripts\python.exe scripts/configure_drone_secrets.py `
--server https://drone.smartsoltech.kr `
--repo trevor/new_lottery_bot `
--token-file C:\secure\drone-token.txt `
--ssh-key C:\secure\lottery-deploy-key `
--known-hosts C:\secure\lottery-known-hosts `
--env-file C:\secure\lottery-production.env `
--deploy-user trevor `
--deploy-path /opt/new_lottery_bot
```
Пути `C:\secure\...` — примеры файлов с ограниченными правами доступа. Токены не нужно вставлять в командную строку. Отпечаток host key сверяется через доверенный доступ к серверу; автоматическое принятие неизвестного ключа отключено. Для Drone используется отдельный ключ `id_ed25519_lottery_deploy`; пользователь `trevor` имеет доступ к Docker и каталогу приложения.
При подготовке deployment актуальные настройки взяты из работающего контейнера: PostgreSQL 16 расположен на `192.168.20.2`, база `lottery_bot`. Локальный старый `.env.prod` не является источником production-настроек. Проверено совпадение эффективных BOT_TOKEN, DATABASE_URL, REDIS_URL, ADMIN_IDS и имён volumes с действующим Compose-проектом `new_lottery_bot`. Все пять секретов созданы в Drone с запретом передачи pull request.
Резервная копия рабочей БД восстановлена в изолированном PostgreSQL 16; обновление с `20260701_perf_indexes` до `20260913_staff_concurrency` и проверка схемы прошли. При обновлении PostgreSQL согласованно обновлять клиент резервного копирования: [PostgreSQL не гарантирует восстановление дампа нового клиента в более старую major-версию сервера](https://www.postgresql.org/docs/17/app-pgdump.html#APP-PGDUMP-NOTES).
## Перед первым деплоем
1. Активировать репозиторий в Drone. Runner должен получать Git-репозиторий и обращаться по SSH к `192.168.40.112`; сервер должен иметь доступ к registry/PyPI для сборки.
2. На сервере нужны Docker Engine, Compose v2 с поддержкой `--wait`, `flock`, `tar`, `install`. Пользователь должен иметь доступ к Docker и выделенному каталогу. Подготовить ключ и проверить обычный неинтерактивный SSH-вход.
3. Если уже существует контейнер `lottery_bot`, проверить его label `com.docker.compose.project`. Скрипт ожидает `new_lottery_bot` и остановится при другом проекте, чтобы не подменить чужой стек. Существующие volumes, каталог и внешний PostgreSQL необходимо сопоставить до переноса. Не запускать `down -v`.
4. В `.env.prod` использовать реальные настройки из `.env.prod.example`. Для внешней БД оставить `COMPOSE_PROFILES` пустым и указать доступный из контейнера PostgreSQL в `DATABASE_URL`; POSTGRES_PASSWORD не требуется. Для БД в этом Compose задать `COMPOSE_PROFILES=local-db`, непустой POSTGRES_PASSWORD, согласованные POSTGRES_* и адрес `postgres` в URL. Спецсимволы пароля внутри URL должны быть URL-encoded. Клиент резервного копирования и PostgreSQL в Compose/CI используют major-версию 16, соответствующую рабочей БД.
5. При использовании переменных со знаком `$` учитывать интерполяцию Compose env-файла; заключать соответствующее значение в одинарные кавычки. Проверять конфигурацию командой `docker compose --project-name new_lottery_bot --env-file .env.prod config --quiet`, чтобы значения секретов не попадали в вывод.
6. Проверить доступ к целевой БД и возможность `pg_dump`. БД должна уже существовать. Пользователю миграций нужны права изменения принадлежащих приложению объектов. Исторические дубликаты миграция сообщает без удаления данных.
7. Загрузить пять секретов указанным скриптом, опубликовать изменения в `master` и дождаться завершения **всего** pipeline, включая `deploy`.
## Последовательность deployment-скрипта
- Получает архив и env-файл по SSH с обязательной проверкой host key.
- Берёт файловую блокировку деплоя и отклоняет устаревший номер сборки.
- Создаёт каталог `releases/COMMIT-BUILD`, собирает образ с тегом commit и поднимает Redis/необходимую локальную БД.
- Делает PostgreSQL backup в `backups/COMMIT-BUILD-UTC_TIMESTAMP.dump` с ограниченными правами. Дальнейшие действия прекращаются, если backup не получен.
- Останавливает старого polling-бота, выполняет `alembic upgrade head` и проверку схемы.
- Запускает новый образ, ожидает healthcheck, затем ещё 120 секунд проверяет heartbeat и отсутствие перезапусков. Только после этого переключает `current` и записывает `last-build`.
- При ошибке после остановки пытается вернуть прежний образ. **Это откат приложения:** схема БД автоматически не откатывается; используется env текущего deployment. Восстановление БД из backup требует отдельного решения. При первом запуске предыдущего образа может не быть.
Heartbeat обновляется после проверки БД в работающем event loop; до запуска бот проверяет Redis, схему и Telegram `getMe`. Отдельный watchdog-поток завершает процесс при устаревшем heartbeat, даже если завис драйвер или event loop; после этого действует Docker restart policy. На рабочем сетевом маршруте длительное повторное использование соединений зависало: в `lottery_env` установлен `DB_POOL_RECYCLE=15`, сохраняющий ограниченный пул с коротким сроком жизни соединения. Один только начальный Docker healthcheck не является проверкой длительной работы или доставки всех сообщений.
## Проверка результата
После успешного шага `deploy` проверить `/start`, админку, `/cashier` со второго Telegram-аккаунта, добавление участника и тестовый розыгрыш. В логах должны отсутствовать ошибки миграций, `Conflict: terminated by other getUpdates request` и повторяющиеся ошибки соединения с БД. Проверить сохранение FSM после штатного рестарта, содержимое backup и возможность восстановления на отдельной тестовой БД.
Локальные команды CI:
```powershell
.\.venv\Scripts\python.exe -m pip install -r requirements-dev.txt
.\.venv\Scripts\python.exe -m ruff check src main.py scripts tests migrations
.\.venv\Scripts\python.exe -m pytest -q
.\.venv\Scripts\python.exe -m pip_audit -r requirements.txt
.\.venv\Scripts\python.exe scripts/check_secrets.py
.\.venv\Scripts\python.exe scripts/build_release.py
```
Для PostgreSQL-тестов задаётся `TEST_DATABASE_URL` **только отдельной одноразовой тестовой БД**: тесты удаляют и создают в ней таблицы. Пользователю тестов также требуется `CREATEDB` для проверки миграций. Рабочий `DATABASE_URL` тестами не используется: `tests/conftest.py` подставляет временную SQLite БД, если `TEST_DATABASE_URL` не указан.