Как использовать правила запланированных событий EventBridge, чтобы поддерживать функции Lambda в теплом состоянии и избегать проблем, связанных с холодным запуском.

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

Во-первых, давайте удостоверимся, что мы знаем, что такое холодный старт и почему он возникает. В бессерверном мире образ контейнера, который запускает вашу функцию, запускается по запросу, обычно в ответ на запуск определенного события. При длительном бездействии провайдер останавливает этот контейнер для экономии ресурсов и затрат (это называется «переохлаждением»). Время для всего этого индивидуально для каждого поставщика облачных услуг. Например: AWS Lambdas перестанет работать после 5–7 минут бездействия.

Холодный старт - это то, что происходит, когда вызывается одна из этих неактивных («холодных») функций. Вашему провайдеру требуется различное время, чтобы восстановить вашу функцию из холодного состояния. Иногда это всего лишь несколько дополнительных миллисекунд, а иногда может быть больше секунды или двух. В конечном итоге непредсказуемо подобное часто приводит к непредвиденным результатам в другом месте вашего приложения.

Когда я впервые столкнулся с этой проблемой, я помню, как сначала подумал: Ну, может быть, нужно провести некоторую оптимизацию производительности. Однако в экосистеме AWS увеличение выделения памяти для Lambda не влияет на фактическое время подготовки контейнера. Это связано с тем, что процесс определения распределения пространства EC2 для рабочего не контролируется теми же ресурсами (вы можете узнать больше о том, что происходит под капотом здесь).

Первое, с чего вы должны начать, пытаясь сократить время холодного старта, - это разделить ваши функции и убедиться, что вы следуете лучшим практикам. Избегайте создания монолитных функций и старайтесь разбивать их на более мелкие и более детализированные функции. Это дает дополнительное преимущество, делая ваши функции более повторно используемыми. В зависимости от размера ваших пакетов развертывания и ваших требований это может быть все, что вам нужно. Но если вам нужна еще большая предсказуемость, вам нужно найти способ гарантировать, что ваши критически важные бессерверные функции никогда не перестанут работать.

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

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

Недавно наша команда решила создать приложение-компаньон Slack, которое дополнит нашу существующую веб-платформу. Первоначальная идея была проста: используйте косую черту (т.е. «/ command»), чтобы пользователи могли взаимодействовать с нашей веб-платформой, не покидая Slack. Вот очень простой обзор настройки:

Но вот где такая вариативность времени запуска становится проблематичной при такой настройке (из документации Slack API):

Нам нужно было правильно проверять и отправлять все входящие запросы от Slack. Механизм для этого - лямбда-функция, которая сначала проверяет входящий запрос, затем асинхронно передает операцию другой функции и, наконец, возвращает подтверждение обратно в Slack.

Это трехсекундное окно, хотя и велико, не всегда было достаточным, учитывая переменный характер холодных запусков Lambda и задержку в сети. Наша команда обнаружила ~ 30% тайм-аута, когда функция вызывалась из холодного состояния (большой отклик). Эта функция уже была хорошо оптимизирована (время горячего выполнения в среднем составляет ‹70 мс… намного лучше), поэтому нам потребовалось другое решение для обеспечения надежности нашего сервиса.

Наше решение состояло в том, чтобы гарантировать, что эта лямбда-функция никогда не перестанет работать (т. Е. Всегда отвечает в течение 3 секунд), без существенного влияния на наш ежемесячный счет от AWS. Мы достигли этого с помощью правила запланированного события EventBridge (ранее известного как события Cloudwatch). Если вы не знакомы с правилами проведения мероприятий, ознакомьтесь с документацией AWS здесь.

Самый быстрый способ включить эти триггеры - зайти в консоль EventBridge и создать новое правило:

Отсюда требуется всего 4 щелчка мышью, чтобы настроить ваше мероприятие по фиксированному расписанию. Ниже показана минимально необходимая конфигурация. 5 минут - идеальное место для предотвращения «остывания» лямбды. Что-то менее частое может привести к тому, что функция перестанет работать, а что-то более частое будет излишним. Вы можете установить несколько функций в качестве целей триггера, но для этого примера я добавил только одну.

Если вы используете шаблоны CloudFormation, вы можете найти документы, необходимые для прикрепления правила события к вашему шаблону функции, здесь. Это правило находится в разделе "Ресурсы: {}" вашего шаблона.

Если вместо этого вы хотите использовать AWS SAM, вы можете найти нужную документацию здесь.

Совет. Если вы используете несколько сред, стоит отметить, что нет особых причин сохранять в тепле себя лямбда-выражения Dev или Sandbox, вам следует установить этот триггер условно, используя параметр шаблона для вашей среды.

Теперь последний шаг. Мы не хотим выполнять всю функциональную логику, содержащуюся в нашей лямбде, каждый раз, когда срабатывает этот триггер - это помогает нам управлять затратами и более эффективно использовать ресурсы. Если ваша функция Lambda не зависит от каких-либо других запланированных событий, вы можете просто добавить следующий код в начало каждой функции, запускаемой этим правилом EventBridge (вот пример Node.js):

Этот фрагмент кода довольно прост: мы просто определяем тип события и сразу же возвращаем его, если источник события связан с запланированным событием. Это предотвращает выполнение остальной части нашей функциональной логики.

Если вы уже используете запланированные события как часть своей функции, вам нужно вернуться в EventBridge, чтобы настроить целевой ввод:

Установите для этого постоянного значения какое-нибудь уникальное значение, например «LambdaWarmerEvent». После этого вы сможете извлечь это значение из объекта события и вернуть свою функцию, как показано выше.

И все - никаких холодных запусков для ваших лямбда-функций!

Заключение

Надеюсь это поможет! Если вас интересует дополнительная информация о создании с помощью AWS Amplify, Serverless Providers или Slack Apps, дайте мне знать в комментариях или по адресу [email protected].

Счастливого строительства!