Введение
В этом руководстве мы собираемся развернуть функцию AWS Lambda с помощью CodePipeline и AWS SAM, чтобы постоянно интегрировать и развертывать изменения нашего кода в облаке, но вместо использования консоли управления AWS мы напишем в CloudFormation шаблон, благодаря чему вы можете легко подготовить инфраструктуру для каждой среды разработки, а также внедрить ее в каждый проект на основе AWS SAM.
Архитектура
Как мы можем видеть на диаграмме выше, мы собираемся написать 2 шаблона кода, первый будет шаблоном CloudFormation и будет отвечать за базовую инфраструктуру, которая в данном случае в основном является конвейером, второй будет шаблон AWS SAM, который будет шаблоном для развертывания нашей сервисной инфраструктуры.
Руки вверх!
Во-первых, давайте создадим пару файлов и каталогов, которые будут находиться в одном месте:
Начнем с написания кода нашей функции Lambda в файле main.js в папке /src:
const AWS = require('aws-sdk') let dynamodb = new AWS.DynamoDB.DocumentClient({ region: 'us-east-1'}) exports.handler = (event, context, callback) => { let params = { TableName: process.env.ddbTable, Item: { pk: "[email protected]", sk: "user", first_name: "Marco", last_name: "Mercado" }, ConditionExpression: "attribute_not_exists(pk)", ReturnConsumedCapacity: 'TOTAL' } dynamodb.put(params, (err, data) => { if (err) callback(err) else callback(null, data) }) }
Функция довольно проста, мы выполняем операцию помещения в таблицу DynamoDB с помощью AWS SDK для NodeJS. Единственное, что я хочу, чтобы вы заметили, это то, что в строке 7 мы вызываем переменную среды для таблицы DynamoDB, эта переменная будет определена в шаблоне SAM.
ПРИМЕЧАНИЕ. В этом примере файл package.json используется по умолчанию. Его можно создать, просто запустив npm init -y.
Теперь давайте создадим файл template.yml, в котором мы будем использовать AWS SAM для развертывания наших бессерверных ресурсов:
AWSTemplateFormatVersion: "2010-09-09" Transform: AWS::Serverless-2016-10-31 Description: > AWS SAM Lambda function example Parameters: Environment: Type: String Description: Environment name Globals: Function: Timeout: 3 Resources: DdbTable: Type: AWS::DynamoDB::Table DeletionPolicy: Retain Properties: TableName: !Sub tutorial-dynamodb-cicd-${Environment} AttributeDefinitions: - AttributeName: pk AttributeType: S - AttributeName: sk AttributeType: S KeySchema: - AttributeName: pk KeyType: HASH - AttributeName: sk KeyType: RANGE BillingMode: PAY_PER_REQUEST ProvisionedThroughput: ReadCapacityUnits: 0 WriteCapacityUnits: 0 MainFunction: Type: AWS::Serverless::Function Properties: FunctionName: !Sub sam-function-example-${Environment} CodeUri: src/ Handler: main.handler Runtime: nodejs12.x Environment: Variables: ddbTable: !Ref DdbTable Policies: - Statement: - Sid: DynamoDBPolicy Effect: Allow Action: - dynamodb:PutItem Resource: - !GetAtt DdbTable.Arn Outputs: MainFunction: Description: "Main Lambda Function ARN" Value: !GetAtt MainFunction.Arn
Шаблон будет развертывать таблицу DynamoDB со средой в качестве суффикса для разделения ресурсов по средам и функцию Lambda с ролью IAM, которая позволяет записывать в таблицу DynamoDB.
Хороший! на данный момент мы можем развернуть нашу функцию с помощью интерфейса командной строки AWS SAM, но это будет ручной шаг, мы хотим, чтобы каждый раз, когда разработчик помещает код в репозиторий, эти изменения автоматически создавались и развертывались.
Служба, в которой будет работать шаблон AWS SAM, называется CodeBuild. Зная, что нам нужно написать пошаговые инструкции по сборке и развертыванию нашего проекта с помощью AWS SAM, эти инструкции будут записаны в файле buildspec.yml в разделе папку .awscodepipeline, как следующий код:
version: 0.2 phases: build: commands: - sam build -t template.yml post_build: commands: - | sam deploy --no-confirm-changeset --no-fail-on-empty-changeset \ --s3-bucket $CODEPIPELINE_BUCKET \ --s3-prefix $STACK_NAME \ --region $AWS_REGION \ --stack-name $STACK_NAME \ --capabilities CAPABILITY_IAM \ --parameter-overrides \ Environment=$ENVIRONMENT
Теперь у нас есть компоненты для настройки нашего конвейера автоматизации, последний шаг — написать наш шаблон CloudFormation, расположенный в файле main.yml в каталоге .cloudformation, это самая длинная часть примера, поэтому я собираюсь ее разбить. по шагам, если вы хотите посмотреть весь файл, перейдите по ссылке на репозиторий GitHub в конце этого поста.
Сначала, как и для каждого шаблона CloudFormation, мы определяем версию и пишем краткое описание шаблона:
AWSTemplateFormatVersion: '2010-09-09' Description: 'CloudFormation template to create an automation pipeline for AWS SAM projects'
Далее определяем наши параметры:
Parameters: FunctionName: Type: String Description: Name of the function to deploy with the pipeline Environment: Type: String Description: Environment name GitHubRepo: Type: String Description: GitHub repository name GitHubUser: Type: String Description: GitHub user name GitHubOAuthToken: Type: String Description: GitHub auth token Resources:
Теперь мы собираемся написать, какие ресурсы будет создавать этот шаблон. Прежде всего, мы собираемся определить роли IAM, которые будут принимать CodePipeline и CodeBuild, чтобы иметь необходимые разрешения:
ПРИМЕЧАНИЕ. На этом этапе каждый блок кода будет иметь отступ в разделе Ресурсы.
Для кодбилда:
CodeBuildServiceRole: Type: AWS::IAM::Role Properties: Path: / AssumeRolePolicyDocument: Version: "2012-10-17" Statement: - Effect: Allow Principal: Service: codebuild.amazonaws.com Action: sts:AssumeRole Policies: - PolicyName: "SAM" PolicyDocument: Version: "2012-10-17" Statement: - Effect: Allow Action: - dynamodb:DescribeTable - dynamodb:CreateTable - dynamodb:DeleteTable Resource: '*' - Effect: Allow Action: - cloudformation:CreateChangeSet - cloudformation:DescribeChangeSet - cloudformation:ExecuteChangeSet - cloudformation:DescribeStackEvents - cloudformation:DescribeStacks - cloudformation:GetTemplateSummary - cloudformation:UpdateStack Resource: '*' - Effect: Allow Action: - lambda:AddPermission - lambda:CreateFunction - lambda:DeleteFunction - lambda:GetFunction - lambda:GetFunctionConfiguration - lambda:ListTags - lambda:RemovePermission - lambda:TagResource - lambda:UntagResource - lambda:UpdateFunctionCode - lambda:UpdateFunctionConfiguration Resource: arn:aws:lambda:*:*:function:* - Effect: Allow Action: - iam:PassRole Resource: "*" Condition: StringEquals: iam:PassedToService: lambda.amazonaws.com - Effect: Allow Action: - iam:CreateRole - iam:DetachRolePolicy - iam:PutRolePolicy - iam:AttachRolePolicy - iam:DeleteRole - iam:GetRole Resource: "arn:aws:iam::*:role/*" - PolicyName: "logs" PolicyDocument: Version: "2012-10-17" Statement: - Effect: Allow Action: - logs:CreateLogGroup - logs:CreateLogStream - logs:PutLogEvents - ssm:GetParameters Resource: '*' - PolicyName: "S3" PolicyDocument: Version: "2012-10-17" Statement: - Effect: Allow Action: - s3:GetObject - s3:PutObject - s3:GetObjectVersion Resource: !Sub arn:aws:s3:::${ArtifactBucket}/*
Для CodePipeline:
CodePipelineServiceRole: Type: AWS::IAM::Role Properties: Path: / AssumeRolePolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Principal: Service: codepipeline.amazonaws.com Action: sts:AssumeRole Policies: - PolicyName: root PolicyDocument: Version: 2012-10-17 Statement: - Resource: - !Sub arn:aws:s3:::${ArtifactBucket}/* - !Sub arn:aws:s3:::${ArtifactBucket} Effect: Allow Action: - s3:* - Resource: "*" Effect: Allow Action: - codebuild:StartBuild - codebuild:BatchGetBuilds - iam:PassRole - Resource: "*" Effect: Allow Action: - codecommit:CancelUplodaArchive - codecommit:GetBranch - codecommit:GetCommit - codecommit:GetUploadArchiveStatus - codecommit:UploadArchive
Далее мы собираемся создать корзину S3 для хранения наших артефактов:
ArtifactBucket: Type: AWS::S3::Bucket DeletionPolicy: Delete
Теперь нам нужен проект CodeBuild, помните, что CodeBuild — это вычислительный экземпляр, управляемый , который будет выполнять список шагов для сборки вашего проекта, поэтому нам нужно настроить этот экземпляр:
CodeBuildProject: Type: AWS::CodeBuild::Project Properties: Artifacts: Type: CODEPIPELINE Source: Type: CODEPIPELINE BuildSpec: .awscodepipeline/buildspec.yml Environment: ComputeType: BUILD_GENERAL1_SMALL Image: aws/codebuild/amazonlinux2-x86_64-standard:2.0 Type: LINUX_CONTAINER EnvironmentVariables: - Name: CODEPIPELINE_BUCKET Type: PLAINTEXT Value: !Ref ArtifactBucket - Name: STACK_NAME Type: PLAINTEXT Value: !Sub ${FunctionName}-${Environment} - Name: ENVIRONMENT Type: PLAINTEXT Value: !Ref Environment Name: !Ref AWS::StackName ServiceRole: !Ref CodeBuildServiceRole
Наконец, мы собираем все вместе в конвейер CodePipeline, помните, что CodePipeline — это служба оркестровки, в этом примере координирующая репозиторий GitHub как источник нашего кода с нашим проектом CodeBuild, который собирается развернуть наши бессерверные ресурсы.
Pipeline: Type: AWS::CodePipeline::Pipeline Properties: Name: !Sub ${FunctionName}-${Environment} RoleArn: !GetAtt CodePipelineServiceRole.Arn ArtifactStore: Type: S3 Location: !Ref ArtifactBucket Stages: - Name: Source Actions: - Name: Function ActionTypeId: Category: Source Owner: ThirdParty Version: '1' Provider: GitHub Configuration: Owner: !Ref GitHubUser Repo: !Ref GitHubRepo Branch: !Ref Environment OAuthToken: !Ref GitHubOAuthToken RunOrder: 1 OutputArtifacts: - Name: SourceArtifact - Name: Build Actions: - Name: Build ActionTypeId: Category: Build Owner: AWS Version: '1' Provider: CodeBuild Configuration: ProjectName: !Ref CodeBuildProject InputArtifacts: - Name: SourceArtifact OutputArtifacts: - Name: BuildArtifact RunOrder: 2
Прежде чем развертывать проект, убедитесь, что ваш код обновлен в репозитории GitHub в ветке, которая соответствует имени среды, которую вы собираетесь выбрать (т. е. staging).
Теперь мы можем развернуть наш проект с помощью следующей команды с помощью интерфейса командной строки AWS:
ПРИМЕЧАНИЕ. Важно, чтобы на этом этапе у вас был ранее установлен интерфейс командной строки AWS и токен доступа GitHub OAuth, если вы не знаете, как сгенерировать свой токен или как установить интерфейс командной строки AWS. оставлю вам пару полезных ссылок.
Создать токен доступа GitHub OAuth
aws cloudformation create-stack \ --stack-name lambda-cicd-post \ --template-body file://.cloudformation/main.yml \ --parameters \ ParameterKey=FunctionName,ParameterValue=<LAMBDA_FUNCTION_NAME> \ ParameterKey=Environment,ParameterValue=<ENVIRONMENT> \ ParameterKey=GitHubUser,ParameterValue=<GITHUB_USER> \ ParameterKey=GitHubRepo,ParameterValue=<REPO_NAME> \ ParameterKey=GitHubOAuthToken,ParameterValue=<GITHUB_OAUTH_TOKEN> \ --region us-east-1 \ --capabilities CAPABILITY_IAM
Должно произойти следующее:
Стек должен успешно завершиться в CloudFormation:
Следует создать конвейер CodePipeline, который немедленно запускает свой первый запуск:
Этот первый запуск должен создать следующий стек:
И теперь вы сможете проверить свою функцию:
И это все для этого урока, помните, что весь код будет здесь, увидимся в следующий раз!