Serverless в Microsoft Azure или боты для telegram на Azure Functions и python

3
8160

Это продолжение серий постов про «Бомжуем в Microsoft Azure» или как нам запускать сервисы в облаке и при этом не тратить много-много денег.

В Microsoft Azure есть такой сервис Azure Functions — это бессерверные вычисления. Суть его в том, что у вас есть какой-то код/функция, которая запустилась по какому-то триггеру, отработала, сделала что-то и спокойно умерла, ожидая, когда её вызовут еще раз. Такой Function as a Service. Вам не надо настраивать сервера, платформу или инфраструктуру, вы просто берёте свой код и запускаете где-то в облаке.

В Azure Functions существует несколько триггеров, с помощью которых вы можете запустить выполнение функции: HTTPTrigger, TimerTrigger, CosmosDBTrigger, BlobTrigger и т.д. Подробное описание всех триггеров есть в документации. Ажуровские функции поддерживают запуск кода на .net core, node.js, python, java и powershell core (preview) в Linux и Windows среде, включая собранные в docker-контейнерах.

Но почему я решил написать про Azure Functions. Знаете почему? Потому что выполнение функций стоит копейки. 1 миллион запусков в месяц обойдётся в 12,50 рублей. Да, да, двенадцать рублей и пятьдесят копеек. И вот, в один прекрасный субботний вечер, я подумал, а что если запустить телеграмовского бота внутри ажурных функций, но перед тем, как окунуться в мир ажура, надо рассказать про нюансы.

Для telegram, как и для любой платформы существует несколько способов запуска ботов: polling и webhook. Polling не требует сертификатов и публикации вашего приложения, он просто раз в секунду, например, идёт в API мессенджера и спрашивает «Есть чо?», если есть, то в код прилетает целая пачка объектов JSON, с которыми вы уже развлекаетесь. Это просто, не очень быстро работает при больших нагрузках и мессенджеры не любят, когда вы их регулярно пинаете. Webhook же работает наоборот, вы сообщаете мессенджеру endpoint, где живёт ваш фронт и когда в мессенджере происходит какая-то активность, то он просто присылает вам сообщения, но есть нюансы, так как вам требуется публикация вашего приложения, SSL-сертификат и FQDN имя.

В Azure Functions есть Consumption Plan — это оплата за потребление, но у него есть стандартное ограничение на выполнение функции — 5 минут, его можно расширить, но главная идея в том, что мы не будет крутить постоянно запущенный код для бота внутри Azure Functions, мы скажем телеграму, чтобы он сам триггерил функцию на запуск (тот самый Webhook) и вся магия уже будет происходить внутри кода. Для этого будем использовать HTTPTrigger.

Для Azure Functions можно писать и тестировать всё локально в VScode, а уже только потом заливать всё в облако. Нам необходимо создать функцию для анонимного HTTP триггера.

VScode создаст проект, все необходимые файлы и даст возможность запускать локально функции, как будто мы в облаке. ;) Открывайте Debug, запускаем и смотрим, что всё работает, видно, что реквесты летят и видно разные статусы.

Для работы с telegram будем использовать простую либу — pytelegrambotapi. Идём в @botfather в телеграме, получаем токен, тут ничего нового. Плюс нам понадобится ngrok для локального тестирования. Если вы еще с ним не сталкивались, то он просто поднимает тоннель для тестирования приложений локально, может пробросить порт, выдаёт вам домен, поддерживает https и пишет логи. Очень крутая и бесплатная штука.

В нашем проекте надо поправить несколько файлов. В requirements.txt добавляем pyTelegramBotAPI и requests.

host.json

По-дефолту, в Azure Functions URL формируется https://…/api/ЧтоТоЕщё, а если хотите убрать /api/, то надо оставить routePrefix пустым.

function.json

Это уже конфиг запуска функции. ScriptFile — файл запуска, authLevel в нашем случае анонимный, тип триггера, методы и route. Route добавляется к урлу функции, т.к. /api/ мы убрали, то вот route это и есть «ЧтоТоЕщё«.

__init__.py — это просто эхо бот, который отвечает на команду /start

Немного пояснений по коду. Всё, что прилетает в Azure Functions уходит в метод main() в файле __init__.py. Затем данные через триггеры уходят в req, которая описана в function.jsonname. Это основная функция, которая запускается первой, в ней мы и запускаем процессинг обработки сообщений. Для req: func.HttpRequest мы принимаем http-запросы и делаем возврат в func.HttpResponse — 200. Здесь подробнее, что есть в пакете azure.functions.*.

Теперь надо объяснить телеграму, куда слать все сообщения. Для этого в pyTelegramBotAPI есть bot.set_webhook, но с ним лучше особо не играться, т.к. телега просто блокирует запросы от вас, поэтому самый простой вариант это использовать curl или просто открыть в браузере.

После того, как вы установили webhook для телеграма, получили токен для бота, можно запустить локально. Помните, что ngrok при каждом перезапуске в бесплатной версии генерирует вам новый URL. Запускать ngrok надо с параметрами ngrok http 7071 — это означает, что весь трафик будет перенаправлен к вам локально на порт 7071, где крутится локальная функция. Например, webhook c ngrok будет такой — https://0357c88a.ngrok.io/tlg

Развернуть нашу функцию в Azure Functions можно разными способами (Azure CLI, портал Azure, Visual Studio), самое простое использовать VSCode c плагином Azure Functions.

Он создаст ресурсную группу, хранилище, Application Insights для мониторинга и сбора логов и саму функцию.

Вот этот URL+route из function.json и будет нашим webhook для telegram — https://mynewfunctelegram.azurewebsites.net/tlg

Если открыть Live Metrics Stream в Monitoring нашей функции, то будет видно прямой стрим данных и что происходит с ней. ;) То, что вы видите задержку на клиенте это на самом деле проблема между моим прокси и телеграмом, так что Azure Functions отвечает очень бодро.

Видно все, что происходит с функцией, что она получает и куда отправляет, доступен поиск через Log Analytics.

Какой можно сделать вывод из всего этого. Azure Functions клёво, я точно продолжну ставить эксперименты, попробую перетащить туда нашего бота из групп и посмотреть, как это всё будет работать в полупродакшене. Вам не надо думать о сертификатах, есть лайв мониторинг, легко интегрируется с blob storage или azure cosmosdb, быстрый деплой. Короче, есть куда копнуть еще. ;)

зыж ну и прайс, естественно — https://azure.microsoft.com/ru-ru/pricing/details/functions/

3 КОММЕНТАРИИ

  1. А если мой бот сохраняет в файлики данные? Файлики копеечные, но сохранять их надо, так как при каждом таком вызове Functions — придется считывать один из них (тот,что принадлежит конкретному юзеру) и записать туда новые данные (если надо).

    Functions это могут? или там надо что-то еще подрубать?

  2. Привет, а этот способ еще работает? Сколько не пробую, локально все окей, но уже с Azure — никак

Добавить комментарий