Cloud Formationを使ってGolang製LambdaのCICDをGitHub Actionsで構築してみた
はじめに
こんにちは、SHIFT の開発部門に所属しているKatayamaです。今期から転属になり、開発を担当していくことになりました。 ただ、前期はDevOps導入支援等に携わっていた関係で今回はCICD関係の記事を書こうと思います。
以前に投稿させて頂いた記事(AWS SAM を使って Golang 製 Lambda のための CICD を GitHub Actions で構築してみた)では、AWS SAMを使った CICD を構築してみた。今回は AWS SAM の何がいいのかをより理解する目的+ちょっと Cloud Formation もやってみたかったという事で Cloud Formation を使った Lambda の CICD の構築をやってみた。
構成
AWS SAM を使って Golang 製 Lambda のための CICD を GitHub Actions で構築してみた#構成と全く同じなのでそちらを参照。
Lambda の CICD の準備(S3 Bucket を構築する template.yaml)
今回は zip をアプロードするタイプで CICD を構築するので S3 の Bucket が必要になる。まずはそれを Cloud Formation で作成する。
作成対象は S3 の Bucket のみだが、BucketPolicy も設定したいので以下のようなテンプレートにした。
AWSTemplateFormatVersion: 2010-09-09
Description: The AWS CloudFormation template for artifact S3 Bucket of cloudformation-go-lambda
Parameters:
UserName:
Type: String
Description: AWS user name.
Resources:
ArtifactBucket:
Type: "AWS::S3::Bucket"
Properties:
BucketName: aws-cloudformation-build-artifact-go-lambda
VersioningConfiguration:
Status: Enabled
ArtifactBucketBucketPolicy:
Type: "AWS::S3::BucketPolicy"
Properties:
PolicyDocument:
Statement:
- Action:
- "s3:GetObject"
Effect: Allow
Resource: !Join
- ""
- - "arn:"
- !Ref "AWS::Partition"
- ":s3:::"
- !Ref ArtifactBucket
- /*
Principal:
AWS: !Join
- ""
- - !Sub arn:aws:iam::${AWS::AccountId}:user/
- !Ref UserName
Bucket: !Ref ArtifactBucket
Outputs:
BucketName:
Description: Bucket for Lmabda build artifact.
Value: !GetAtt ArtifactBucket.Arn
※組み込み関数(!Join, !Ref など)を複数使うような場合は Fn:Join を使う(以下のような書き方はできない)。
AWS: !Sub !Ref arn:aws:iam::${AWS::AccountId}:user/UserName
上記のテンプレートを AWS CLI コマンドで deploy すればいいので、
aws cloudformation deploy --stack-name s3-bucket-for-cloudfromation-go-lambda --template-file s3-template.yaml --parameter-overrides UserName=xxxxxxxxxx
というコマンドを実行すれば、S3 Bucket が作成される。
※create-stack コマンドでも stack を作成する事はできるが、deploy を使う方が今後変更が入った時に change set が作られるので deploy にしている(change set とは以下の図の「変更セット」の事)。
・参考:AWS::S3::Bucket
・参考:AWS::S3::BucketPolicy
・参考:Principals
・参考:AWS JSON ポリシーの要素: Principal
・参考:Fn::Join
・参考:AWS::Partition
・参考:aws.cloudformation.deploy
・参考:Return values
Lambda 関数を構築する template.yaml
次に Lambda 関数を構築する template.yaml を書く。
AWSTemplateFormatVersion: "2010-09-09"
Description: The AWS CloudFormation template for cloudformation-go-lambda
Parameters:
BacklogApiKey:
Type: String
Description: Backlog api key.
BacklogDomein:
Type: String
Description: Backlog domein.
SlackApiToken:
Type: String
Description: Slack api token.
Resources:
GoLambdaFunction:
Type: AWS::Lambda::Function
Properties:
Code: ./main.zip
Description: Lambda function create by aws cloudformation.
Handler: main
FunctionName: cloudformation-go-lambda
Runtime: go1.x
Timeout: 3
Environment:
Variables:
BACKLOG_API_KEY: !Ref BacklogApiKey
BACKLOG_DOMEIN: !Ref BacklogDomein
BACKLOG_ISSUE_STATUS: 未対応
BACKLOG_STATUS_ID: 3
SLACK_API_TOKEN: !Ref SlackApiToken
SLACK_CHANNEL: "#lambda実行"
Role: !GetAtt GoLambdaFunctionRole.Arn
GoLambdaFunctionRole:
Type: "AWS::IAM::Role"
Properties:
AssumeRolePolicyDocument:
Version: 2012-10-17
Statement:
- Action:
- "sts:AssumeRole"
Effect: Allow
Principal:
Service:
- lambda.amazonaws.com
ManagedPolicyArns:
- "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
Policies:
- PolicyName: CloudFormationGoLambdaFunctionRolePolicy-CodeCommit
PolicyDocument:
Statement:
- Action:
- "codecommit:GetCommit"
Resource:
- !Sub "arn:aws:codecommit:ap-northeast-1:${AWS::AccountId}:*"
Effect: Allow
Sid: CodeCommit
Path: /service-role/
GoLambdaFunctionCodeCommitPushPermission:
Type: "AWS::Lambda::Permission"
Properties:
Action: "lambda:InvokeFunction"
Principal: events.amazonaws.com
FunctionName: !Ref GoLambdaFunction
SourceArn: !GetAtt GoLambdaFunctionCodeCommitPush.Arn
GoLambdaFunctionCodeCommitPush:
Type: "AWS::Events::Rule"
Properties:
EventPattern:
source:
- aws.codecommit
detail:
eventSource:
- codecommit.amazonaws.com
detail-type:
- CodeCommit Repository State Change
Targets:
- Id: GoLambdaFunctionCodeCommitPushLambdaTarget
Arn: !GetAtt GoLambdaFunction.Arn
Outputs:
GoLambdaFunction:
Description: "Go Lambda Function ARN"
Value: !GetAtt GoLambdaFunction.Arn
GoLambdaFunctionIamRole:
Description: "Implicit IAM Role created for Go function"
Value: !GetAtt GoLambdaFunctionRole.Arn
GoLambdaFunctionCodeCommitPush:
Description: Cloud Watch Events for Go function. Triger is Code Commit push.
Value: !GetAtt GoLambdaFunctionCodeCommitPush.Arn
以下 template.yaml について見ていく。
GoLambdaFunction Code
AWS::Lambda::Function Codeを見ると分かるが、 AWS SAM と違って template.yaml があるプロジェクト内のコードを指定する事はできない。
ZipFile:を使えば、Node.js/Python ではインラインでコードが書けるが、基本は S3 の zip or ECR を指定する事になると思う。
※今回は aws cloudformation package でローカルアーティファクトをパッケージ化し、その後 aws cloudformation deploy を実行する方法を取っているので、./main.zip になっている(これは Cloud Formation の UpdateStack を実行した時に、コードのみを変更している場合、テンプレートが変わらないので No updates are to be performed と言うエラーになるため)。
・参考:S3Bucket と S3Key による指定の際におこる問題
GoLambdaFunction Environment
環境変数を設定している。
※API key や Token などの他から見えてはいけないものは、aws cloudformation deploy 時の --parameter-overrides オプションで上書きする事で設定し、それを Resources の方で、組み込み関数 Ref で参照している。
・参考:パラメータ
・参考:AWS::Lambda::Function Environment
・参考:Ref
GoLambdaFunctionRole AssumeRolePolicyDocument
Role をアタッチできるサービスを設定している(信頼されたエンティティというやつ)。
※信頼されたエンティティについては、以下の図を参照。
・参考:AWS::IAM::Role
GoLambdaFunctionRole ManagedPolicyArns
Lambda 機能の AWS マネージドポリシーに書かれている AWSLambdaBasicExecutionRole をアタッチしている(Lambda の実行ログを CloudWatch にアップロードするためのアクセス権限)。
GoLambdaFunctionRole Policies
単純なインラインポリシー。今回は CodeCommit にアクセス(GetCommit)したいので、それを設定している。
GoLambdaFunctionRole Path
今回の Role は AWS の各サービスがユーザに代わって何かを行う権限を付与するためのロールなので、サービスロールに該当するのでIAM 識別子に基づいて、 /service-role/ とした。
GoLambdaFunctionCodeCommitPushPermission
Lambda 関数の権限で注意点で書いたとおり、Lambda 関数の権限には ① 実行ロール、② リソースベースのポリシーの 2 つがあり、② の方の設定をしないと Cloud Watch Events から Lambda をトリガーできない。
なのでここで permission の設定をしている。
GoLambdaFunctionCodeCommitPush
Cloud Watch Events(EventBridge)の設定を行っている部分で、Code Commit の push をトリガーに Lambda を呼び出す設定。
Outputs
Cloud Formation の stack の出力タブに表示させたいものを定義している。
・参考:AWS::Lambda::Function Return values
・参考:AWS::Events::Rule Return values
AWS Cloud Formation によるリソースの構築(GitHub Actions の CICD パイプライン)
AWS SAM による Build・Deploy と同様に、GitHub Actions 上で CICD を実行させるようにした。 GitHub Actions の pipeline.yaml は以下の通り。
name: lambda-build-deploy-with-cloudformation
on:
push:
branches: [cloudformation]
env:
ARTIFACTS_BUCKET: aws-cloudformation-build-artifact-go-lambda
jobs:
build-and-package:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Setup Go environment
uses: actions/setup-go@v2.1.3
with:
go-version: "1.17.1"
- name: Configure AWS Credentials
uses: aws-actions/configure-aws-credentials@v1
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: ap-northeast-1
- name: Go get aws lambda library
run: go get github.com/aws/aws-lambda-go/lambda
- name: Go build
run: GOOS=linux go build -o main lambda.go
- name: Create zip
run: zip main.zip main config.yaml
- name: Upload artifacts to artifact buckets
run: |
aws cloudformation package \
--template-file template.yaml \
--s3-bucket ${ARTIFACTS_BUCKET} \
--output-template-file packaged-template.yaml
- uses: actions/upload-artifact@v2
with:
name: packaged-template.yaml
path: packaged-template.yaml
deploy:
needs: [build-and-package]
runs-on: ubuntu-latest
steps:
- uses: actions/download-artifact@v2
with:
name: packaged-template.yaml
- name: Configure AWS Credentials
uses: aws-actions/configure-aws-credentials@v1
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: ap-northeast-1
- name: Cloudformation deploy
run: |
aws cloudformation deploy \
--template-file packaged-template.yaml \
--stack-name cloudformation-go-lambda \
--capabilities CAPABILITY_IAM \
--parameter-overrides BacklogApiKey=${{ secrets.BACKLOG_API_KEY }} BacklogDomein=${{ secrets.BACKLOG_DOMEIN }} SlackApiToken=${{ secrets.SLACK_API_TOKEN }}
以下で各コマンドについてみていく。
※今回は、Golang 製 Lambda のための CICD を GitHub Actions で構築してみたと同じような考え方で Build・Deploy を区切った。aws cloudformation package を行っている理由についてはS3Bucket と S3Key による指定の際におこる問題を参照。
go build
Golang 製 Lambda のための CICD を GitHub Actions で構築してみた#GitHub Actionsを参照。
aws cloudformation package/deploy
Lambda のコードの修正を stack に反映させるためにまず package を行い、その後その package 化されたものを deploy している。
・参考:aws.cloudformation.package
・参考:aws.cloudformation.deploy
まとめとして
Cloud Formation の場合、AWS SAM と違って自分で全てのリソースの設定をしなければならないので大変・・・。また、どのリソースタイプになるのか?も数が多いので把握が大変かもしれない。
やってみて感じたが、ほぼ AWS CLI と同じ事を yaml に書いてやる感じなので、やはり CLI のコマンドで構築する方法から、そのリソースタイプになるのか?プロパティは何か?を把握していくといいのかもと感じた。
ex) ECR へのレプリケーションをトリガーに ECS のサービス更新を実行する#AWS CLI コマンドからの設定の Cloud Watch Events の設定のように、CLI コマンドが分かればどのリソースか?が把握しやすい気がする。
__________________________________
お問合せはお気軽に
https://service.shiftinc.jp/contact/
SHIFTについて(コーポレートサイト)
https://www.shiftinc.jp/
SHIFTのサービスについて(サービスサイト)
https://service.shiftinc.jp/
SHIFTの導入事例
https://service.shiftinc.jp/case/
お役立ち資料はこちら
https://service.shiftinc.jp/resources/
SHIFTの採用情報はこちら
https://recruit.shiftinc.jp/career/