AWS Serverless API Builder: Amazon API Gateway с интеграцией AWS Lambdas


Бессерверный логический уровень

Одним из наиболее известных бессерверных шаблонов является шлюз AWS API и AWS Lambda для создания микросервиса, который может представлять собой логический уровень трехуровневой архитектуры. Объединение этих двух сервисов создает бессерверное приложение, которое является безопасным, высокодоступным и масштабируемым. Используя бессерверный подход, вы не несете ответственности за управление сервером ни в каком качестве.


Масштабная проектная архитектура

Для более масштабной проектной архитектуры вы можете подумать о переносе веб-приложений, связывающих один шлюз API с одной функцией Lambda, если они используют такие фреймворки, как Flask (для Python). Эти веб-фреймворки поддерживают маршрутизацию и отдельные пользовательские контексты, которые хорошо подходят, если приложение выполняется на веб-сервере.
Вы можете применить тот же подход к новому облачному приложению и добавить больше функций в одну Lambda.
При таком подходе Amazon API Gateway передает все запросы одной и той же функции Lambda для обработки маршрутизации. По мере того как приложение разрабатывает больше маршрутов, функция Lambda увеличивается в размере (размер физического файла пакетов), и развертывание новых версий заменяет всю функцию. В этом контексте нескольким разработчикам становится сложнее работать над одним и тем же проектом.
Кроме того, AWS указывает эти размеры пакетов развертывания как предельные. Имеет смысл разбить разные части программы, которые могут быть независимыми по домену, как отдельную бессерверную функцию.

Размер пакета развертывания (архива .zip)
50 МБ (в архиве, для прямой загрузки)
250 МБ (в разархивированном виде)
Эта квота применяется ко всем файлам, которые вы загружаете, включая слои и пользовательские среды выполнения.
3 МБ (консольный редактор)

Кроме того, если вы планируете развертывание каждой AWS Lambda со своей собственной конечной точкой Amazon API Gateway, вы получите несколько разных конечных точек URL-адресов, а отслеживание разных конечных точек и обновление разных клиентов станет серьезной проблемой в масштабе.
Лучшей архитектурой является использование встроенной функции маршрутизации, доступной в API Gateway. Во многих случаях вам не нужен веб-фреймворк, что увеличивает размер пакета. AWS API Gateway проверяет параметры, уменьшая необходимость проверки параметров внутри лямбда-кода. Кроме того, вы можете настроить механизмы аутентификации и авторизации и другие функции для облегчения кода.
Новая архитектура выглядит так:

Amazon API Gateway с интеграцией AWS Lambdas

Когда подход ясен, пора развиваться. Первое, что нам нужно создать, это инфраструктура.


Автоматизируйте создание инфраструктуры

Мы используем инфраструктуру как код (IaC) для создания этой архитектуры. Некоторые из многочисленных преимуществ такого подхода использования инфраструктуры в виде кода по сравнению с созданием приложений в консоли — это гибкость, воспроизводимость и возможность повторного использования.
У вас есть возможность управлять всеми ресурсами в одном месте и при необходимости развертывать единый шаблон в любой среде. Вы можете последовательно развернуть одно и то же приложение в нескольких регионах с помощью одной команды. Эти характеристики позволяют избежать проблемы дрейфа окружающей среды. Используя инфраструктуру как код, вы можете проще управлять своими приложениями из одного места.
Мы будем использовать не только IaC, но и AWS CDK, который позволяет создавать приложения в облаке со значительной выразительностью языка программирования.


Конструктор бессерверных API

Итак, мы воспользуемся преимуществом использования языка программирования, такого как Python, для разработки компоновщика, функция которого состоит в автоматизации создания лямбда-выражений и их интеграции с API-интерфейсом шлюза, чтобы избежать шаблонного кода.
С помощью этого компоновщика нам просто нужно указать метод и необходимые ресурсы API и настроить значения лямбда в нашем файле свойств (с помощью файлов свойств мы можем установить разные значения для разных сред с разработкой и производством) и наш код в cdk заботится о создании лямбды и интеграции с API-интерфейсом шлюза.

Посмотрим как!

Во-первых, мы создадим проект Python AWS CDK, а в основном классе app.py, где определен стек, мы создадим стек шлюза API вместе с его свойствами:

api_gateway_props = ApiGatewayProps(
    props=properties,
    template=env_template
)

api_gateway_stack = ApiGatewayDPPStack(
    app,
    api_gateway_props=api_gateway_props,
    env=cdk_env,
    tags=tags)
Войти в полноэкранный режим

Выйти из полноэкранного режима

Во-вторых, мы создадим стек API Gateway и вызовем приватный метод __get_build_integrations, где находится бессерверный билдер:

class ApiGatewayDPPStack(core.Stack):


    def __init__(self, scope: core.Construct, api_gateway_props: api_gateway_props, **kwargs) -> None:
        props = api_gateway_props.props
        template = api_gateway_props.template
        id = template.substitute(name=props["api_gateway"]["stack_name"])
        super().__init__(scope, id, stack_name=id,description=props["api_gateway"]["description"], **kwargs)
        self.__create_api_gateway_role(props, template)
        api = api_gateway.RestApi(self, template.substitute(name=props["api_gateway"]["api_rest_name"]),
                                  description=template.substitute(name=props["api_gateway"]["api_rest_description"]),
                                  deploy_options=api_gateway.StageOptions(
                                      stage_name=template.substitute(name=props["api_gateway"]["stage_name"]),
                                      data_trace_enabled=True,
                                      logging_level=api_gateway.MethodLoggingLevel.INFO,
                                      access_log_destination=log_group_destination)
                                  )

self.__get_build_integrations(
    props,
    api,
    template,
    api_gateway_props
)
Войти в полноэкранный режим

Выйти из полноэкранного режима

Наконец, мы разработаем __get_build_integrations:

def __get_build_integrations(self, props: dict,
                             api: api_gateway.RestApi, template: Template,
                             api_gateway_props: api_gateway_props):
    for integration in props["api_gateway"]["integrations"]:
        name = integration["name"]
        environment, id_prefix, request_schema, response_schema, role =  
    self.build_api_method(api_gateway_props,integration, name, template)
        lambda_integration = lambda_builder.create_lambda(self,
                                                          template,
                                                          integration["lambda"],
                                                          role,
                                                          environment)
method_builder_props = method_props.MethodBuilderProps(response_schema, lambda_integration,name, api, self, integration["base_resource"],integration["method"], id_prefix, request_schema)
api_method.MethodBuilder(method_builder_props)
Войти в полноэкранный режим

Выйти из полноэкранного режима

Чтобы разработать этот метод, у нас будет цикл, который будет повторяться для каждого интегрирования, и ему понадобятся три функции:
1.- Получите свойства и ресурсы определения API, такие как запрос, схема ответа для проверки.
2.- Создайте и настройте лямбду с ее свойствами и слоями. Чтобы сделать пример более полным, мы создали две лямбда-выражения, которые создают и удаляют элемент с помощью Amazon DynamoDB.

def create_lambda(self, template, props, execution_role, environment = None ):

    lambda_name = template.substitute(name=props['name'])
    lambda_function = _lambda.Function(
        self,
        id=lambda_name,
        function_name=lambda_name,
        description=props['description'],
        runtime=_lambda.Runtime.PYTHON_3_8,
        code=_lambda.Code.from_asset(props['code_from_asset']),
        handler=props['handler'],
        memory_size=props['cpu'],
        timeout=core.Duration.minutes(props['timeout']),
        role=execution_role,
        environment=environment
    )
    __add_layer(lambda_function, props, self, template)
    return lambda_function


def __add_layer(lambda_function, props, self, template):
    if "layer" in props:
        layer_name = template.substitute(name=props["layer"]["name"])
        layer = _lambda.LayerVersion(self,
                                     layer_name,
                                     layer_version_name=layer_name,
                                     compatible_runtimes=[_lambda.Runtime.PYTHON_3_8],
                                     code=_lambda.Code.from_asset(props["layer"]["code"]),
                                     description=props["layer"]["description"])
        lambda_function.add_layers(layer)
Войти в полноэкранный режим

Выйти из полноэкранного режима

3.- Добавьте новый метод в API Gateway со всей необходимой информацией:

def addMethod(self, baseResource, props, responseModel, requestModel, requestValidator):
    baseResource.add_method(
        http_method=props.httpMethod,
        integration=self.lambdaIntegration,
        request_parameters=props.queryParameters,
        method_responses=[
            api_gateway.MethodResponse(status_code=str(props.responseStatusCode),
                                       response_models=responseModel)],
        request_validator=requestValidator,
        request_models=requestModel,
        operation_name=self.methodName)
Войти в полноэкранный режим

Выйти из полноэкранного режима

Весь код проекта находится в следующем репозитории github:

Пример в документации AWS CDK дает общее представление о необходимых методах, но чем сложнее API Rest, который вы создаете, тем сложнее код, который вам нужен для его автоматизации.

На этом все, как всегда обратная связь приветствуется 🙂