LambdaのCDをCodePipelineとCloudFormationで構築してみる

LambdaのCDをCodePipelineとCloudFormationで構築してみる

Clock Icon2019.06.10

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

おはようございます、もきゅりんです。

タイトル通りですが、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を見に行くと、こんな感じで進行しました。

pipeline1

pipeline2

lambdaのコンソールにいくとこんなのがデプロイされます。

lambda1

叩くと時間が表示されます。

lambda2

その後、マスターブランチに変更をプッシュして、デプロイをトリガーするとクリクリとパイプラインが発動します。

結果のステータスをSlackとかに投げてあげたりすると、また親切ですねー。

以上です、どなたかのお役に立てば幸いです。

参考

AWS CodePipeline を使用して Lambda アプリケーションの継続的な配信パイプラインを構築する

AWS Serverless Application Model (SAM)

Updating My AWS CodeBuild Project from Ubuntu 14.04 to 18.04

CodePipeline パイプライン構造のリファレンス

AWS CloudFormation 設定プロパティのリファレンス

Share this article

facebook logohatena logotwitter logo

© Classmethod, Inc. All rights reserved.