Введение в модель сервера без AWS

Введение в модель сервера приложений AWS

1. обзор

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

Однако развертывание состоит из множества этапов, выполняемых вручную, что может оказаться затруднительным из-за растущей сложности и количества сред.

В этом руководстве мы обсудим, как использоватьAWS Serverless Application Model (SAM), which enables a template-based description and automated deployment of serverless applications on AWS.

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

  • Основы модели серверных приложений (SAM), а также базовой CloudFormation

  • Определение безсерверного приложения с использованием синтаксиса шаблона SAM

  • Автоматическое развертывание приложения с использованием CloudFormation CLI

2. основы

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

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

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

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

2.1. AWS CloudFormation

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

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

A template is a description of an application, как он должен быть структурирован во время выполнения. Мы можем определить набор необходимых ресурсов, а также то, как эти ресурсы должны быть настроены. CloudFormation предоставляет общий язык для определения шаблонов, поддерживая JSON и YAML в качестве формата.

Resources are the building blocks in CloudFormation. Ресурсом может быть что угодно, например RestApi, этап RestApi, пакетное задание, таблица DynamoDB, экземпляр EC2, сетевой интерфейс, роль IAM и многие другие. The official documentation в настоящее время перечисляет около 300 типов ресурсов для CloudFormation.

A stack is the instantiation of a template. CloudFormation позаботится о предоставлении и настройке стека.

2.2. Модель бессерверного приложения (SAM)

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

Вот почему Amazon представила модель бессерверных приложений (SAM). SAM started with the claim to provide a clean and straightforward syntax for defining serverless applications. Currently, it has only three resource types, which are Lambda functions, DynamoDB tables, and APIs.

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

Более подробная информация доступна как вat the official GitHub repository, так и вAWS documentation.

3. Предпосылки

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

Кроме того, нам нужен AWS CLIinstalled.

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

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

Хотя в следующем руководстве используетсяexample-sam-bucket, имейте в виду, что имена сегментов должны быть уникальными, поэтому вы должны выбрать свое имя.

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

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

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

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

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

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

AWSTemplateFormatVersion: '2010-09-09'
Transform: 'AWS::Serverless-2016-10-31'
Description: example 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). Мы также можем указатьDescription.

Тело состоит из набора ресурсов: каждый ресурс имеет имя, ресурсType и наборProperties.

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

Поскольку мы хотим развернуть нашexample application, мы должны определить одинSimpleTable, дваFunctions, а также одинApi в нашем теле шаблона.

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

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

AWSTemplateFormatVersion: '2010-09-09'
Transform: 'AWS::Serverless-2016-10-31'
Description: example Serverless Application Model example

Resources:
  PersonTable:
    Type: AWS::Serverless::SimpleTable
    Properties:
      PrimaryKey:
          Name: id
          Type: Number
      TableName: Person

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

Полный список поддерживаемых свойствSimpleTable можно найти вin the official specification.

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

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

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

AWSTemplateFormatVersion: '2010-09-09'
Transform: 'AWS::Serverless-2016-10-31'
Description: example Serverless Application Model example

Resources:
  StorePersonFunction:
    Type: AWS::Serverless::Function
    Properties:
      Handler: com.example.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.example.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

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

Handler defines the logic of the function. Поскольку мы используем Java, это имя класса, включая пакет, в связи с именем метода.

Runtime defines how the function was implemented, в нашем случае это Java 8.

Timeout defines how long the execution of the code may take at most до того, как AWS завершит выполнение.

MemorySizedefines the size of the assigned memory in MB. Важно знать, что AWS распределяет ресурсы ЦП пропорциональноMemorySize. Таким образом, в случае функции, интенсивно использующей ЦП, может потребоваться увеличитьMemorySize, даже если функции не требуется столько памяти.

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

Policiescan hold a set of AWS-managed IAM policies or SAM-specific policy templates. Мы используем специальные политики SAMDynamoDBCrudPolicy дляStorePersonFunction иDynamoDBReadPolicy дляGetPersonByPathParamFunction иGetPersonByQueryParamFunction.

Environmentdefines environment properties at runtime. Мы используем переменную среды для хранения имени нашей таблицы DynamoDB.

Eventscan hold a set of AWS events, which shall be able to trigger the function. В нашем случае мы определяемEvent типаApi. Уникальная комбинацияpath, HTTPMethod иRestApiId связывает функцию с методом нашего API, который мы определим в следующем разделе.

Полный список поддерживаемых свойствFunction можно найти вin the official specification.

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

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

Первая возможность - определить наш встроенный API-интерфейс в формате Swagger:

AWSTemplateFormatVersion: '2010-09-09'
Transform: 'AWS::Serverless-2016-10-31'
Description: example 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

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

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

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

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

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

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

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

type определяет, что мы хотим использовать интеграцию с лямбда-прокси, и поэтому мы должны установитьhttpMethod на“POST”, так как это то, что ожидают лямбда-функции.

Полный список поддерживаемых свойствApi можно найти вin the official specification.

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

Второй вариант - это неявное определение API в ресурсах Function:

AWSTemplateFormatVersion: '2010-09-09'
Transform: 'AWS::Serverless-2016-10-31'
Description: example Serverless Application Model Example with Implicit API Definition

Globals:
  Api:
    EndpointConfiguration: REGIONAL
    Name: "TestAPI"

Resources:
  StorePersonFunction:
    Type: AWS::Serverless::Function
    Properties:
      Handler: com.example.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.example.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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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 

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

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

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

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

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

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

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

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

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

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

5.4. Test

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

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

StorePersonFunction:

$> 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"}'

GetPersonByPathParamFunction:

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

GetPersonByQueryParamFunction:

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

5.5. Очистить

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

aws cloudformation delete-stack --stack-name example-sam-stack

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

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

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

  • Основы модели серверного приложения (SAM), а также базовая CloudFormation

  • Определение безсерверного приложения с использованием синтаксиса шаблона SAM

  • Автоматическое развертывание приложения с использованием CloudFormation CLI

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