1. Обзор
В нашей предыдущей статье мы уже реализовали бессерверное приложение с полным стеком на AWS, используя API Gateway для конечных точек REST, AWS Lambda для бизнес-логики, а также DynamoDB в качестве базы данных.
Однако развертывание состоит из множества ручных шагов, которые могут стать неудобными с ростом сложности и количества сред.
В этом руководстве мы обсудим, как использовать модель бессерверных приложений AWS (SAM), которая обеспечивает описание на основе шаблонов и автоматическое развертывание бессерверных приложений в AWS .
Подробно рассмотрим следующие темы:
- Основы модели бессерверных приложений (SAM), а также базового CloudFormation
- Определение бессерверного приложения с использованием синтаксиса шаблона SAM
- Автоматическое развертывание приложения с помощью CloudFormation CLI
2. Основы
Как обсуждалось ранее , AWS позволяет нам реализовывать полностью бессерверные приложения с помощью шлюза API, функций 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://foreach-sam-bucket
Хотя в дальнейшем в руководстве используется foreach-sam-bucket
, имейте в виду, что имена сегментов должны быть уникальными, поэтому вам нужно выбрать свое имя.
В качестве демонстрационного приложения мы будем использовать код из Использование AWS Lambda с API Gateway .
4. Создание шаблона
В этом разделе мы создадим наш шаблон SAM.
Сначала мы рассмотрим общую структуру, прежде чем определять отдельные ресурсы.
4.1. Структура шаблона
Во-первых, давайте посмотрим на общую структуру нашего шаблона:
AWSTemplateFormatVersion: '2010-09-09'
Transform: 'AWS::Serverless-2016-10-31'
Description: ForEach 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
.
Тело состоит из набора ресурсов: у каждого ресурса есть имя, тип
ресурса и набор свойств
.
В настоящее время спецификация SAM поддерживает три типа: AWS::Serverless::Api
, AWS::Serverless::Function
, а также AWS::Serverless::SimpleTable
.
Поскольку мы хотим развернуть наше примерное приложение , мы должны определить одну SimpleTable
, две Functions
, а также один API
в теле нашего шаблона.
4.2. Определение таблицы DynamoDB
Давайте теперь определим нашу таблицу DynamoDB:
AWSTemplateFormatVersion: '2010-09-09'
Transform: 'AWS::Serverless-2016-10-31'
Description: ForEach Serverless Application Model example
Resources:
PersonTable:
Type: AWS::Serverless::SimpleTable
Properties:
PrimaryKey:
Name: id
Type: Number
TableName: Person
Нам нужно только определить два свойства для нашей SimpleTable
: имя таблицы, а также первичный ключ, который называется id
и имеет тип Number
в нашем случае.
Полный список поддерживаемых свойств SimpleTable
можно найти в официальной спецификации .
Примечание. Поскольку мы хотим получить доступ к таблице только с помощью первичного ключа, нам достаточно AWS::Serverless::SimpleTable
. Для более сложных требований вместо него можно использовать собственный тип CloudFormation AWS::DynamoDB::Table .
4.3. Определение лямбда-функций
Далее, давайте определим наши две функции:
AWSTemplateFormatVersion: '2010-09-09'
Transform: 'AWS::Serverless-2016-10-31'
Description: ForEach Serverless Application Model example
Resources:
StorePersonFunction:
Type: AWS::Serverless::Function
Properties:
Handler: com.foreach.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.foreach.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
.
Среда
определяет свойства среды во время выполнения. Мы используем переменную среды для хранения имени нашей таблицы DynamoDB.
События
могут содержать набор событий AWS, которые должны запускать функцию. В нашем случае мы определяем Event
типа Api
. Уникальная комбинация path
, HTTP Method
и RestApiId
связывает функцию с методом нашего API, который мы определим в следующем разделе.
Полный список поддерживаемых свойств Function
можно найти в официальной спецификации .
4.4. Определение API как файл Swagger
После определения таблицы и функций DynamoDB мы теперь можем определить API.
Первая возможность заключается в том, чтобы определить наш встроенный API, используя формат Swagger:
AWSTemplateFormatVersion: '2010-09-09'
Transform: 'AWS::Serverless-2016-10-31'
Description: ForEach 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»
, а также набор путей
.
Как мы видим, пути
представляют собой структуру API, которую нам приходилось определять вручную до . Пути
в Swagger эквивалентны ресурсам в консоли AWS. Точно так же каждый путь
может иметь один или несколько глаголов HTTP, которые эквивалентны методам в консоли AWS.
Каждый метод может иметь один или несколько параметров, а также валидатор запросов.
Самая захватывающая часть — это атрибут x-amazon-apigateway-integration
, который является специфичным для AWS расширением Swagger:
uri
указывает, какая функция Lambda должна быть вызвана.
ответы
определяют правила преобразования ответов, возвращаемых функцией. Поскольку мы используем интеграцию Lambda Proxy, нам не нужно какое-либо конкретное правило.
type
определяет, что мы хотим использовать интеграцию Lambda Proxy, поэтому мы должны установить для httpMethod
значение «POST»
, поскольку именно этого ожидают функции Lambda.
Полный список поддерживаемых свойств Api
можно найти в официальной спецификации .
4.5. Неявное определение API
Второй вариант — неявно определить API в ресурсах функции:
AWSTemplateFormatVersion: '2010-09-09'
Transform: 'AWS::Serverless-2016-10-31'
Description: ForEach Serverless Application Model Example with Implicit API Definition
Globals:
Api:
EndpointConfiguration: REGIONAL
Name: "TestAPI"
Resources:
StorePersonFunction:
Type: AWS::Serverless::Function
Properties:
Handler: com.foreach.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.foreach.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
Как мы видим, наш шаблон теперь немного изменился: больше нет ресурса AWS::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 foreach-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 <YOUR STACK NAME>
5.2. Развертывание
Теперь мы можем инициировать фактическое развертывание:
$> aws cloudformation deploy --template-file ./sam-templates/packaged-template.yml --stack-name foreach-sam-stack --capabilities CAPABILITY_IAM
Поскольку нашему стеку также нужны роли IAM (например, роли функций для доступа к нашей таблице DynamoDB), мы должны явно подтвердить это, указав параметр –capabilities
.
И вывод CLI должен выглядеть так:
Waiting for changeset to be created..
Waiting for stack create/update to complete
Successfully created/updated stack - foreach-sam-stack
5.3. Обзор развертывания
После развертывания мы можем просмотреть результат:
$> aws cloudformation describe-stack-resources --stack-name foreach-sam-stack
CloudFormation перечислит все ресурсы, которые являются частью нашего стека.
5.4. Тест
Наконец, мы можем протестировать наше приложение с помощью любого 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 foreach-sam-stack
6. Заключение
В этой статье мы рассмотрели модель бессерверных приложений AWS (SAM), которая позволяет создавать описания на основе шаблонов и автоматически развертывать бессерверные приложения в AWS.
Подробно мы обсудили следующие темы:
- Основы модели бессерверных приложений (SAM), а также базового CloudFormation
- Определение бессерверного приложения с использованием синтаксиса шаблона SAM
- Автоматическое развертывание приложения с помощью CloudFormation CLI
Как обычно, весь код для этой статьи доступен на GitHub .