- PVSM.RU - https://www.pvsm.ru -

Serverless Telegram бот в Яндекс.облаке, или 4.6 копейки за 1000 сообщений

Краткое содержание

"Whenever you receive a webhook update, you have two options"
Из Telegram Bot Api Faq [1]


Привет!

Долгое время концепция serverless (а если говорить точнее — её реализация в виде сервиса AWS Lambda) была для меня относительно понятной, но очень абстрактной идеей. Она часто звучала в Radio-T, обсуждениях на реддите, но никак не входила в мою жизнь. Рабочие проекты живут не в облаке, а домашние — зачем? Виртуалки дешевеют, Docker освоен, и всё отлично работает.

Но презентация Yandex Cloud Functions, и в особенности озвученные цены на этот сервис, дали новую пищу для размышлений.

TL;DR — дождливым пятничным вечером мы напишем простенького Telegram бота на javascript, который сможет отвечать на запросы простыми сообщениями. Если это ваш домашний проект — его использование почти наверняка обойдётся вам значительно дешевле самых бюджетных VPS [2].

Поехали.

Что такое Serverless в самом бытовом понимании?

Не буду очень глубоко уходить в дебри, на Хабре регулярно [3] появляются обзорные статьи [4] на эту тему. Это возможность разместить в облаке функцию, на каком-то из поддерживаемых платформой языков программирования, задать условие её срабатывания — и всё. Когда случится триггер — поднимется виртуальное окружение, в нем отработает функция, и выключится. Вместе с окружением.


В чем преимущества такого подхода?

Безопасность

Вы получаете безопасное изолированное окружение с последней версией компилятора / интерпретатора.
Вместо того, чтобы следить за обновлением пакетов на настоящей ОС в Виртуальной машине, заниматься настройкой политик безопасности и файрволла — вы загружаете программу на сервер, и она работает.

Стабильность и отказоустойчивость

Вместо того чтобы конфигурировать pm2, настраивать политику перезагрузок, следить за утечкой памяти и постигать нюансы деплоя — да, вы просто загружаете программу на сервер, а всё остальное берёт на себя провайдер услуги.

Цена, особенно в условиях домашних малонагруженных проектов

При тарификации учитывается объём памяти, резервируемый под функцию за время её исполнения и количество вызовов. Согласно документации [5] 10 000 000 запусков функции, работающей 800ms с ограничением по памяти 512мб будет стоить 3 900₽.

Что это означает для меня? Мой типичный петпроджект — бот отвечающий на вопросы по расписанию для широко известного в узких кругах мероприятия. Запускать его нужно раз в год, на протяжении нескольких дней. В прошлом году он ответил на 1000 запросов от участников, ему более чем хватит 128мб, время выполнения функции 300ms. Такой сценарий использования будет стоить 0.046₽.

Да, 4.6 копеек. Плюс я не буду тратить время на настройки, что ещё приятней. Никакой настройки pm2, никакой актуализации докерфайла или настроек окружения, и вишенкой на торте — SLA 99,9.

Из пока нерешённых задач (но, полагаю, это вопрос времени) — привязка внешних доменов, а так же тонкая настройка http методов, которые служат триггером функции. Сейчас http триггер это строго post запрос на авто-генерируемую точку входа типа https://functions.yandexcloud.net/xxxxxxxxxxxxxxxx [6].

Из хороших новостей — это полноценный https, который отвечает всем требованиям Telegram для работы с api через webHooks. Но у AWS Lambda есть дополнения в виде API Gateway, да и сама настройка триггера шире, если вам это нужно.

Ещё из очевидных минусов — пользоваться приходится ровно тем, что дают. Написать serverless на непредставленных платформой языках программирования или технологиях, использовать нестандартные параметры компилятора/интерпретатора вы не сможете. Существуют дополнительные ограничения [7] призванные защитить и оградить всех участников процесса разработки.

Как создать .js файл, чтобы работать в Яндекс.Облаке?

Краткий гайд через веб интерфейс:

  • создаём Функцию
  • создаём в веб интерфейсе файл с любым именем и расширением js
  • выбираем интерпретатор — nodejs10 или nodejs12
  • в файле пишем функцию с одним параметром в exports.myFunction (ну, в произвольное поле в exports)
  • указываем таймаут работы функции, оперативную память (128МБ-1024МБ с шагом в 128МБ), точку входа (имяфайла.myFunction)
  • делаем функцию публичной

Функция написанная в файле может:

Получить данные http запроса через входящий параметр:

функция не получает request в чистом виде, и конечно же не управляет ходом запроса — она получает в своём единственном параметре объект [8] с информацией о запросе:

{
    "httpMethod": "<название HTTP метода>",
    "headers": "<словарь со строковыми значениями HTTP-заголовков>",
    "multiValueHeaders": "<словарь со списками значений HTTP-заголовков>",
    "queryStringParameters": "<словарь queryString-параметров>",
    "multiValueQueryStringParameters": "<словарь списков значений queryString-параметров>",
    "requestContext": "<словарь с контекстом запроса>",
    "body": "<содержимое запроса>",
    "isBase64Encoded": <true или false>
}

Ответить на http запрос

согласно документации [9]:

{
    "statusCode": <HTTP код ответа>,
    "headers": "<словарь со строковыми значениями HTTP-заголовков>",
    "multiValueHeaders": "<словарь со списками значений HTTP-заголовков>",
    "body": "<содержимое ответа>",
    "isBase64Encoded": <true или false>
}

Итак, что-нибудь пятничное, бесполезное

Сначала, посмотрим на то что уже написано до нас — реализаций таких ботов для AWS Lambda вагон и маленькая тележка [10].

У них есть одна проблема — для того чтобы не изобретать велосипед, и обеспечить привычный интерфейс, все эти реализации при получении запроса инициируют post до сервера api telegram, что в условиях крупного Российского хостера невыполнимо.
Конечно можно воспользоваться proxy [11], но это как-то не элегантно. Давайте перечитаем документацию.

Как можно было заметить на КДПВ, и цитате в начале поста — при работе через webHook, telegram слушает ответ на своё update сообщение, чтобы понять, был ли он обработан нашим ботом. Более того, он готов принять сообщение в рамках этого же запроса.

Таким образом нельзя отправить несколько сообщений, нельзя вообще ничего, что не входит в функцию sendMessage [12]. Но для многих проектов этого будет достаточно.

Соблюдём традиции, и передадим привет Хабровчанам:

exports.input = function (data){
    let body = JSON.parse(data.body);
    let answer = {
           "method":"sendMessage",
           "chat_id": body.message.chat.id, 
           "reply_to_message_id" : body.message.message_id, 
           "text" : "Привет, Habr!"
    };

    return {
        "statusCode": 200,
        "headers": {
            'Content-Type': 'application/json'
        },
        "body": JSON.stringify(answer),
        "isBase64Encoded": false
    }

}

Выставим настройки в минимум:

Serverless Telegram бот в Яндекс.облаке, или 4.6 копейки за 1000 сообщений - 2

И сообщим в Telegram, что будем использовать webHook:

curl -F url=https://functions.yandexcloud.net/functionsecreturl" https://api.telegram.org/botBOTKEY/setWebhook

Всё. Бот работает.
@YandexServerlessBot [13]
Serverless Telegram бот в Яндекс.облаке, или 4.6 копейки за 1000 сообщений - 3

Всё вышеописанное в одной картинке

Serverless Telegram бот в Яндекс.облаке, или 4.6 копейки за 1000 сообщений - 4

Подводя итог — в некоторых случаях serverless это крайне дёшево, удобно, и экономит кучу времени, а любую документацию надо читать внимательно: тогда она может приятно удивить.

Если вам стало интересно — добро пожаловать в документацию [14] по Yandex Cloud Functions, там много чего интересного, от интеграции с другими сервисами облака до удобного дебага, графиков нагрузки и т.д.

Видео с конференции также доступно на YouTube [15].

Автор: NiPh

Источник [16]


Сайт-источник PVSM.RU: https://www.pvsm.ru

Путь до страницы источника: https://www.pvsm.ru/node-js/332161

Ссылки в тексте:

[1] Telegram Bot Api Faq: https://core.telegram.org/bots/faq#how-can-i-make-requests-in-response-to-updates

[2] VPS: https://www.reg.ru/?rlink=reflink-717

[3] регулярно: https://habr.com/ru/company/selectel/blog/452266/

[4] обзорные статьи: https://habr.com/ru/company/otus/blog/466519/

[5] документации: https://cloud.yandex.ru/docs/functions/pricing#price-example

[6] https://functions.yandexcloud.net/xxxxxxxxxxxxxxxx: https://functions.yandexcloud.net/xxxxxxxxxxxxxxxx

[7] ограничения: https://cloud.yandex.ru/docs/functions/concepts/function-invoke#headers

[8] объект: https://cloud.yandex.ru/docs/functions/concepts/function-invoke#request

[9] документации: https://cloud.yandex.ru/docs/functions/concepts/function-invoke#response

[10] маленькая тележка: https://dev.to/nqcm/-building-a-telegram-bot-with-aws-api-gateway-and-aws-lambda-27fg

[11] proxy: https://habr.com/ru/post/424427/

[12] sendMessage: https://core.telegram.org/bots/api#sendmessage

[13] @YandexServerlessBot: https://telegram.me/YandexServerlessBot

[14] документацию: https://cloud.yandex.ru/docs/functions/

[15] YouTube: https://youtu.be/Gj65FkbcPpE

[16] Источник: https://habr.com/ru/post/470081/?utm_campaign=470081&utm_source=habrahabr&utm_medium=rss