Борьба с холодным стартом serverless-функций: «подогрев» среды и оптимизация запуска контейнеров

Основная претензия при работе с serverless — время холодного старта, которым нельзя управлять «из коробки». Если функция стартует впервые за последние 5–25 минут, скорее всего запуск будет долгим — сотни миллисекунд. Причём статически типизированные языки имеют в разы большее время холодного запуска, которое может достигать нескольких секунд. Разработчики решают это на этапе загрузки своего кода, и им в помощь есть целые библиотеки. Например, они позволяют вызвать функцию заранее. Эти способы действительно сокращают время, но не устраняют проблему полностью и могут работать нестабильно. Параллельно этот вопрос пытаются решить и сами облачные провайдеры. Сегодня поговорим о том, как с холодным стартом справляются и те и другие.

Откуда берётся время холодного запуска

Задержка запуска бессерверных вычислений «на холодную» появляется из-за того, что после получения запроса или срабатывания любого другого триггера облачный serverless-сервис, как правило, сначала загружает код и готовит среду выполнения с нужными параметрами и только потом запускает функцию. Суть проблемы можно понять по этой схеме:

Голубым обозначено время холодного запуска. Всё это время запрос в Amazon API Gateway ожидает в очереди. В зависимости от политики платформы это время до непосредственного выполнения может ещё и оплачиваться. У Yandex.Cloud в старой версии рантаймов — без специальных нововведений для уменьшения времени холодного запуска — оплачивается время старта интерпретатора и загрузка кода.

После окончания выполнения функции среда с загруженным кодом продолжает «жить» некоторое время. Если вызов функции повторить, то она стартует в той же среде гораздо быстрее.

К сожалению, временем жизни среды разработчик управлять никак не может. Платформа сохраняет выделенные ресурсы, пока они не потребуются кому-то другому (или пока не освободятся по таймауту). В итоге среда может и полчаса просуществовать, а может исчезнуть уже через несколько минут.

Очевидно, что время холодного запуска serverless-функции зависит от платформы. А для конкретной платформы оно определяется множеством факторов: языком, выделенными на функцию ресурсами, зависимостями и дополнительными пакетами. Типичный разброс времени холодного запуска serverless-функций на разных языках ещё пару лет назад выглядел примерно так:

Сравнение времени холодного запуска serverless-функций для разных рантаймов на 2019 год. Источник.

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

С точки зрения разработки это оказывается особенно критично при резких скачках трафика: на старте распродаж, во время дневных или сезонных пиков активности пользователей (например, во время заказа обедов с доставкой). На кону лояльность большого количества клиентов, но именно в эти периоды под обработку запросов будут выделяться новые ресурсы, а клиентам придётся ждать запуска очередного параллельного потока (для запущенных потоков скорость приёма входящих запросов останется минимальной).

Приёмы оптимизации на уровне своего кода

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

Здесь видно, чья оптимизация работает на каждом из этапов.

«Подогрев»

Самый распространённый способ — «подогреть» функцию. В большинстве случаев ваша среда живёт определённое время после выполнения кода, поэтому достаточно вызвать функцию чуть раньше или непосредственно перед её вызовом. Так к моменту «боевого» запроса среда выполнения будет развёрнута. В преддверии пиков трафика можно сгенерировать несколько одновременных запросов, чтобы заранее инициализировать нужное количество параллельных потоков.

Чтобы оптимизировать нагрузку и снизить стоимость «прогрева» (всё-таки вы используете ресурсы), можно различать обычные и «прогревочные» вызовы. Последние не должны выполнять весь код функции. Зачастую достаточно просто проверить связь со средой обычным пингом — для этого даже существуют специальные библиотеки, например lambda-warmer или serverless-plugin-warmup.

«Подогрев» работает хорошо, если не требуется держать функцию наготове постоянно и можно прогнозировать всплеск трафика. Но с точки зрения платформы он не даёт никаких гарантий. Библиотеки всё так же не управляют временем жизни среды исполнения и просто увеличивают вероятность быстрого ответа.

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

Фокусы с языками и кодом

Раз уж время холодного запуска в определённой степени зависит от кода, можно заняться его оптимизацией, в частности сокращением размера пакета.

Надо учитывать, что для некоторых языков время холодного запуска меньше (например, у Node.js, Python или Go оно минимально). Пищу для размышлений на эту тему можно найти в соответствующих рейтингах.

Для кода на Java есть ещё, может быть, не самый оптимальный, но элегантный вариант ускорить «прогрев» виртуальной машины, используя универсальную GraalVM, способную компилировать функцию заранее в двоичные файлы (инструмент Native Image). Так из общего времени холодного запуска можно вычесть время «прогрева» виртуальной машины. К сожалению, этот способ не поддерживает динамическую загрузку классов и имеет некоторые другие ограничения.

Ту же схему можно реализовать на Go, который уже поддерживается в AWS и не требует экспорта конкретной сигнатуры функции. Так, код Go можно скомпилировать на локальном компьютере и запускать в serverless гораздо быстрее. Подробнее читайте здесь.

Выделение ресурсов

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

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

Оптимизация на уровне запуска контейнера

Подход платформ к решению проблемы холодного запуска со своей стороны примерно одинаковый: предварительный прогрев инфраструктуры «на всякий случай».

У AWS эта опция называется Provisioned Concurrency. Она поддерживает среду и функцию в состоянии «боевой готовности», заранее развёртывая среду и запуская код инициализации. Документация AWS обещает ответ за двузначное число миллисекунд. Главное преимущество Provisioned Concurrency — гарантии со стороны платформы. Правда, за это придётся заплатить. Опция особенно полезна для языков, время холодного запуска на которых максимально (например, Java).

У Yandex Cloud Functions оптимизация встроена в последний релиз (летом этого года он вышел в статусе Preview для двух рантаймов). Здесь предусмотрен пул виртуальных машин, которые при появлении запроса поступают в распоряжение соответствующей функции. В отличие от AWS, пользовательский код в среде ещё не загружен. Но для некоторых языков (Python и Node.js) запущены интерпретаторы. В итоге время холодного старта удалось сократить в 100 раз — до нескольких миллисекунд.

Плюс в том, что Yandex.Cloud всегда использует последнюю версию рантайма — его не надо отдельно обновлять. Правда, здесь тоже есть свои ограничения, связанные с тем, что интерпретатор загружается до пользовательского кода: некоторые механизмы, например LD_PRELOAD, здесь не работают. Кроме того, нет возможности управлять интерпретатором с помощью переменных окружения. Вероятно, механизм будет ещё как-то дорабатываться, поскольку превью-версии были запущены только летом 2021 года.

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

Разрабатываем вместе с сообществом

В Yandex.Cloud есть живое и растущее serverless-комьюнити Yandex Serverless Ecosystem и официальный чат Yandex.Cloud в Telegram, где можно задавать вопросы и оперативно получать ответы от единомышленников и коллег. Присоединяйтесь!

И ещё в Yandex.Cloud действует программа free tier. Это позволяет реализовать массу проектов бесплатно, если не выходить за лимиты.

Читайте так же:

  • 5000 мА·ч, 120 Вт, MIUI 13 и Snapdragon 888 Pro. Новые подробности о Xiaomi Mi Mix 45000 мА·ч, 120 Вт, MIUI 13 и Snapdragon 888 Pro. Новые подробности о Xiaomi Mi Mix 4 Китайский источник приводит новые подробности о перспективном флагмане Xiaomi Mi Mix 4. На этот раз сообщается. Что аппарат получит аккумуляторную батарею BM58 емкостью 5000 мА·ч. Так что по автономности новинка не уступит тому же Mi 11 Ultra. А вот заряжаться Mi Mix 4 будет быстрее […]
  • Google: обновление Page Experience Update может заменить предыдущие Speed UpdateGoogle: обновление Page Experience Update может заменить предыдущие Speed Update За последнее время Google запустил множество алгоритмов поиска, связанных со скоростью. Поэтому у специалистов возникает вопрос, будет ли Google использовать все предыдущие версии или заменит их обновлением Page Experience Update. Джон Мюллер из Google предположил, что поисковик, скорее […]
  • Проверить текст сайта на уникальность онлайнПроверить текст сайта на уникальность онлайн Повысьте свою корректуру с помощью online-spellcheck.com Существует три основных подхода к использованию проверки орфографии и проверке текста на наличие ошибок. Каждое слово сравнивается со словом в данном словаре. Слово с ошибками легко распознать, если словарь достаточно велик, […]
  • Примеры контентов сайтаПримеры контентов сайта Один миллиард-это количество сайтов в интернете сегодня. По данным Atlantic, среднестатистический человек посещает примерно 100 отдельных доменов каждый месяц. В 2017 году состояние экономики создателей показало. Что американские потребители читают 207 статей и посещают 398 веб-сайтов […]