Блог Amazon Web Services
Новая функциональность AWS Lambda: поддержка образов контейнеров
Оригинал статьи: ссылка (Danilo Poccia, Chief Evangelist (EMEA))
С помощью AWS Lambda вы можете загружать и запускать исходный код, не думая о серверах. Такой подход нравится многим нашим клиентам, но, если вы уже организовали процесс разработки с использованием контейнеров, его использование для запуска приложений в Lambda может быть затруднительно.
Для решения этой проблемы мы выпустили новую функциональность, которая позволяет упаковывать и разворачивать Lambda-функции в виде образов контейнеров размером до 10 ГБ. Благодаря этому, вы можете достаточно легко собирать и разворачивать более крупные приложения, которым требуется большое количество зависимостей, например, рабочие нагрузки, связанные с машинным обучением или работающие с большими объёмами данных. Функции, развёрнутые с помощью контейнеров, просты в использовании и поддерживают автоматическое масштабирование, высокую доступность и встроенную интеграцию со многими сервисами AWS так же, как и функции, запакованные в ZIP-архивы.
Мы предоставляем базовые образы для всех сред запуска, поддерживаемых в Lambda (Python, Node.js, Java, .NET, Go, Ruby): вам остаётся только добавить свой код и необходимые зависимости. Кроме того, мы предоставляем базовые образы для нестандартных сред запуска, которые основаны на Amazon Linux. С их помощью вы можете добавить свою среду запуска путём использования Lambda Runtime API.
Вы также можете запускать в Lambda и другие базовые образы – на основе Alpine или Debian Linux, что потребует реализовать для них Lambda Runtime API. Для облегчения задачи создания новых базовых образов мы выпустили Lambda Runtime Interface Clients, которые реализуют Runtime API для всех поддерживаемых сред запуска. Клиенты доступны через стандартные менеджеры пакетов, чтобы упростить их использование в пользовательских образах, и распространяются по лицензии с открытым исходным кодом.
Мы также выпустили Lambda Runtime Interface Emulator с открытым исходным кодом, который позволяет вам протестировать образ локально и убедиться, что он будет работать корректно при развёртывании в Lambda. Lambda Runtime Interface Emulator включён во все базовые образы, поставляемые AWS, а также может быть использован и в других образах.
Кроме того, ваши контейнерные образы могут использовать Lambda Extensions API для интеграции с системами мониторинга и безопасности и другими утилитами.
Для развёртывания образа контейнера используйте репозиторий Amazon Elastic Container Registry. Давайте посмотрим, как это работает на практике на нескольких примерах. В первом мы будем использовать образ для Node.js, предоставляемый AWS, а во втором – соберём свой собственный образ для Python.
Использование базового образа для Node.js, предоставляемого AWS
Ниже представлен исходный код простой Lambda-функции на Node.js (app.js
), которая генерирует PDF-файл с помощью модуля PDFKit. При каждом вызове она создаёт новое письмо со случайными данными, сгенерированными через модуль faker.js. В выводе функции используется синтаксис Amazon API Gateway для возврата PDF-файла.
Я использую npm
для инициализации пакета и добавления трёх необходимых зависимостей в файл package.json
. Также я создаю файл package-lock.json
, который включу в образ контейнера для более предсказуемого результата.
Теперь я создаю Dockerfile
для образа контейнера моей Lambda-функции. В качестве базового образа я использую образ для среды запуска nodejs12.x
, предоставляемый AWS. Все базовые образы от AWS доступны в Docker Hub и ECR Public. В данном случае я использую Docker Hub:
Для использования образа из ECR Public достаточно заменить первую строку файла на следующую:
В приведённом выше Dockerfile
в базовый образ добавляется исходный код (app.js
) и файлы, описывающие сам пакет и его зависимости (package.json
и package-lock.json
). Затем для установки зависимостей запускается npm
. С помощью инструкции CMD
я задаю обработчик функции, но это можно сделать и позже – при конфигурации Lambda-функции.
Для локальной сборки образа random-letter
я использую Docker CLI:
Чтобы проверить, что всё работает корректно, я запускаю контейнер локально с использованием Lambda Runtime Interface Emulator:
Теперь протестируем вызов функции с помощью cURL. Я передаю пустой документ JSON в качестве входных данных:
При возникновении ошибок я могу исправить их локально. Если всё работает, переходим к следующему шагу.
Для хранения образа я использую новый репозиторий ECR. Перед загрузкой я присваиваю образу необходимый тег. Для поиска уязвимостей в используемом ПО, можно включить сканирование ECR-образов.
Для создания функции я использую Консоль управления AWS, но вы также можете использовать AWS Serverless Application Model (SAM), которая теперь тоже поддерживает образы контейнеров.
В консоли Lambda я нажимаю Create function. На открывшейся странице я выбираю опцию Container image, задаю название функции, а затем нажимаю кнопку Browse images, чтобы найти необходимый образ в моих репозиториях ECR.
После выбора репозитория я выбираю загруженный ранее образ с тегом latest
. После того как я выбрал необходимый образ, Lambda запоминает его digest (на изображении ниже он отображается справа от тега). Вы можете увидеть digest для локальных образов с помощью команды docker images --digests
. Благодаря такому подходу функция будет всегда использовать одинаковый образ, даже если тег latest
будет переназначен, что позволяет защититься от непреднамеренных изменений. Вы также можете изменить образ, используемый в функции. При этом изменение конфигурации функции не приводит к изменению образа, даже если тег к тому времени был переназначен.
При желании я могу переопределить некоторые значения образа контейнера. Сейчас я не буду этого делать, но это позволяет использовать один образ для нескольких функций путём переопределения обработчика в значении поля CMD
.
Я оставляю стандартные значения в остальных полях и нажимаю Create function.
При создании или обновлении кода функции платформа Lambda оптимизирует образы контейнеров, подготавливая их к получению вызовов. Такая оптимизация занимает от нескольких секунд до нескольких минут в зависимости от размера образа. После её завершения я могу вызвать функцию и протестировать её в консоли.
Работает! Давайте теперь добавим API Gateway в качестве триггера. Я нажимаю Add Trigger и создаю API Gateway с использованием HTTP API. Для простоты я оставляю аутентификацию в API открытой для всех.
Теперь я открываю адрес точки доступа API несколько раз, чтобы скачать несколько случайных писем.
Всё работает корректно! Ниже представлено несколько примеров PDF-файлов со случайными данными из модуля faker.js.
Создание нестандартного образа для Python
Иногда вам может потребоваться использовать нестандартный образ для ваших контейнеров, например, чтобы следовать политикам вашей компании или для использования версии среды запуска, которую мы не поддерживаем.
В данном случае я хочу создать образ с использованием Python 3.9. Исходный код моей функции (app.py
) очень простой, я хочу просто поздороваться и вывести используемую версию Python.
Как я уже упоминал, Lambda Runtime Interface Clients (которые реализуют Runtime API) доступны с открытым исходным кодом для всех поддерживаемых сред запуска. Я начну с образа Python, основанного на Alpine Linux, а затем добавлю к нему Lambda Runtime Interface Client для Python. Вот так выглядит Dockerfile
:
На этот раз Dockerfile
гораздо более подробный. Он создаёт образ в три этапа, следуя лучшей практике Docker для многоступенчатой сборки. Вы можете применять такой же подход с тремя этапами для создания своих образов:
- На первом этапе происходит сборка базового образа со средой запуска, в данном случае Python 3.9, а также устанавливается GCC, который позже понадобится для сборки зависимостей.
- На втором этапе устанавливается Lambda Runtime Interface Client, а также копируется сама функция и устанавливаются её зависимости.
- На третьем этапе создаётся финальный образ, который добавляет результат второго этапа к базовому образу из первого этапа. Здесь я также добавил Lambda Runtime Interface Emulator, но это необязательный шаг (больше информации об этом ниже).
Я создал скрипт entry.sh
для использования в качестве ENTRYPOINT
. Он запускает Lambda Runtime Interface Client для Python. При локальном запуске Runtime Interface Client вызывается через Lambda Runtime Interface Emulator.
Теперь я могу использовать Lambda Runtime Interface Emulator для локальной проверки, что функция и контейнер работают корректно:
Образ контейнера без Lambda Runtime Interface Emulator
В образ контейнера необязательно добавлять Lambda Runtime Interface Emulator. Если я не включу его, я всё равно могу тестировать локально путём установки Lambda Runtime Interface Emulator на мой локальный компьютер с помощью следующих шагов:
- Я удаляю команды копирования Lambda Runtime Interface Emulator (
aws-lambda-rie
) и скриптаentry.sh
из третьего этапаDockerfile
, в данном случае они нам не понадобятся. - Я использую следующий
ENTRYPOINT
для запуска Lambda Runtime Interface Client по умолчанию:
ENTRYPOINT [ "/usr/local/bin/python", “-m”, “awslambdaric” ]
- Я использую следующие команды ля установки Lambda Runtime Interface Emulator на локальный компьютер, например, в директорию
~/.aws-lambda-rie
:
mkdir -p ~/.aws-lambda-rie
curl -Lo ~/.aws-lambda-rie/aws-lambda-rie https://github.com/aws/aws-lambda-runtime-interface-emulator/releases/latest/download/aws-lambda-rie
chmod +x ~/.aws-lambda-rie/aws-lambda-rie
После того как Lambda Runtime Interface Emulator установлен локально, я могу подключить его при запуске контейнера. Теперь команда для локального запуска будет выглядеть следующим образом (если Lambda Runtime Interface Emulator установлен в директории ~/.aws-lambda-rie
):
Тестирование нестандартного образа для Python
Независимо от того каким из двух способов я запустил контейнер локально, я могу протестировать вызов функции с помощью cURL:
В результате я получаю то, что и ожидал:
Затем я загружаю образ в ECR и создаю функцию так же, как и в первом примере. Вот результат теста моей функции в консоли:
Полученный образ основан на Alpine и использует Python 3.9 в Lambda.
Функциональность уже доступна для использования
Вы можете использовать образы контейнеров в ваших Lambda-функциях уже сегодня в регионах US East (N. Virginia), US East (Ohio), US West (Oregon), Asia Pacific (Tokyo), Asia Pacific (Singapore), Europe (Ireland), Europe (Frankfurt), South America (São Paulo). Мы планируем выпустить поддержку дополнительных регионов в ближайшее время. Поддержка образов контейнеров доступна в дополнение к поддержке функций в ZIP-архивах. Мы планируем поддерживать оба формата упаковки Lambda-функций.
Использование этой функции не требует дополнительных затрат. Вы платите за использование репозитория ECR, а также стандартную цену на Lambda.
Вы можете воспользоваться новой функциональностью по поддержке образов контейнеров в AWS Lambda через консоль, Интерфейс командной строки AWS (CLI), AWS SDK, AWS Serverless Application Model (SAM), AWS Cloud Development Kit (CDK), а также решений от партнёров AWS, включая Aqua Security, Datadog, Epsagon, HashiCorp Terraform, Honeycomb, Lumigo, Pulumi, Stackery, Sumo Logic и Thundra.
Новая функциональность открывает возможность для реализации новых сценариев, помогает упростить интеграцию с вашими конвейерами разработки (development pipeline), а также облегчает использование нестандартных образов для запуска необходимых платформ при создании бессерверных приложений.
Узнайте больше и начните использовать образы контейнеров с AWS Lambda.