Как спроектировать и создать AWS Serverless API Builder с помощью CDK Python

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

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

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

Для более масштабной архитектуры проекта можно подумать о переносе веб-приложений, связывающих один API Gateway с одной 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 проверяет параметры, уменьшая необходимость проверки параметров внутри кода лямбды. Кроме того, вы можете настроить механизмы аутентификации и авторизации и другие возможности для облегчения кода.
Новая архитектура выглядит следующим образом:

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

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

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

Бессерверный конструктор API

Итак, мы воспользуемся преимуществом использования такого языка программирования, как Python, чтобы разработать конструктор, функция которого заключается в автоматизации создания лямбд и их интеграции с api шлюза, чтобы избежать boilderplate кода.
С помощью этого конструктора нам просто нужно указать метод и необходимые ресурсы 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-шлюза и вызовем приватный метод __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:

https://github.com/ysyzygy/aws-serverless-api-builder-cdk

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

https://docs.aws.amazon.com/cdk/api/v1/python/aws_cdk.aws_apigateway/README.html#aws-lambda-backed-apis

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

Оцените статью
devanswers.ru
Добавить комментарий