Масштабируемый Продакшн-реди Телеграм бот на Django

На Хабре очень много статей о том, как создать простейшего Телеграм бота с кнопками меню и логикой, есть инструкции, как это все задеплоить. В этой статье я расскажу, как делать ботов для продакшена, которыми смогут пользоваться сотни тысяч пользователей.

6c988e8a22c6b92d1efe05833a00cb9c.jpeg

Всем привет! Меня зовут Даниил Охлопков и у меня есть рой телеграм ботов, которыми пользуются суммарно более 300 000 людей. Разрабатывая их на протяжении прошлого года, я пришел к стеку технологий и подходов, которые обеспечивают мне не только комфортную разработку, но и масштабируемость. В этой статье я поделюсь всеми накопленными знаниями.

Про мой опыт

В этом моменте мне хочется сказать, что все «знания» из этой статьи были добыты мной в боях, никто меня этому не учил — я просто гуглил все проблемы и искал решения на stackoverflow. Я считаю, что у меня есть чуйка на костыли и гов*нокод, именно она помогла мне открыть набор инструментов и технологий, упомянутый в статье. Я предполагаю, что все можно сделать проще и лучше, но сейчас я расскажу свой способ. Поделитесь в комментариях, как бы деплоили тг бота в прод!

Я выложил на GitHub пример проекта, который использует все упомянутые в статье подходы и технологии. Это шаблон телеграм бота, основанный на python-telegram-bot, Django, Celery, Postgres, Redis, Dokku, GitHub Actions и др. Надеюсь, будет полезно и интересно. Поехали

Почему Django?

Многие скажут: ведь Django — очень большой фреймворк! Зачем забивать гвозди микроскопом? У меня на это есть несколько аргументов:

  1. В Django есть поддержка баз данных, встроенная админка и огромная библиотека плагинов на все случаи жизни. Теория от YCombinator учит использовать готовые компоненты вместо написания своих с нуля, так как это позволит сильно сэкономить время на старте и не придется наступать на грабли, на которые уже кто-то наступал.

  2. Если какие-то модули не нужны сейчас, то они могут пригодится в будущем. Например, ваш бот стал популярным и появились фродеры: админка сильно упростит анализ данных системы и позволит расширить вашу команду «не программистами».

  3. Django проверена миллионами разработчиков. Написаны тысячи сайтов с документацией и примерами. Понятно как ее правильно деплоить в продакшн и масштабировать.

Каждый раз, разрабатывая Телеграм бота сложнее, чем hello-world, нужна база данных как минимум, чтобы хранить информацию о всех пользователях, которые пользуются ботом (да, на данный момент Telegram Bot API не выдает даже такую базовую статистику). Как только ботом начинают пользоваться десятки тысячи людей, понадобится как-то масштабировать систему — возможно, одного сервера не хватит.

Если логика вашего Телеграм бота сложна, вам понадобятся background tasks. Например, чтобы разослать тысячам пользователей сообщение с рекламой анонсом новых возможностей бота. В экосистеме Django такие задачи легко реализуются с помощью модуля Celery. В качестве его брокера я люблю использовать Redis, а для периодических задач модуль Django-celery-beat (появляется возможность через админку просто накликать, какую функцию по какому крону вызывать).

Телеграм бот в продакшене: особенности

Предвижу летящие тухлые помидоры, ведь я не знаток девопса и не эксперт в определениях. Поправьте меня в комментариях. Для меня в контексте телеграм ботов «продакшн» означает, что 

  1. База данных должна быть нормальная (не текстовый фай, не sqlite, не in memory). Например, Postgres.

    1. Это обеспечит стабильность и уменьшит шансы, что все данные внезапно потеряются.

  2. Если что-то упало, чтобы поднялось само.

    1. Желательно при этом собирать ошибки, например, через Sentry.

  3. Настроен CI, который автоматизирует превращение нового кода в рабочий сервис.

    1. Я использую GitHub Actions, который заставляет Dokku сделать git pull и начать собирать проект, когда кто-то закоммитил в мастер. Подробнее об этом в конце статьи.

  4. Есть возможность легко масштабировать проект по необходимости. В частности — обработчики сообщений пользователей.

    1. Для этого polling не подойдет, только вебхуки — об этом прямо сейчас ⬇️.

Вебхуки

Существуют два способа работать с Telegram Bot API (да и вообще со всеми микросервисами): пулинг и вебхуки.

  1. Polling — это когда ваш скрипт периодически заходит на серверы Телеграмма и запрашивает новые события о том, как ваши пользовали провзаимодействовали с ботом.

  2. 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 страничке.

Очень надеюсь, что такая сумбурная статья кому-то будет полезной. Напомню, что вот сюда я выложил рабочий код — шаблон телеграм бота, где реализовано все, что упоминалось в статье. Также у меня есть свой Телеграм канал, где я делюсь своим опытом разработки ТГ ботов и не только.

А как вы деплоете своих телеграм ботов в продакшн? Как бы вы улучшили мой подход? Напишите об этом в комментариях.

© Habrahabr.ru