LambdaのCDをCodePipelineとCloudFormationで構築してみる
おはようございます、もきゅりんです。
タイトル通りですが、CodePipelineを利用したLambdaのCD(Continuous Deployment)をCloudFormation(以下CFn)で構築してみたのでまとめました。
Lambdaを手元でわちゃわちゃやった後に、デプロイしたいときはリポジトリにプッシュすればよろしおす、便利よね、ということでCFnにしてみました。
色々なやり方があるとは思いますが、元ネタはこちらです。
AWS CodePipeline を使用して Lambda アプリケーションの継続的な配信パイプラインを構築する
この例では、CodeCommitのGitリポジトリを利用します。
前提条件
- CodeCommitが利用できる状態であること
- AWS CLIインストール&設定済み
CodeCommitリポジトリの作成については、『CodeCommit ユーザーガイド』の「セットアップ」を参照してください。
ファイルをコミットしてCodeCommitにプッシュ
プッシュするファイルを作成していきます。
現在の時刻を返すLambda関数。
# lambda_function.py from datetime import datetime, timedelta, timezone def lambda_handler(event, context): JST = timezone(timedelta(hours=+9), 'JST') time = datetime.now(JST) print("What time is it now...?") print(time)
アプリケーションを定義するSAMテンプレート。
SAMとは何ぞや?という話はここではしませんので、気になる方は、下記が参考ドキュメントです。
AWSサーバーレスアプリケーションモデル(AWS SAM)とは何ですか?
# template.yaml AWSTemplateFormatVersion: '2010-09-09' Transform: AWS::Serverless-2016-10-31 Description: Outputs the time Resources: TimeFunction: Type: AWS::Serverless::Function Properties: Handler: lambda_function.lambda_handler Runtime: python3.7 CodeUri: ./
上記のファイルをコミットし、CodeCommitにプッシュします。
$ git add . $ git commit -m "add project files" $ git push
CodePipelineの作成
下記のテンプレートで作成します。
BuildSpecの内容については、実際に利用する場合は、適宜修正を施す必要がありそうです。
なお、IAMRoleですが結構雑に設定しているので、ちゃんと利用する場合は、最小権限の原則?(そんな称し方でいいのか??)に準拠しましょう。
# lambda_codepipeline_demo.yaml AWSTemplateFormatVersion: 2010-09-09 Description: CodePipeline For Lambda Deploy Parameters: CodeCommitRepositoryName: Type: String PipelineName: Type: String BucketName: Type: String Resources: # CodeWatchEventを実行できるIAMRole AmazonCloudWatchEventRole: Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Principal: Service: - events.amazonaws.com Action: sts:AssumeRole Path: / Policies: - PolicyName: cwe-pipeline-execution PolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Action: codepipeline:StartPipelineExecution Resource: !Join - "" - - "arn:aws:codepipeline:" - !Ref "AWS::Region" - ":" - !Ref "AWS::AccountId" - ":" - !Ref "PipelineName" # CloudFormationに適用するIAMRole CFnLambdaPipeline: Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Action: sts:AssumeRole Principal: Service: cloudformation.amazonaws.com Path: / ManagedPolicyArns: - arn:aws:iam::aws:policy/AdministratorAccess # CodeBuildに適用するIAMRole CodeBuildServiceRole: Type: AWS::IAM::Role Properties: Path: / AssumeRolePolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Principal: Service: codebuild.amazonaws.com Action: sts:AssumeRole ManagedPolicyArns: - arn:aws:iam::aws:policy/AmazonS3FullAccess Policies: - PolicyName: CodeBuildAccess PolicyDocument: Version: "2012-10-17" Statement: - Resource: "*" Effect: Allow Action: - logs:CreateLogGroup - logs:CreateLogStream - logs:PutLogEvents # CodePipelineに適用するIAMRole 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: SamplePipelinePolicy PolicyDocument: Version: 2012-10-17 Statement: - Resource: - !Sub arn:aws:s3:::${ArtifactBucket}/* Effect: Allow Action: - s3:PutObject - s3:GetObject - s3:GetObjectVersion - s3:GetBucketVersioning - Resource: "*" Effect: Allow Action: - cloudformation:* - codecommit:* - codedeploy:* - codebuild:* - s3:* - ecs:* - elasticloadbalancing:* - autoscaling:* - iam:PassRole # S3Bucket ArtifactBucket: Type: AWS::S3::Bucket Properties: BucketName: !Ref BucketName # CloudWatchEventの実行ルール AmazonCloudWatchEventRule: Type: AWS::Events::Rule Properties: EventPattern: source: - aws.codecommit detail-type: - "CodeCommit Repository State Change" resources: - !Join - "" - - "arn:aws:codecommit:" - !Ref "AWS::Region" - ":" - !Ref "AWS::AccountId" - ":" - !Ref "CodeCommitRepositoryName" detail: event: - referenceCreated - referenceUpdated referenceType: - branch referenceName: - master Targets: - Arn: !Join - "" - - "arn:aws:codepipeline:" - !Ref "AWS::Region" - ":" - !Ref "AWS::AccountId" - ":" - !Ref "PipelineName" RoleArn: !GetAtt AmazonCloudWatchEventRole.Arn Id: codepipeline-AppPipeline # CodeBuildProject CodeBuildProject: Type: AWS::CodeBuild::Project Properties: Artifacts: Type: CODEPIPELINE Source: Type: CODEPIPELINE BuildSpec: | version: 0.2 phases: install: runtime-versions: python: 3.7 commands: - aws cloudformation package --template-file template.yaml --s3-bucket $BUCKET_NAME --output-template-file outputtemplate.yaml artifacts: type: zip files: - template.yaml - outputtemplate.yaml Environment: ComputeType: BUILD_GENERAL1_SMALL Image: aws/codebuild/docker:18.09.0-1.7.0 Type: LINUX_CONTAINER EnvironmentVariables: - Name: BUCKET_NAME Value: !Ref BucketName Name: !Ref AWS::StackName ServiceRole: !Ref CodeBuildServiceRole # CodePipeLine Pipeline: Type: AWS::CodePipeline::Pipeline Properties: RoleArn: !GetAtt CodePipelineServiceRole.Arn ArtifactStore: Type: S3 Location: !Ref ArtifactBucket Stages: - Name: Source Actions: - Name: Source ActionTypeId: Category: Source Owner: AWS Version: 1 Provider: CodeCommit Configuration: RepositoryName: !Ref CodeCommitRepositoryName PollForSourceChanges: false BranchName: master RunOrder: 1 OutputArtifacts: - Name: SourceArtifact - Name: Build Actions: - Name: Build ActionTypeId: Category: Build Owner: AWS Version: 1 Provider: CodeBuild Configuration: ProjectName: !Ref CodeBuildProject RunOrder: 1 InputArtifacts: - Name: SourceArtifact OutputArtifacts: - Name: BuildArtifact - Name: Deploy Actions: - InputArtifacts: - Name: BuildArtifact Name: deploy ActionTypeId: Category: Deploy Owner: AWS Version: 1 Provider: CloudFormation RunOrder: 2 Configuration: ActionMode: CHANGE_SET_REPLACE ChangeSetName: changeset RoleArn: !GetAtt CFnLambdaPipeline.Arn Capabilities: CAPABILITY_IAM StackName: lambda-pipeline-changeset TemplatePath: BuildArtifact::outputtemplate.yaml - InputArtifacts: - Name: BuildArtifact Name: execute-changeset ActionTypeId: Category: Deploy Owner: AWS Version: 1 Provider: CloudFormation RunOrder: 3 Configuration: ActionMode: CHANGE_SET_EXECUTE StackName: lambda-pipeline-changeset ChangeSetName: changeset RoleArn: !GetAtt CFnLambdaPipeline.Arn
スタックを作成します。
BUCKETNAMEは好きなバケット名で入力して下さい。
どうでもいいのですが、最近はdeployでスタック作成しています。
aws cloudformation deploy --template-file lambda_codepipeline_demo.yaml --capabilities CAPABILITY_NAMED_IAM \ --stack-name lambda-deploy --parameter-overrides \ CodeCommitRepositoryName=YOUR_REPOSITORY_NAME PipelineName=lambda-deploy-pipeline BucketName=BUCKETNAME
CodePipelineを見に行くと、こんな感じで進行しました。
lambdaのコンソールにいくとこんなのがデプロイされます。
叩くと時間が表示されます。
その後、マスターブランチに変更をプッシュして、デプロイをトリガーするとクリクリとパイプラインが発動します。
結果のステータスをSlackとかに投げてあげたりすると、また親切ですねー。
以上です、どなたかのお役に立てば幸いです。
参考
AWS CodePipeline を使用して Lambda アプリケーションの継続的な配信パイプラインを構築する
AWS Serverless Application Model (SAM)
Updating My AWS CodeBuild Project from Ubuntu 14.04 to 18.04