- PVSM.RU - https://www.pvsm.ru -
Основная претензия при работе с serverless — время холодного старта, которым нельзя управлять «из коробки». Если функция стартует впервые за последние 5–25 минут, скорее всего запуск будет долгим — сотни миллисекунд. Причём статически типизированные языки имеют в разы большее время холодного запуска, которое может достигать нескольких секунд. Разработчики решают это на этапе загрузки своего кода, и им в помощь есть целые библиотеки. Например, они позволяют вызвать функцию заранее. Эти способы действительно сокращают время, но не устраняют проблему полностью и могут работать нестабильно. Параллельно этот вопрос пытаются решить и сами облачные провайдеры. Сегодня поговорим о том, как с холодным стартом справляются и те и другие.
Задержка запуска бессерверных вычислений «на холодную» появляется из-за того, что после получения запроса или срабатывания любого другого триггера облачный serverless-сервис, как правило, сначала загружает код и готовит среду выполнения с нужными параметрами и только потом запускает функцию. Суть проблемы можно понять по этой схеме:
Голубым обозначено время холодного запуска. Всё это время запрос в Amazon API Gateway ожидает в очереди. В зависимости от политики платформы это время до непосредственного выполнения может ещё и оплачиваться. У Yandex.Cloud в старой версии рантаймов — без специальных нововведений для уменьшения времени холодного запуска — оплачивается время старта интерпретатора и загрузка кода.
После окончания выполнения функции среда с загруженным кодом продолжает «жить» некоторое время. Если вызов функции повторить, то она стартует в той же среде гораздо быстрее.
К сожалению, временем жизни среды разработчик управлять никак не может. Платформа сохраняет выделенные ресурсы, пока они не потребуются кому-то другому (или пока не освободятся по таймауту). В итоге среда может и полчаса просуществовать, а может исчезнуть уже через несколько минут.
Очевидно, что время холодного запуска serverless-функции зависит от платформы. А для конкретной платформы оно определяется множеством факторов: языком, выделенными на функцию ресурсами, зависимостями и дополнительными пакетами. Типичный разброс времени холодного запуска serverless-функций на разных языках ещё пару лет назад выглядел примерно так:
Сравнение времени холодного запуска serverless-функций для разных рантаймов на 2019 год. Источник [1].
Важно учитывать, что «на холодную» стартует каждый новый поток выполнения функции. Платформы умеют использовать тот же контекст и даже иногда на короткое время ставят запросы в очередь, но управлять параметрами этой очереди разработчик не может.
С точки зрения разработки это оказывается особенно критично при резких скачках трафика: на старте распродаж, во время дневных или сезонных пиков активности пользователей (например, во время заказа обедов с доставкой). На кону лояльность большого количества клиентов, но именно в эти периоды под обработку запросов будут выделяться новые ресурсы, а клиентам придётся ждать запуска очередного параллельного потока (для запущенных потоков скорость приёма входящих запросов останется минимальной).
К вопросу ускорения холодного запуска есть два подхода: со стороны платформы и со стороны разработчика. В первом случае мы ускоряем процесс до запуска контейнера включительно, во втором — после.
Начнём с уровня разработки.
Здесь видно, чья оптимизация работает на каждом из этапов.
Самый распространённый способ — «подогреть» функцию. В большинстве случаев ваша среда живёт определённое время после выполнения кода, поэтому достаточно вызвать функцию чуть раньше или непосредственно перед её вызовом. Так к моменту «боевого» запроса среда выполнения будет развёрнута. В преддверии пиков трафика можно сгенерировать несколько одновременных запросов, чтобы заранее инициализировать нужное количество параллельных потоков.
Чтобы оптимизировать нагрузку и снизить стоимость «прогрева» (всё-таки вы используете ресурсы), можно различать обычные и «прогревочные» вызовы. Последние не должны выполнять весь код функции. Зачастую достаточно просто проверить связь со средой обычным пингом — для этого даже существуют специальные библиотеки, например lambda-warmer [2] или serverless-plugin-warmup [3].
«Подогрев» работает хорошо, если не требуется держать функцию наготове постоянно и можно прогнозировать всплеск трафика. Но с точки зрения платформы он не даёт никаких гарантий. Библиотеки всё так же не управляют временем жизни среды исполнения и просто увеличивают вероятность быстрого ответа.
Бессерверные вычисления должны полностью освобождать разработчиков от беспокойства о масштабировании, но при использовании «подогрева функций» разработчики вкладывают в них силы и время: начинают думать о количестве запущенных потоков, времени отклика, увеличении квот и т. д. Особенно много этим приходится заниматься, когда мы начинаем ожидать большой входящий поток трафика.
Раз уж время холодного запуска в определённой степени зависит от кода, можно заняться его оптимизацией, в частности сокращением размера пакета.
Надо учитывать, что для некоторых языков время холодного запуска меньше (например, у Node.js, Python или Go оно минимально). Пищу для размышлений на эту тему можно найти в соответствующих рейтингах [1].
Для кода на Java есть ещё, может быть, не самый оптимальный, но элегантный вариант ускорить «прогрев» виртуальной машины, используя универсальную GraalVM, способную компилировать функцию заранее в двоичные файлы (инструмент Native Image). Так из общего времени холодного запуска можно вычесть время «прогрева» виртуальной машины. К сожалению, этот способ не поддерживает динамическую загрузку классов и имеет некоторые другие ограничения.
Ту же схему можно реализовать на Go, который уже поддерживается в AWS и не требует экспорта конкретной сигнатуры функции. Так, код Go можно скомпилировать на локальном компьютере и запускать в serverless гораздо быстрее. Подробнее читайте здесь [4].
Время ожидания ответа serverless-функции на стороне клиента зависит также от времени выполнения самой функции, поэтому есть очевидное, но иногда вполне действенное решение — выделить дополнительные ресурсы (память и процессор выделяются пропорционально).
Хотя напрямую это нигде не описано, практика показывает, что у AWS время холодного запуска линейно зависит от выделенной памяти. Подробнее про эксперименты по исследованию зависимости времени холодного старта от выделенной памяти и размера кода можно почитать здесь [5]. По этой статье можно примерно понять, сколько надо выделить ресурсов, чтобы сократить задержку до нужной величины.
Подход платформ к решению проблемы холодного запуска со своей стороны примерно одинаковый: предварительный прогрев инфраструктуры «на всякий случай».
У AWS эта опция называется Provisioned Concurrency [6]. Она поддерживает среду и функцию в состоянии «боевой готовности», заранее развёртывая среду и запуская код инициализации. Документация AWS обещает ответ за двузначное число миллисекунд. Главное преимущество Provisioned Concurrency — гарантии со стороны платформы. Правда, за это придётся заплатить. Опция особенно полезна для языков, время холодного запуска на которых максимально (например, Java).
У Yandex Cloud Functions оптимизация встроена в последний релиз (летом этого года он вышел в статусе Preview для двух рантаймов). Здесь предусмотрен пул виртуальных машин, которые при появлении запроса поступают в распоряжение соответствующей функции. В отличие от AWS, пользовательский код в среде ещё не загружен. Но для некоторых языков (Python и Node.js) запущены интерпретаторы. В итоге время холодного старта удалось сократить в 100 раз — до нескольких миллисекунд.
Плюс в том, что Yandex.Cloud всегда использует последнюю версию рантайма — его не надо отдельно обновлять. Правда, здесь тоже есть свои ограничения, связанные с тем, что интерпретатор загружается до пользовательского кода: некоторые механизмы, например LD_PRELOAD, здесь не работают. Кроме того, нет возможности управлять интерпретатором с помощью переменных окружения. Вероятно, механизм будет ещё как-то дорабатываться, поскольку превью-версии были запущены только летом 2021 года.
Так или иначе проблема холодного старта постепенно решается. Вероятно, полностью избавиться от неё в модели «функция по запросу» не получится, но сегодня мы говорим уже о единицах и десятках миллисекунд, а не о десятках секунд, как это было ещё совсем недавно.
В Yandex.Cloud есть живое и растущее serverless-комьюнити Yandex Serverless Ecosystem [7] и официальный чат Yandex.Cloud [8] в Telegram, где можно задавать вопросы и оперативно получать ответы от единомышленников и коллег. Присоединяйтесь!
И ещё в Yandex.Cloud действует программа free tier [9]. Это позволяет реализовать массу проектов бесплатно, если не выходить за лимиты.
Автор: golodnyj
Источник [10]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/python/367404
Ссылки в тексте:
[1] Источник: https://levelup.gitconnected.com/aws-lambda-cold-start-language-comparisons-2019-edition-%EF%B8%8F-1946d32a0244
[2] lambda-warmer: https://github.com/jeremydaly/lambda-warmer
[3] serverless-plugin-warmup: https://github.com/juanjoDiaz/serverless-plugin-warmup
[4] здесь: https://engineering.opsgenie.com/run-native-java-using-graalvm-in-aws-lambda-with-golang-ba86e27930bf
[5] здесь: https://acloudguru.com/blog/engineering/does-coding-language-memory-or-package-size-affect-cold-starts-of-aws-lambda.
[6] Provisioned Concurrency: https://aws.amazon.com/ru/blogs/compute/operating-lambda-performance-optimization-part-1/
[7] Yandex Serverless Ecosystem: https://t.me/YandexCloudFunctions
[8] Yandex.Cloud: https://t.me/yandexcloud_chat
[9] free tier: https://cloud.yandex.ru/docs/billing/concepts/serverless-free-tier
[10] Источник: https://habr.com/ru/post/574944/?utm_source=habrahabr&utm_medium=rss&utm_campaign=574944
Нажмите здесь для печати.