«1. Обзор

В нашей предыдущей статье мы уже реализовали бессерверное приложение с полным стеком на AWS, используя API Gateway для конечных точек REST, AWS Lambda для бизнес-логики, а также DynamoDB в качестве базы данных.

Однако развертывание состоит из множества ручных шагов, которые могут стать неудобными с ростом сложности и количества сред.

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

Подробно мы рассмотрим следующие темы:

    Основы модели бессерверных приложений (SAM), а также лежащее в основе CloudFormation определение бессерверного приложения с использованием синтаксиса шаблона SAM Автоматическое развертывание приложения с помощью CLI CloudFormation

2. Основы

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

Однако недостатком является то, что в настоящее время нам нужно много ручных шагов в консоли AWS, таких как создание каждой функции, загрузка кода, создание таблицы DynamoDB, создание ролей IAM, создание API и структуры API и т. д.

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

Именно здесь вступают в игру CloudFormation для приложений на AWS в целом и Модель бессерверных приложений (SAM) специально для бессерверных приложений.

2.1. AWS CloudFormation

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

Следующие термины и понятия необходимы для понимания CloudFormation и SAM:

Шаблон — это описание приложения, его структура во время выполнения. Мы можем определить набор необходимых ресурсов, а также то, как эти ресурсы должны быть настроены. CloudFormation предоставляет общий язык для определения шаблонов, поддерживая форматы JSON и YAML.

Ресурсы — это строительные блоки в CloudFormation. Ресурсом может быть что угодно, например RestApi, стадия RestApi, пакетное задание, таблица DynamoDB, экземпляр EC2, сетевой интерфейс, роль IAM и многое другое. В официальной документации на данный момент перечислено около 300 типов ресурсов для CloudFormation.

Стек — это экземпляр шаблона. CloudFormation позаботится о подготовке и настройке стека.

2.2. Модель бессерверных приложений (SAM)

Как это часто бывает, использование мощных инструментов может стать очень сложным и неудобным, что также относится к CloudFormation.

Вот почему Amazon представила модель бессерверных приложений (SAM). SAM начал с заявления о предоставлении чистого и простого синтаксиса для определения бессерверных приложений. В настоящее время у него есть только три типа ресурсов: функции Lambda, таблицы DynamoDB и API.

SAM основан на синтаксисе шаблона CloudFormation, поэтому мы можем определить наш шаблон, используя простой синтаксис SAM, и CloudFormation продолжит обработку этого шаблона.

Дополнительные сведения доступны в официальном репозитории GitHub, а также в документации AWS.

3. Предварительные требования

Для следующего руководства нам потребуется учетная запись AWS. Учетной записи бесплатного уровня должно быть достаточно.

Кроме того, нам нужно установить AWS CLI.

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

$>aws s3 mb s3://baeldung-sam-bucket

В качестве демонстрационного приложения мы будем использовать код из Использование AWS Lambda с API Gateway.

4. Создание шаблона

«В этом разделе мы создадим наш шаблон SAM.

Сначала мы рассмотрим общую структуру, прежде чем определять отдельные ресурсы.

4.1. Структура шаблона

Во-первых, давайте посмотрим на общую структуру нашего шаблона:

Как мы видим, шаблон состоит из заголовка и тела:

AWSTemplateFormatVersion: '2010-09-09'
Transform: 'AWS::Serverless-2016-10-31'
Description: Baeldung Serverless Application Model example
 
Resources:
  PersonTable:
    Type: AWS::Serverless::SimpleTable
    Properties:
      # Define table properties here
  StorePersonFunction:
    Type: AWS::Serverless::Function
    Properties:
      # Define function properties here
  GetPersonByHTTPParamFunction:
    Type: AWS::Serverless::Function
    Properties:
      # Define function properties here
  MyApi:
    Type: AWS::Serverless::Api
    Properties:
      # Define API properties here

Заголовок указывает версию шаблона CloudFormation (AWSTemplateFormatVersion), а также версию нашего шаблона SAM (Transform). Мы также можем указать Описание.

Тело состоит из набора ресурсов: у каждого ресурса есть имя, тип ресурса и набор свойств.

В настоящее время спецификация SAM поддерживает три типа: AWS::Serverless::Api, AWS::Serverless::Function, а также AWS::Serverless::SimpleTable.

Поскольку мы хотим развернуть наше примерное приложение, мы должны определить одну SimpleTable, две функции, а также один API в теле нашего шаблона.

4.2. Определение таблицы DynamoDB

Теперь давайте определим нашу таблицу DynamoDB:

Нам нужно определить только два свойства для нашей SimpleTable: имя таблицы, а также первичный ключ, который называется id и имеет тип Number в нашем случае.

AWSTemplateFormatVersion: '2010-09-09'
Transform: 'AWS::Serverless-2016-10-31'
Description: Baeldung Serverless Application Model example
 
Resources:
  PersonTable:
    Type: AWS::Serverless::SimpleTable
    Properties:
      PrimaryKey:
          Name: id
          Type: Number
      TableName: Person

Полный список поддерживаемых свойств SimpleTable можно найти в официальной спецификации.

Примечание. Поскольку мы хотим получить доступ к таблице только с помощью первичного ключа, нам достаточно AWS::Serverless::SimpleTable. Для более сложных требований вместо него можно использовать собственный тип CloudFormation AWS::DynamoDB::Table.

4.3. Определение лямбда-функций

Далее давайте определим наши две функции:

Как мы видим, каждая функция имеет одинаковые свойства:

AWSTemplateFormatVersion: '2010-09-09'
Transform: 'AWS::Serverless-2016-10-31'
Description: Baeldung Serverless Application Model example
 
Resources:
  StorePersonFunction:
    Type: AWS::Serverless::Function
    Properties:
      Handler: com.baeldung.lambda.apigateway.APIDemoHandler::handleRequest
      Runtime: java8
      Timeout: 15
      MemorySize: 512
      CodeUri: ../target/aws-lambda-0.1.0-SNAPSHOT.jar
      Policies: DynamoDBCrudPolicy
      Environment:
        Variables:
          TABLE_NAME: !Ref PersonTable
      Events:
        StoreApi:
          Type: Api
            Properties:
              Path: /persons
              Method: PUT
              RestApiId:
                Ref: MyApi
  GetPersonByHTTPParamFunction:
    Type: AWS::Serverless::Function
    Properties:
      Handler: com.baeldung.lambda.apigateway.APIDemoHandler::handleGetByParam
      Runtime: java8
      Timeout: 15
      MemorySize: 512
      CodeUri: ../target/aws-lambda-0.1.0-SNAPSHOT.jar
      Policies: DynamoDBReadPolicy
      Environment:
        Variables:
          TABLE_NAME: !Ref PersonTable
      Events:
        GetByPathApi:
          Type: Api
            Properties:
              Path: /persons/{id}
              Method: GET
              RestApiId:
                Ref: MyApi
        GetByQueryApi:
          Type: Api
            Properties:
              Path: /persons
              Method: GET
              RestApiId:
                Ref: MyApi

Обработчик определяет логику функции. Поскольку мы используем Java, это имя класса, включая пакет, в сочетании с именем метода.

Время выполнения определяет, как была реализована функция, в нашем случае это Java 8.

Тайм-аут определяет максимальное время, которое может занять выполнение кода, прежде чем AWS прекратит выполнение.

MemorySize определяет размер выделенной памяти в мегабайтах. Важно знать, что AWS распределяет ресурсы ЦП пропорционально MemorySize. Таким образом, в случае функции, интенсивно использующей ЦП, может потребоваться увеличить MemorySize, даже если функции не требуется столько памяти.

CodeUri определяет расположение кода функции. В настоящее время он ссылается на целевую папку в нашей локальной рабочей области. Когда мы позже загрузим нашу функцию с помощью CloudFormation, мы получим обновленный файл со ссылкой на объект S3.

Политики могут содержать набор политик IAM, управляемых AWS, или шаблоны политик для SAM. Мы используем специфичные для SAM политики DynamoDBCrudPolicy для StorePersonFunction и DynamoDBReadPolicy для GetPersonByPathParamFunction и GetPersonByQueryParamFunction.

Environment определяет свойства среды во время выполнения. Мы используем переменную среды для хранения имени нашей таблицы DynamoDB.

События могут содержать набор событий AWS, которые должны запускать функцию. В нашем случае мы определяем Event типа Api. Уникальная комбинация пути, метода HTTP и RestApiId связывает функцию с методом нашего API, который мы определим в следующем разделе.

Полный список поддерживаемых свойств функций можно найти в официальной спецификации.

4.4. Определение API в виде файла Swagger

После определения таблицы и функций DynamoDB теперь мы можем определить API.

Первая возможность заключается в том, чтобы определить наш API в формате Swagger:

Наш API имеет три свойства: StageName определяет этап API, EndpointConfiguration определяет, является ли API региональным или оптимизированным для периферии, и DefinitionBody содержит фактическую структуру API.

AWSTemplateFormatVersion: '2010-09-09'
Transform: 'AWS::Serverless-2016-10-31'
Description: Baeldung Serverless Application Model example
 
Resources:
  MyApi:
    Type: AWS::Serverless::Api
    Properties:
      StageName: test
      EndpointConfiguration: REGIONAL
      DefinitionBody:
        swagger: "2.0"
        info:
          title: "TestAPI"
        paths:
          /persons:
            get:
              parameters:
              - name: "id"
                in: "query"
                required: true
                type: "string"
              x-amazon-apigateway-request-validator: "Validate query string parameters and\
                \ headers"
              x-amazon-apigateway-integration:
                uri:
                  Fn::Sub: arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${GetPersonByHTTPParamFunction.Arn}/invocations
                responses: {}
                httpMethod: "POST"
                type: "aws_proxy"
            put:
              x-amazon-apigateway-integration:
                uri:
                  Fn::Sub: arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${StorePersonFunction.Arn}/invocations
                responses: {}
                httpMethod: "POST"
                type: "aws_proxy"
          /persons/{id}:
            get:
              parameters:
              - name: "id"
                in: "path"
                required: true
                type: "string"
              responses: {}
              x-amazon-apigateway-integration:
                uri:
                  Fn::Sub: arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${GetPersonByHTTPParamFunction.Arn}/invocations
                responses: {}
                httpMethod: "POST"
                type: "aws_proxy"
        x-amazon-apigateway-request-validators:
          Validate query string parameters and headers:
            validateRequestParameters: true
            validateRequestBody: false

В DefinitionBody мы определяем три параметра: версию swagger как «2.0», info:title: как «TestAPI», а также набор путей.

«Как мы видим, пути представляют собой структуру API, которую раньше приходилось определять вручную. Пути в Swagger эквивалентны ресурсам в консоли AWS. Точно так же каждый путь может иметь один или несколько глаголов HTTP, которые эквивалентны методам в консоли AWS.

Каждый метод может иметь один или несколько параметров, а также валидатор запроса.

Самая захватывающая часть — это атрибут x-amazon-apigateway-integration, который является специфичным для AWS расширением Swagger:

uri указывает, какая функция Lambda должна быть вызвана.

ответы определяют правила преобразования ответов, возвращаемых функцией. Поскольку мы используем интеграцию Lambda Proxy, нам не нужно какое-либо конкретное правило. Тип

определяет, что мы хотим использовать интеграцию Lambda Proxy, поэтому мы должны установить для httpMethod значение «POST», поскольку именно этого ожидают функции Lambda.

Полный список поддерживаемых свойств API можно найти в официальной спецификации.

4.5. Неявное определение API

Второй вариант — неявное определение API в ресурсах функций:

Как мы видим, наш шаблон теперь немного отличается: ресурса AWS::Serverless::Api больше нет. .

AWSTemplateFormatVersion: '2010-09-09'
Transform: 'AWS::Serverless-2016-10-31'
Description: Baeldung Serverless Application Model Example with Implicit API Definition
 
Globals:
  Api:
    EndpointConfiguration: REGIONAL
    Name: "TestAPI"
 
Resources:
  StorePersonFunction:
    Type: AWS::Serverless::Function
    Properties:
      Handler: com.baeldung.lambda.apigateway.APIDemoHandler::handleRequest
      Runtime: java8
      Timeout: 15
      MemorySize: 512
      CodeUri: ../target/aws-lambda-0.1.0-SNAPSHOT.jar
      Policies:
        - DynamoDBCrudPolicy:
            TableName: !Ref PersonTable
      Environment:
        Variables:
          TABLE_NAME: !Ref PersonTable
      Events:
        StoreApi:
          Type: Api
          Properties:
            Path: /persons
            Method: PUT
  GetPersonByHTTPParamFunction:
    Type: AWS::Serverless::Function
    Properties:
      Handler: com.baeldung.lambda.apigateway.APIDemoHandler::handleGetByParam
      Runtime: java8
      Timeout: 15
      MemorySize: 512
      CodeUri: ../target/aws-lambda-0.1.0-SNAPSHOT.jar
      Policies:
        - DynamoDBReadPolicy:
            TableName: !Ref PersonTable
      Environment:
        Variables:
          TABLE_NAME: !Ref PersonTable
      Events:
        GetByPathApi:
          Type: Api
          Properties:
            Path: /persons/{id}
            Method: GET
        GetByQueryApi:
          Type: Api
          Properties:
            Path: /persons
            Method: GET

Однако CloudFormation принимает атрибуты Events типа Api как неявное определение и все равно создает API. Как только мы протестируем наше приложение, мы увидим, что оно ведет себя так же, как при явном определении API с помощью Swagger.

Кроме того, есть раздел Globals, где мы можем определить имя нашего API, а также то, что наша конечная точка должна быть региональной.

Есть только одно ограничение: при неявном определении API мы не можем установить имя этапа. Вот почему AWS в любом случае создаст этап Prod.

5. Развертывание и тестирование

После создания шаблона мы можем приступить к развертыванию и тестированию.

Для этого мы загрузим наш код функции в S3 до запуска фактического развертывания.

В конце концов, мы можем протестировать наше приложение с помощью любого HTTP-клиента.

5.1. Загрузка кода в S3

На первом этапе мы должны загрузить код функции в S3.

Мы можем сделать это, вызвав CloudFormation через интерфейс командной строки AWS:

С помощью этой команды мы запускаем CloudFormation, чтобы взять код функции, указанный в CodeUri:, и загрузить его на S3. CloudFormation создаст файл packaged-template.yml с тем же содержимым, за исключением того, что CodeUri: теперь указывает на объект S3.

$> aws cloudformation package --template-file ./sam-templates/template.yml --s3-bucket baeldung-sam-bucket --output-template-file ./sam-templates/packaged-template.yml

Давайте посмотрим на вывод CLI:

5.2. Развертывание

Uploading to 4b445c195c24d05d8a9eee4cd07f34d0 92702076 / 92702076.0 (100.00%)
Successfully packaged artifacts and wrote output template to file packaged-template.yml.
Execute the following command to deploy the packaged template
aws cloudformation deploy --template-file c:\zz_workspace\tutorials\aws-lambda\sam-templates\packaged-template.yml --stack-name <YOUR STACK NAME>

Теперь мы можем инициировать фактическое развертывание:

Поскольку нашему стеку также нужны роли IAM (например, роли функций для доступа к нашей таблице DynamoDB), мы должны явно подтвердить это, указав – параметр возможностей.

$> aws cloudformation deploy --template-file ./sam-templates/packaged-template.yml --stack-name baeldung-sam-stack  --capabilities CAPABILITY_IAM

И вывод CLI должен выглядеть так:

5.3. Обзор развертывания

Waiting for changeset to be created..
Waiting for stack create/update to complete
Successfully created/updated stack - baeldung-sam-stack

После развертывания мы можем просмотреть результат:

CloudFormation выведет список всех ресурсов, которые являются частью нашего стека.

$> aws cloudformation describe-stack-resources --stack-name baeldung-sam-stack

5.4. Test

Наконец, мы можем протестировать наше приложение с помощью любого HTTP-клиента.

Давайте посмотрим на некоторые примеры команд cURL, которые мы можем использовать для этих тестов.

StorePersonFunction:

GetPersonByPathParamFunction:

$> curl -X PUT 'https://0skaqfgdw4.execute-api.eu-central-1.amazonaws.com/test/persons' \
   -H 'content-type: application/json' \
   -d '{"id": 1, "name": "John Doe"}'

GetPersonByQueryParamFunction:

$> curl -X GET 'https://0skaqfgdw4.execute-api.eu-central-1.amazonaws.com/test/persons/1' \
   -H 'content-type: application/json'

5.5. Очистка

$> curl -X GET 'https://0skaqfgdw4.execute-api.eu-central-1.amazonaws.com/test/persons?id=1' \
   -H 'content-type: application/json'

В конце мы можем очистить, удалив стек и все включенные ресурсы:

6. Заключение

aws cloudformation delete-stack --stack-name baeldung-sam-stack

В этой статье мы рассмотрели модель бессерверных приложений AWS (SAM ), который обеспечивает описание на основе шаблонов и автоматическое развертывание бессерверных приложений на AWS.

Подробно мы обсудили следующие темы:

Основы модели бессерверных приложений (SAM), а также лежащее в основе CloudFormation определение бессерверного приложения с использованием синтаксиса шаблона SAM Автоматическое развертывание приложения с использованием CloudFormation CLI

    Как обычно, весь код для этой статьи доступен на GitHub.

«