«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.
«