На Хабре очень много статей о том, как создать простейшего Телеграм бота с кнопками меню и логикой, есть инструкции, как это все задеплоить. В этой статье я расскажу, как делать ботов для продакшена, которыми смогут пользоваться сотни тысяч пользователей.
Всем привет! Меня зовут Даниил Охлопков и у меня есть рой телеграм ботов, которыми пользуются суммарно более 300 000 людей. Разрабатывая их на протяжении прошлого года, я пришел к стеку технологий и подходов, которые обеспечивают мне не только комфортную разработку, но и масштабируемость. В этой статье я поделюсь всеми накопленными знаниями.
Про мой опыт
В этом моменте мне хочется сказать, что все "знания" из этой статьи были добыты мной в боях, никто меня этому не учил - я просто гуглил все проблемы и искал решения на stackoverflow. Я считаю, что у меня есть чуйка на костыли и гов*нокод, именно она помогла мне открыть набор инструментов и технологий, упомянутый в статье. Я предполагаю, что все можно сделать проще и лучше, но сейчас я расскажу свой способ. Поделитесь в комментариях, как бы деплоили тг бота в прод!
Я выложил на GitHub пример проекта, который использует все упомянутые в статье подходы и технологии. Это шаблон телеграм бота, основанный на python-telegram-bot, Django, Celery, Postgres, Redis, Dokku, GitHub Actions и др. Надеюсь, будет полезно и интересно. Поехали
Почему Django?
Многие скажут: ведь Django - очень большой фреймворк! Зачем забивать гвозди микроскопом? У меня на это есть несколько аргументов:
В Django есть поддержка баз данных, встроенная админка и огромная библиотека плагинов на все случаи жизни. Теория от YCombinator учит использовать готовые компоненты вместо написания своих с нуля, так как это позволит сильно сэкономить время на старте и не придется наступать на грабли, на которые уже кто-то наступал.
Если какие-то модули не нужны сейчас, то они могут пригодится в будущем. Например, ваш бот стал популярным и появились фродеры: админка сильно упростит анализ данных системы и позволит расширить вашу команду "не программистами".
Django проверена миллионами разработчиков. Написаны тысячи сайтов с документацией и примерами. Понятно как ее правильно деплоить в продакшн и масштабировать.
Каждый раз, разрабатывая Телеграм бота сложнее, чем hello-world, нужна база данных как минимум, чтобы хранить информацию о всех пользователях, которые пользуются ботом (да, на данный момент Telegram Bot API не выдает даже такую базовую статистику). Как только ботом начинают пользоваться десятки тысячи людей, понадобится как-то масштабировать систему - возможно, одного сервера не хватит.
Если логика вашего Телеграм бота сложна, вам понадобятся background tasks. Например, чтобы разослать тысячам пользователей сообщение с рекламой анонсом новых возможностей бота. В экосистеме Django такие задачи легко реализуются с помощью модуля Celery. В качестве его брокера я люблю использовать Redis, а для периодических задач модуль Django-celery-beat (появляется возможность через админку просто накликать, какую функцию по какому крону вызывать).
Телеграм бот в продакшене: особенности
Предвижу летящие тухлые помидоры, ведь я не знаток девопса и не эксперт в определениях. Поправьте меня в комментариях. Для меня в контексте телеграм ботов "продакшн" означает, что
База данных должна быть нормальная (не текстовый фай, не sqlite, не in memory). Например, Postgres.
Это обеспечит стабильность и уменьшит шансы, что все данные внезапно потеряются.
Если что-то упало, чтобы поднялось само.
Желательно при этом собирать ошибки, например, через Sentry.
Настроен CI, который автоматизирует превращение нового кода в рабочий сервис.
Я использую GitHub Actions, который заставляет Dokku сделать git pull и начать собирать проект, когда кто-то закоммитил в мастер. Подробнее об этом в конце статьи.
Есть возможность легко масштабировать проект по необходимости. В частности - обработчики сообщений пользователей.
Для этого polling не подойдет, только вебхуки - об этом прямо сейчас ⬇️.
Вебхуки
Существуют два способа работать с Telegram Bot API (да и вообще со всеми микросервисами): пулинг и вебхуки.
Polling - это когда ваш скрипт периодически заходит на серверы Телеграмма и запрашивает новые события о том, как ваши пользовали провзаимодействовали с ботом.
Webhook - это когда вы говорите серверам Телеги: присылай мне на мой URL события сам.
Первый способ сильно упрощает разработку ботов, в то время как второй требует поднятого веб-сервера, который будет слушать входящие сообщения и засовывать из в handlers, которые вы описали. Более того, серверы Телеграмма требуют настроенного HTTPS шифрования, поэтому, например, настроить вебхуки на ваш домашний IP без настроенного DNS вряд ли получится. Решение с ngrok поможет лишь протестировать работу бота через вебхуки, в продакшене его, конечно же, не надо использовать.
Если polling - отлично помогает при разработке телеграм бота, то Webhook отлично подходит для продакшена: в случаях наплыва пользователей, можно запустить два инстанса с вашими хендлерами, а заранее настроенный load balancer сам будет параллелить между ними входящие события по вебхуку (такая функция есть как в docker-compose, так и в большинстве современных облачных PaaS, таких как Heroku, Google App Engine или Digital Ocean App Platform).
Как скрестить Django и Telegram bot
Как работать с вебхуками в случае с Django? Просто создать view (пример кода), который получит входящий ивент от Телеграмма и отправит его в ваш обработчик событий (куда вы вешали handlers - логику реакций бота на разные действия пользователей) (пример кода).
Стоит создать Django Model для Телеграм пользователя. И заполнять ее как минимум каждый раз, когда кто-то нажимает команду /start. В этом случае, все пользователи вашего бота будут аккуратно складываться в базу данных, и вы сможете как минимум их посчитать. Для этого рекомендую создать @classmethod
, который будет создавать либо возвращать существующий объект класса User для произвольного Телеграм-события.
Как задеплоить в продакшн
Выше я перечислил большой набор технологий, с которыми многие напрямую не работали. Всякие Redis и load balancers .... звучит сложно.
Год назад до меня дошло, что все, что я перечислил выше уже кто-то делал. И да, это стандартные задачи, которые приходится решать, когда ваш продукт начинает сильно расти. Значит ли это, что кто-то взял и автоматизировал весь процесс, позволив разработчикам не тратить время на уже стандартные действия? КОНЕЧНО.
Создание базы данных Postgres и Redis. Повесить домен и прикрутить HTTPs. Запуск еще одного инстанса приложения и запуск load balancer между ними. Автоматическая сборка новой версии приложения и, в случае успеха, zero-downtime замена старого приложения на новое?
Все эти и другие задачи уже давно автоматизированы. Есть платные платформы, которые это умеют (Google App Engine, Digital Ocean App Platform, Heroku). А есть и self-hosted решения (Dokku, CapRover). Self-hosted означает, что вы снимаете где-нибудь сервер (например, Digital Ocean), а потом простым apt-get install
запускаете бесплатный аналог платных платформ.
Почему я пользуюсь Dokku?
Я люблю Heroku за его простоту. Мне очень нравится их интеграция с GitHub репозиторием, которая максимально автоматизирует процесс деплоя. В итоге код, оказавшись в репозитории, автоматически превращается в рабочий сервис. Больше не надо заходить на серверы, скачивать свежую версию кода, собирать из этого контейнер, запускать его и ждать, что он не упадет сразу же по глупой ошибке, перенаправлять траффик со старого контейнера на новый и гасить старый контейнер. Это все происходит автоматически.
Разработка превращается в творческий процесс, а не в мучительную боль гугления на stackoverflow "установить докер", "как же открыть порт", "настроить https" и тп. Один раз вкусив этот плод, уже не сможешь вернуться обратно.
Сравнение с пакетом в продуктовом магазине.
Это как с кассирами во Вкусвилле: они сами складывают продукты в пакет. Однажды побывав этом магазине, ты уже ожидаешь, что в Пятерочке тебе тоже будут сами складывать продукты в пакет. Но нет! К хорошему быстро привыкаешь и начинаешь требовать это ото всех.
Основная проблема Heroku в том, что он становится очень дорогим, когда бесплатного инстанса перестает хватать: отдельно плати за сервер, отдельно за БД. Что же делать?
Как это обычно бывает, умельцы сделали Open Source self-hosted версию. Называется Dokku. Да, у нее нет красивого UI с кнопочками и one-click github integration, но зато есть очень понятная документация.
Аналогично Heroku, Dokku собирает ваш проект, используя open-source технологию Buildpacks, и позволяет в 2 строчки подключить базы данных и HTTPS шифрование траффика к проекту.
Dokku + Django
Чтобы buildpacks поняли, как запускать ваш проект, нужно указать а) какие зависимости поставить и б) какие команды запускать. Я предпочитаю это все указывать в файлах requirements.txt и Procfile.
В случае с Dokku существует еще третий вспомогательный файл DOKKU_SCALE, в котором указывается сколько копий сервисов нужно запустить. Например, если указать worker=4, это будет значит: возьми из Procfile команду под названием worker (в нашем случае, это Celery worker), запусти ее 4 раза в разных контейнерах, а потом между ними настрой load balancer. Удобно? Удобно.
Прелесть Dokku в том, что когда вы создаете "плагины" с Postgres или Redis, а потом прикрепляете их к вашему приложению, к нему автоматически добавляются переменные окружения DATABASE_URL
и REDIS_URL
, поэтому возможно понадобится немного пошаманить в settings.py, чтобы код был готов читать эти переменные.
Автодеплои по коммиту делаются очень легко. Достаточно настроить CI так, чтобы во время нового изменения ветки Main он заходил по ssh на сервере и делал условный git pull (во внутренний репозиторий Dokku). Этот процесс легко автоматизируется через GitHub Actions, вот код.
Если вам нужна пошаговая инструкция, как создать Dokku приложение, как подключить к нему базы данных, как прикрутить HTTPs, автоматическую сборку через GitHub Actions по коммиту в мастер, читайте в Wiki страничке.
Очень надеюсь, что такая сумбурная статья кому-то будет полезной. Напомню, что вот сюда я выложил рабочий код - шаблон телеграм бота, где реализовано все, что упоминалось в статье. Также у меня есть свой Телеграм канал, где я делюсь своим опытом разработки ТГ ботов и не только.
А как вы деплоете своих телеграм ботов в продакшн? Как бы вы улучшили мой подход? Напишите об этом в комментариях.