Что такое холодные запуски AWS Lambda?

Холодные запуски могут оказаться губительными для производительности AWS Lambda, особенно если вы создаёте клиентоориентированное приложение, которое должно работать в режиме реального времени. Представьте себе, что пользователь отправил приложению запрос, а ваша функция AWS Lambda ещё не запущена. В таком случае система AWS должна будет сначала развернуть ваш код и запустить новый контейнер — и только после этого выполнит запрос. Соответственно, выполнение запроса может занять гораздо больше времени: Lambda-функция начнёт работать только тогда, когда для неё будет готов контейнер.

Холодный запуск происходит при получении первого запроса, для обработки которого создаётся новый рабочий процесс AWS Lambda. Обработка такого запроса занимает больше времени, поскольку сервису AWS Lambda необходимо:

  1. Найти свободные ресурсы в EC2 Fleet и выделить их для рабочего процесса.

  2. Инициализировать рабочий процесс.

  3. Инициализировать ваш функциональный модуль.

Только после этого сервис сможет передать запрос вашей функции-обработчику.

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

При этом у AWS должен быть готовый набор контейнеров, чтобы при вызове функций их можно было сразу запустить. Это означает, что функции после выполнения поддерживаются в разогретом состоянии (обычно 30–45 минут), а затем сворачиваются, позволяя контейнеру подготовиться к вызову новой функции.

Источник: AWS
Источник: AWS

Холодные запуски в AWS Lambda характерны для менее чем 0,25% запросов, однако их последствия могут быть значительными: иногда для выполнения кода требуется целых 5 секунд. Эта проблема особенно актуальна для приложений, работающих в режиме реального времени или в очень жёстких временных рамках.

В чём заключается механизм Provisioned Concurrency и как он помогает решить проблему холодных запусков?

Amazon Lambda поддерживает механизм Provisioned Concurrency, который позволяет лучше управлять производительностью бессерверных приложений. При помощи Provisioned Concurrency можно избежать проблем холодного запуска и задержек при запуске функций AWS Lambda.

Этот механизм позволяет создавать масштабируемые бессерверные приложения с предсказуемой задержкой. Уровень Provisioned Concurrency можно установить для всех версий или алиасов функции. При этом AWS Lambda подготовит для функций контейнеры и гарантирует, что задержка при их вызове не превысит несколько сотых миллисекунды. Это означает, что ваши бессерверные функции адаптируются к резким всплескам трафика или существенным событиям, приводящим к масштабированию, без увеличения задержки.

Тем не менее за Provisioned Concurrency приходится платить. За весь период использования этой опции с момента, когда вы её включили, вам будет выставлен счёт. Время округляется до ближайших 5 минут. Стоимость зависит от уровня Provisioned Concurrency (т. е. числа функций, которые можно выполнять одновременно, без задержек) и объёма выделяемой памяти.

Это означает, что Provisioned Concurrency нужно настраивать очень аккуратно, указывая именно тот уровень, который действительно требуется для ваших рабочих нагрузок — тогда вы избежите непроизводительных затрат.

Как включить опцию Provisioned Concurrency

Ниже мы приводим инструкцию, как настроить Provisioned Concurrency для функций AWS Lambda с помощью консоли управления AWS.

  1. В консоли AWS Lambda выберите любую функцию Lambda.

  2. В выпадающем списке Actions выберите Publish new version. Так вы примените настройки к алиасу или опубликованной версии функции.

  3. Можно добавить описание для версии, но это необязательно. После этого нажмите кнопку Publish.

  4. В выпадающем списке Actions выберите Create alias. Введите имя для каждого алиаса.

  5. В выпадающем списке Version выберите 1, а затем нажмите Create.

  6. Перейдите в блок Concurrency , а затем выберите опцию Add.

  7. Установите переключатель у Qualifier type в значение Alias, а затем выберите тот алиас функции, который вы ранее указали в выпадающем списке Alias. Задайте значение Provisioned Concurrency — число экземпляров функции, которые будут непрерывно запущены. Нажмите кнопку Save.

  8. Перейдите в консоль AWS Lambda. В блоке Provisioned Concurrency должен показываться статус In progress. Инициализация займёт несколько минут. После этого вы сможете использовать Provisioned Concurrency для опубликованного алиаса функции.

К пункту 2
К пункту 2
К пункту 5
К пункту 5
К пункту 8
К пункту 8

Предупреждение: дополнительные расходы. За использование опции Provisioned Concurrency вы заплатите отдельно, помимо обычных затрат на вызовы функций AWS Lambda. Расходы за Provisioned Concurrency будут такими же, как при вызове и непрерывном выполнении нескольких дополнительных экземпляров функции.

Мы описали, как настроить Provisioned Concurrency с помощью консоли управления AWS. Также вы можете изменить параметры с помощью AWS CloudFormation, интерфейса командной строки AWS CLI и пакета AWS SDK.

Использование Lumigo для точной настройки опции Provisioned Concurrency

Lumigo предназначена для мониторинга решений на базе бессерверных вычислений с мощной поддержкой AWS Lambda. Мы обладаем партнёрскими статусами AWS Advanced Technology Partner и AWS Launch Partner и поддерживаем Provisioned Concurrency на своей платформе с её запуска.

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

1. Создайте бесплатный аккаунт Lumigo и подключите наш трассировщик к своим Lambda-функциям.

Бесплатный аккаунт Lumigo можно завести здесь. Установка займёт всего несколько минут — затем вы начнёте мониторинг и отладку функций AWS Lambda, используя методы полномасштабной распределённой трассировки.

Войдите в аккаунт Lumigo.

2.   Узнайте, насколько критична для вас проблема холодных запусков и нужна ли вам опция Provisioned Concurrency.

В главной консоли Lumigo просмотрите информацию о Provisioned Concurrency и о холодных запусках. Все важные сведения находятся на информационной панели:

3.   Настройте оповещения.

Получайте регулярные рекомендации от Lumigo. Тогда вы будете в курсе, у каких функций слишком мало или много одновременно выполняемых экземпляров. Оповещения помогут вам правильно настроить Provisioned Concurrency и найти оптимальный баланс между стоимостью и производительностью.

4.   Чтобы получить информацию об использовании Provisioned Concurrency напрямую через командную строку, используйте инструмент Lumigo CLI.

Выполните команду analyze-lambda-cold-starts, и вы увидите общий уровень Provisioned Concurrency для каждой функции, а также характеристики использования функции в течение заданного времени.

Дополнительные способы повысить производительность холодного запуска AWS Lambda

Cредства мониторинга, позволяющие определить, как холодные запуски воздействуют на приложение

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

Утилиты Amazon CloudWatch Logs и AWS X-Ray помогут разобраться, где и когда в приложении происходят холодные запуски. Правда, эти утилиты потребуют от вас определённых аналитических навыков. С помощью платформ мониторинга, прицельно направленных на бессерверные вычисления (например, Lumigo), гораздо легче отслеживать, как холодные запуски влияют на приложения.

На дашборде Lumigo собраны функции, для которых холодные запуски происходят наиболее часто:

Как видите, 57,36% вызовов функции graphql-api-prod-listSports — холодные запуски. На такие функции обращайте особое внимание!

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

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

Используйте как можно меньше программных пакетов

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

Чем больше программных пакетов — тем больше времени требуется контейнеру для их загрузки. Инструменты Browserify и Serverless Plugin Optimize помогут вам снизить число пакетов.

Читайте исследование на эту тему: Web Frameworks Implication on Serverless Cold Start Performance in NodeJS

Используйте Node.js, Python или Go

Если вы пишете Lambda-функции на Node.js, Python или Go, вы можете сократить холодный запуск до оптимально приемлемого уровня (<500 мс) с минимальными усилиями. Это означает, что даже при холодном запуске время отклика будет находиться в пределах SLA приложения.

В одном из экспериментов Натан Малышев (Nathan Malishev) обнаружил, что при использовании Python, Node.js и Go инициализация занимает гораздо меньше времени, чем при работе с Java или .NET, при этом Python работает как минимум в два раза быстрее по сравнению с Java в зависимости от того, какой объём памяти выделен на Lambda-функцию.

Источник:  Lambda Cold Starts, A Language Comparison – Nathan Malishev

E-Book: Learn best practices for monitoring serverless applications

Что влияет на продолжительность холодного запуска? Наш эксперимент

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

Сравнение холодных запусков типа 1 и типа 2

Майкл Харт (Michael Hart) первым заметил, что существуют заметные различия между двумя типами холодного запуска:

  1. Запуски сразу после изменения кода.

  2. Остальные запуски (например, когда AWS Lambda необходимо увеличить число рабочих процессов в связи с повышением спроса на трафик).

Возможно, при первом холодном запуске сразу после заливки кода необходимы дополнительные действия. Именно поэтому на запуски типа 1 уходит больше времени, чем на запуски типа 2.

На практике чаще всего встречаются холодные запуски типа 2, и именно им мы должны уделять наибольшее внимание. Тем не менее я был крайне заинтригован открытием Харта и сам провёл ряд экспериментов.

Планирование эксперимента

В одном из таких экспериментов я измерил круговую задержку для нескольких функций:

  • control. Функция «Hello world» без зависимостей.

module.exports.handler = async event => {
  return {
     statusCode: 200,
     body: '{}'
  }
}
  • AWS SDK включена в сборку, но на самом деле не требуется. Получается такая же функция, что и control, но с артефактом развёртывания: он включает Node.js AWS SDK (хотя на самом деле функции он не нужен) и занимает 9,5 МБ.

  • Control с большим объёмом ресурсов. Та же функция, что и control, но при этом в артефакт развёртывания добавлено два крупных MP3-файла, что даёт суммарно 60,2 МБ.

  • Функция, использующая AWS SDK. Требует инициализации модуля duration из AWS SDK. Добавляет пакет AWS SDK в свой артефакт развёртывания (9,5 МБ).

const AWS = require('aws-sdk')module.exports.handler = async event => {
  return {
     statusCode: 200,
     body: '{}'
  }
}
  • Требует вызова AWS SDK через Layer. Совпадает с функцией, требующей AWS SDK, но при этом AWS SDK не включается в сборку и не попадает в артефакт деплоймента. Вместо этого используется инъекция пакета AWS SDK через уровень AWS Lambda.

  • Требует использования встроенного AWS SDK. Совпадает с функцией, требующей AWS SDK, но при этом AWS SDK не включается в сборку и не попадает в артефакт деплоймента. Вместо этого используется AWS SDK — часть среды выполнения AWS Lambda.

Для каждой из этих функций я собрал 200 экспериментальных точек для постдеплойных холодных запусков (тип 1) и 1000 точек для остальных холодных запусков (тип 2). Получились вот такие результаты:

На основе этих данных можно сделать несколько выводов.

Влияние типа холодного запуска на производительность

Холодные запуски типа 1 (сразу после развёртывания кода) неизменно требуют больше времени, чем запуски типа 2, особенно если учесть хвостовые задержки (p99).

Влияние размера артефакта развёртывания на производительность

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

module.exports.handler = async event => {
  return {
     statusCode: 200,
     body: '{}'
  }
}

Единственное различие между ними — это размер артефакта развёртывания. Как видно ниже, добавление Node.js AWS SDK в артефакт развёртывания добавляет к круговой задержке при холодном запуске всего 20–60 мс. Однако когда размер артефакта становится намного больше, задержка заметно вырастает. Когда артефакт увеличивается до 60 МБ, к задержке добавляется целых 250–450 мс!

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

Влияние источника зависимостей на производительность

Зачастую пакет AWS SDK — обязательная зависимость. Однако оказывается, важно даже то, откуда загружается AWS SDK. Быстрее всего загружается пакет AWS SDK, встроенный в среду выполнения AWS Lambda. Интересно, что AWS SDK также гораздо быстрее загружать через Layers, чем при добавлении в артефакт развёртывания! Разница при этом гораздо более существенна, чем упомянутые выше 20—60 мс, что говорит о дополнительных факторах.

Прежде чем вы решите для себя раз и навсегда не включать пакет AWS SDK в артефакты развёртывания, нужно учесть ещё и другие факторы.

Например, если вы используете встроенный пакет AWS SDK, вы, по сути, теряете неизменяемость инфраструктуры. Также были случаи, когда у программистов внезапно ломались функции при обновлении версии AWS SDK. Чтобы узнать подробности, прочитайте этот пост.

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

Тем не менее для пользователей фреймворков бессерверных вычислений существует умный плагин serverless-layers, который позволяет избежать многих операционных проблем, характерных для Lambda-уровней. По сути, он использует уровни не как метод предоставления кода, а исключительно для оптимизации. При каждом развёртывании плагин проверяет, изменились ли ваши зависимости, а если да, упаковывает и развёртывает зависимости в виде уровня Lambda (и только для этого проекта), а также обновляет все функции, которые ссылаются на уровень.

Но не надо торопиться! Это ещё не всё.

Влияние директивы require на производительность

На что тратится время, когда во время инициализации модуля запускается эта строка кода?

const AWS = require('aws-sdk')

За кулисами среда выполнения Node.js должна разрешить зависимость и проверить, существует ли aws-sdk по любому из путей, указанному в NODE_PATH. Если папка с модулем найдена, среда запускает логику инициализации в модуле aws-sdk, разрешает все его зависимости и т. д.

Всё это требует процессорных циклов и вызовов ввода-вывода на уровне файловой системы, и именно из-за этого и происходит задержка.

Таким образом, если функции просто нужен клиент DynamoDB, вы сэкономите очень много времени при холодном запуске, если в директиве require у вас будет только клиент DynamoDB.

const DynamoDB = require('aws-sdk/clients/dynamodb')

Поскольку большая часть времени при холодном запуске уходит на разрешение зависимостей — а что, если в принципе устранить необходимость разрешать зависимости при выполнении кода?

Влияние webpack на производительность

Используя систему-сборщик, например webpack, мы можем заранее разрешить все зависимости и свести их исключительно к тому коду, который нам действительно нужен.

Это позволит сэкономить ресурсы, ведь:

  • уменьшится размер артефакта развёртывания;

  • не придётся разрешать зависимости при выполнении.

Результат получается потрясающий!

Итак, вы используете Node.js и хотите свести к минимуму время холодного запуска функций AWS Lambda. Тогда самое эффективное действие — понять, что вам действительно требуется в коде, а затем применить webpack. Данная система параллельно учитывает несколько факторов, создающих временную задержку при холодном запуске.

Для пользователей бессерверных платформ существует плагин serverless-webpack, который сделает эту работу за вас.

Загрузите Lumigo, чтобы обнаружить, исправить и предотвратить холодные запуски

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

Lumigo ведёт мониторинг, используя готовые ключевые метрики холодного запуска функций AWS Lambda, в том числе процентную долю холодного запуска, среднее время холодного запуска и опцию Provisioned Concurrency. Система также в реальном времени оповещает вас о холодных запусках. Если функции начнёт не хватать параллельно выполняемых экземпляров — вы сразу узнаете об этом.

Lumigo также поможет:

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

  • С автоматической распределённой трассировкой. По щелчку мыши, не требуя никаких ручных изменений кода, Lumigo визуализирует всю вашу среду, включая функции AWS Lambda, другие сервисы AWS, а также любые API-вызовы и обращения к внешнему сервису SaaS.

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

  • Получать умные оповещения для бессерверных вычислений. Используя методы машинного обучения, средства прогнозной аналитики Lumigo предупреждают о проблемах (в том числе о холодных запусках AWS Lambda) задолго до того, как те успеют ухудшить производительность или повысить затраты, связанные с работой приложений.

Зарегистрируйте бесплатный аккаунт Lumigo — и мы уже сегодня поможем вам устранить холодные запуски AWS Lambda

Если вам интересна экосистема Serverless-сервисов и все, что с этим связано, заходите в наше сообщество в Telegram, где можно обсудить serverless в целом.

Комментарии (0)