На Хабре очень много статей о том, как создать простейшего Телеграм бота с кнопками меню и логикой, есть инструкции, как это все задеплоить. В этой статье я расскажу, как делать ботов для продакшена, которыми смогут пользоваться сотни тысяч пользователей.
Всем привет! Меня зовут Даниил Охлопков и у меня есть рой телеграм ботов, которыми пользуются суммарно более 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 страничке.
Очень надеюсь, что такая сумбурная статья кому-то будет полезной. Напомню, что вот сюда я выложил рабочий код - шаблон телеграм бота, где реализовано все, что упоминалось в статье. Также у меня есть свой Телеграм канал, где я делюсь своим опытом разработки ТГ ботов и не только.
А как вы деплоете своих телеграм ботов в продакшн? Как бы вы улучшили мой подход? Напишите об этом в комментариях.
maratsalmanov
Дуров в своем чате написал что не будет открывать исходный код серверной части, там было
очень смешное оправдание. То есть я не смогу сделать у себя дома локальный self hosted сервер, а должен полагаться на его серверы где он может в любой момент отключить моего бота по надуманным причинам, или если у него случатся технические проблемы, а они случаются периодически у всех технологических гигнатов, то мой бот тоже упадет?
Спасибо, очень надежно, продолжаю использовать jabber.
ohld Автор
В случае высоких нагрузок на тг бот, команда телеграма заопенсорсила часть Telegram Bot API, которую можно развернуть на своем сервере: github.com/tdlib/telegram-bot-api Для очень высоконагруженных ботов это оправдано.
maratsalmanov
Опять же, вас просто отключат в любой момент если захотят.
Автономность это безопасность и отсутствие зависимостей. Вы хотите огромных убытков, особенно из-за человеческого фактора третьих лиц? habr.com/ru/news/t/452238
ohld Автор
Я считаю, чтобы понести огромные убытки, нужно сначала создать бизнес / продукт, который генерит огромные прибыли. Иначе можно очень долго готовиться, но так ничего и не сделав прекрасного. Если я не буду делать ничего сомнительного, меня не отключат. От человеческого фактора спасают простейшие бекапы.
maratsalmanov
«Сомнительное» или нет это уже будут решать за вас. Монополист Дуров сам решит мешает ли ваш бот сервис бизнес интересам Телеграма и его партнеров. Был бы человек, повод всегда найдется.
ohld Автор
Так можно про все сказать) за всем всегда стоит человек / группа людей. А также законы и чьи-то интересы.
dnbstd
С таким же успехом можно провести аналогию с Майкрософт, Google, AWS etc. Кто платит того и тапки как известно. Начнет гайки крутить телеграмм, перейдут в Дискорд, Сигнал или еше куда. Селф-хостед Джаббер не панацея + этот протокол по сей день имеет куча недоделок (решаемых, тут не спорю).
ohld Автор
Видимо, это был условный «кремлебот», так как его комментарий никак не связан с темой статьи :)