AWS: Lambda-функции – обзор и интеграция с AWS API Gateway

Автор: | 18/08/2021

AWS Lambda позволяет запускать код без необходимости создания и поддержки серверов, т.н. serverless approach, т.е. бессерверные решения.

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

Код для запуска организовывается в lambda functions, а триггерится с помощью triggers. Результаты записываются в CloudWatch Logs.

В роли триггеров могут выступать AWS сервисы, такие как API Gateway, SQS, Application LoadBalancer, CloudFront, Kinesis, либо внешнее событие, например вебхук из Github.

В этом посте создадим простую AWS Lambda-функцию, рассмотрим панель управления, доступные опции и возможности, а затем создадим AWS API Gateway, который будет пересылать запросы в AWS Lambda.

AWS Lambda – примеры использования

Вообще, с Lambda в AWS можно делать очень многое. По сути, это такая себе “серебрянная пуля”, позволяющая реализовать то, что не доступно штатными сервисами AWS.

Примеры использования AWS Lambda:

  • вебсайт: javascript-фронтент в AWS S3 со Static hosting, фронтенд шлёт запросы через API Gateway к базе данных через Lambda
  • анализ логов: хороший пример – AWS WAF Security Automations, когда данные об HTTP-запросах отсылаются в AWS Kinesis, тот шлёт их в Lambda, в которой выполняются проверки, и при необходимости блокируется IP клиента
  • автоматизация задач по бекапам: AWS SNS шлёт событие, например превышение занятого дискового пространства в AWS S3, SNS шлёт событие в Lambda-функцию, а она удаляет старые бекапы
  • процессинг данных: например, при загрузке нового файла в AWS S3 генерируется событие, которое триггерит Lambda-функцию, которая выполнит перекодирование видео-файла
  • serverless cron-задачи: CloudWatch Events генерирует событие по расписанию, которое триггерит Lambda-функцию

Компоненты и концепты

Кратко рассмотрим основные понятия в AWS Lambda:

  • function: это код, который будет выполнятся в лямбе. Код может быть в виде кода или Docker-образа (deployment package). См. Configuring AWS Lambda functions.
  • trigger: ресурс, который запускает выполнение фунции. Триггер включает в себя сервис AWS, который вызывает функцию, и event mapping – ресурс в Lambda, который читает данные из входного потока и передаёт их на обработку в функцию. См. Invoking AWS Lambda functions и Using AWS Lambda with other services.
  • event: JSON-объект, который содержит данные для Lambda-функции, которые должны быть обработаны
  • execution environment: предоставляет защищённое окружение для вызова Lambda-функции. См. AWS Lambda execution environment.
  • deployment package: код Lambda-функции, может быть в виде zip-архива или Docker-образа. См. Lambda deployment packages.
  • runtime: рабочее окружение, в котором выполняется функция. См. Lambda runtimes
  • layer: zip-архив, предоставляющий дополнительный контент для запуска функций, например – библиотеки. См. Creating and sharing Lambda layers.
  • extensions: AWS Lambda поддерживает расширения, которые позволяют интегрировать функции с утилитами типа мониторинга. См. Using Lambda extensions.
  • concurrency: максимальное количество инстансов Lambda-функции, которые могут быть созданы для обработки входящих событий. См. Managing concurrency for a Lambda function.
  • qualifier: “указатель” версии или алиаса функции. См. Lambda function versions.
  • destination: ресурс AWS, котором функция будет отправлять события после обработки. См. Configuring destinations for asynchronous invocation.

Создание “Hello, World” Lambda

Создадим для начала простую функцию “Hello, World” из готовых шаблонов, посмотрим, как оно всё работает, и что там есть.

Создание функции

Переходим в AWS Lambda, кликаем Create function:

Используем готовый шаблон, выбираем Use a blueprint, находим hello-world-python:

Кликаем Configure:

Задаём имя функции, например example-hello, оставляем дефолтную IAM-роль – она даёт нашей доступ функции к CloudWatch Logs, заодно проверяем код, который будет выполняться:

Жмём внизу Create function:

Внизу переключаемся на вкладку Test:

Тут мы передаём JSON с тремя ключами и значениями, которые будут обработаны нашей функцией.

Запускаем:

Теперь посмотрим, что нам предлагает AWS Console в панели управления Lambda-функциями.

Мониторинг

Первое – мониторинг. Тут куча интеграций – и логи AWS CloudWatch, и трассировка вызовов с AWS X-Ray, и Lambda insights, и AWS CodeGuru:

Configuration

Тут остановимся подробнее.

General configuration

  • настройки памяти: максимум памяти, доступный на время выполнения функции. Также, от количества заданной памяти зависит и то, сколько CPU будет выдано функции – на каждые 1769 MB выделяется 1 vCPU. См. Configuring function memory (console).
  • таймауты выполнения: максимальное значение – 900 секунд, после которой выполнение функции останавливается. Учтите, что время выполнения прямо влияет на стоимость использования функции. См. Timeout.
  • IAM-роль: включает в себя IAM-политики, описывающие разрешения для фунции – к каким сервисам AWS и какие API-вызовы она сможет выполнять.

Triggers

Собственно, триггеры, которые триггерят нашу функцию.

Может быть практически любой сервис AWS:

Например, можно создать триггер на AWS LoadBalancer, который при обращении к заданному пути будет перенаправлять запрос в функцию:

Permissions

Просмотр IAM-роли, которая подключена к функции и её политик, которые определяют доступы функции:

Destinations

Куда слать результаты работы функции.

Например, можно отправлять данные в AWS SNS, который перешлёт их в Opsgenie, который отправит данные в Slack:

Environment variables

Переменные окружения для нашей функции. Могут быть зашифрованы с AWS Key Management Service (KMS):

VPC

Функция может запускаться в отдельной AWS VPC для разграничения доступа:

Monitoring and operations tools

Настройки мониторинга и включение дополнительных сервисов, таких как AWS X-Ray, CloudWatch Lambda Insights и Amazon CodeGuru Profiler:

В Extentions можно выбрать готовое расширение из библиотеки или создать своё:

Concurrency

Максимальное количество инстансов одной Lambda-функции, которое можно создать. См. Managing concurrency for a Lambda function.

Может быть двух видов:

  • reserved concurrency: резервирует количество инстансов Lambda-функции для запуска из общего лимита на аккаунт (1000 по умолчанию), задаётся на всю функцию целиком – все версии и алиасы
  • provisioned concurrency: заранее инициализирует окружение для запуска функции, благодаря чему во время скейлинга и запуска новых инстансов функции она запускается быстрее, задаётся для конкретной версии или алиаса

Asynchronous invocation

Настройки очереди на обработку событий – время жизни событий, количество повторных попыток обработки событий, отправка уведомлений об ошибках обработки событий из очереди.

См. Asynchronous invocation:

См. также Synchronous invocation и Asynchronous invocation.

Database proxies

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

File system

К функции можно смонтировать каталог из AWS Elastic File System:

Aliases

Alias – “указатель” на определённую версию функции, который потом можно использовать в ARN функции для доступа. Также, можно создать один алиас для двух версий, и разделять трафик между ними. См. Lambda function aliases:

Versions

AWS Lambda поддерживает версинирование кода и часть параметров функций. Полезно для проверки функций на Dev-окружении, например – через создание алиаса:

AWS Lambda и AWS API Gateway – пример интеграции

Далее, создадим AWS API Gateway, а за ним повесим Lambda-функцию.

API Gateway будет принимать запросы на URI /test, и передавать его в Lambda.

Создание Lambda-функции

Выбираем Author from scratch, в Runtime выбираем Python:

Код пока не трогаем:

Тут lambda_handler() – это обработчик по-умолчанию, который вызывается при обращении к Lambda-функции, и которому передаются два аргумента:

  1. event: событие от API Gateway, см. Using AWS Lambda with other services
  2. context: методы и параметры для запуска функции, см. AWS Lambda context object in Python

Создание AWS API Gateway

Создаём новый шлюз, тип HTTP API:

Добавляем интеграцию:

Выбираем Lambda, регион и саму функцию:

Задаём URI /test:

Оставляем дефолтный stage:

Через минуту новый API Gateway создан – копируем его URL:

И пробуем с curl:

[simterm]

$ curl https://fwu399qo70.execute-api.us-east-2.amazonaws.com/test
"Hello from Lambda!"

[/simterm]

Что бы увидеть event полностью – выводим его в json.dumps():

import json

def lambda_handler(event, context):
    return {
        'statusCode': 200,
        'body': json.dumps(event)
    }

После изменения кода кликаем Deploy:

Пробуем:

[simterm]

$ curl https://fwu399qo70.execute-api.us-east-2.amazonaws.com/test
{"version": "2.0", "routeKey": "ANY /test", "rawPath": "/test", "rawQueryString": "", "headers": {"accept": "*/*", "content-length": "0", "host": "fwu399qo70.execute-api.us-east-2.amazonaws.com", "user-agent": "curl/7.78.0", "x-amzn-trace-id": "Root=1-611bbaa4-3cb7c28e4e3181dd647f1030", "x-forwarded-for": "194.***.***.29", "x-forwarded-port": "443", "x-forwarded-proto": "https"}, "requestContext": {"accountId": "534***385", "apiId": "fwu399qo70", "domainName": "fwu399qo70.execute-api.us-east-2.amazonaws.com", "domainPrefix": "fwu399qo70", "http": {"method": "GET", "path": "/test", "protocol": "HTTP/1.1", "sourceIp": "194.***.***.29", "userAgent": "curl/7.78.0"}, "requestId": "ENoZriIrCYcEPWg=", "routeKey": "ANY /test", "stage": "$default", "time": "17/Aug/2021:13:33:24 +0000", "timeEpoch": 1629207204179}, "isBase64Encoded": false}

[/simterm]

Попробуем с переменными – добавим новую:

Выводим её значение в функции через os.getenv():

import os
import json

def lambda_handler(event, context):
    return {
        'statusCode': 200,
        'body': os.getenv('Env')
    }

Проверяем:

[simterm]

$ curl https://fwu399qo70.execute-api.us-east-2.amazonaws.com/test
test

[/simterm]

И попробуем изменить обработчик по-умолчанию.

Переименуем lambda_handler в main_handler:

import os
import json

def main_handler(event, context):
    return {
        'statusCode': 200,
        'body': os.getenv('Env')
    }

Если запустим сейчас – получим ошибку выполнения:

[simterm]

$ curl https://fwu399qo70.execute-api.us-east-2.amazonaws.com/test
{"message":"Internal Server Error"}

[/simterm]

Скролим вниз, к Runtime settings:

И меняем имя Handler:

Пробуем ещё раз:

[simterm]

$ curl https://fwu399qo70.execute-api.us-east-2.amazonaws.com/test
test

[/simterm]

Готово.