見出し画像

Golang製LambdaのためのCICDをGitHub Actionsで構築してみた

はじめに

はじめまして、SHIFT にて自動化アーキテクトとしてテスト自動化・DevOps 導入支援などをしています片山です。

CICD の一環で Go の Lambda を使って、

 ・Slack から AWS Chatbot で Lambda を呼び出せるようにする
 ・ Lambda から Slack 通知をする
 ・ Backlog チケットを更新する

というような仕組みを構築したが、肝心の Lambda を手動で deploy していたのでは意味ない・・・ という事で Lambda 関数の CICD を構築してみた。

※今回はすでに Lambda が存在する前提で、それを更新する部分を CICD で行った ※Go 言語は今回初めて触ったが、まあ他の言語で開発した事あるのもありなんとかなった。

どのようにやるのか?

CICD と言っても考え方は単純で、ローカルでやっている事を CICD 環境でやればいいだけ。なので、今回だと以下のような手順でローカルから Lambda 更新はできるのでそれを CICD 環境で実行すればいい。

1、 ローカルで Go の Lambda 用の build = zip ファイル作成と同じ手順で zip を作成
  zip 作成手順はmacOS および Linux での .zip ファイルの作成を参照

2、 s3 にファイルをアップロード(今回はファイルコピーなので s3 cp )
  コマンド: aws s3 cp {s3にupしたいファイル名} s3://{s3 bucket名}

3、 s3 のファイルで Lambda を更新
  コマンド: aws lambda update-function-code --function-name {lambda関数名} --s3-bucket {lambdaへupするzipのあるs3bucket名} --s3-key {lambdaにupするzipファイル名}

AWS CLI の各種コマンドのリファレンスは以下。

 ・ aws.s3.cp
 ・ aws.lambda.update-function-code

※CICD の CI と CD の区切りをどこにするのか?が 1 つのポイントになり、これの定義でパイプラインの中身にの構成が変化するが、今回は継続的インテグレーション、継続的デリバリー、継続的デプロイメントの 3 つの工程があると考え、

 ・ CI:継続的インテグレーション、継続的デリバリー
 ・ CD:継続的デプロイメント

として CICD のパイプラインは構築した。

・参考:CI/CD とは

GitHub Actions

GitGub Actions の yaml は以下のようになる

name: lambda-build-deploy

on:
  push:
    branches: [lambda]
  workflow_dispatch:

jobs:
  build:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v2

      - name: Setup Go environment
        uses: actions/setup-go@v2.1.3
        with:
          go-version: "1.16.7"

      - 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: 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: upload main.zip to s3
        run: aws s3 cp main.zip s3://go-lambda-deploy

  deploy:
    needs: build
    runs-on: ubuntu-latest

    steps:
      - 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: update Lambda function
        run: |
          aws lambda update-function-code --function-name go-lambda --s3-bucket go-lambda-deploy --s3-key main.zip\
          | jq '.FunctionName, .LastUpdateStatus'

一部補足をすると・・・

 ・go build -o main lambda.go
  build した後にできるバイナリの名前を main にしたいので -o main とし
  ている(lambda.goというファイル名にしているので、指定しないと lambda というバイナリができる)

 ・zip main.zip main config.yaml
  今回は config.yaml も Lambda 関数で読み込むので zip に取り込みたか
  ったためこのようにしている

 ・*| jq '.FunctionName, .LastUpdateStatus'*
  aws cli のリファレンスを読むと分かるが、response に Lambda 関数の
  環境変数が含まれているのでここに API KEY などがあると CI の log にそ 
  れが出力されてしまい NG。なので jq コマンドなどで表示させる項目を
  絞り込むようにする。何もしないと以下の図のようになってしまう
 (※Token は新規発行しなおしたので log に出ているものは失効済み)

IAM

s3 に upload し s3 から Lambda に deploy(zip を Lambda にアップロード)するのに必要な最小限の権限は以下

 ・ s3
   ・s3:PutObject
    バケットにオブジェクトを追加する権限

   ・s3:GetObject
    Amazon S3 からオブジェクトを取得する権限

 ・ lambda
   ・UpdateFunctionCode
    AWS Lambda 関数のコードを更新する権限

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "VisualEditor0",
      "Effect": "Allow",
      "Action": ["s3:PutObject", "s3:GetObject", "lambda:UpdateFunctionCode"],
      "Resource": [
        "arn:aws:s3:::go-lambda-deploy/main.zip",
        "arn:aws:lambda:ap-northeast-1:xxxxxxxxxx:function:go-lambda"
      ]
    }
  ]
}

Resource の部分についての解説としては以下。

 ・ aws:s3:::go-lambda-deploy/main.zip
  s3 は Region・Account の指定が不要なので空白になっている(
  s3:①:②:go-... の ① に Regionが、② に Account が、それぞれ本来指定
  される)。ユーザがアクセス可能なリソースを、 go-lambda-deploy で
  Bucket名の、 main.zip` でObject名の、指定をそれぞれしている

 ・ aws:lambda:ap-northeast-1:xxxxxxxxxx:function:go-lambda
  ユーザがアクセス可能なリソースを、 ap-northeast-1 でRegion名の、
  xxxxxxxxxx でAccount名の、 go-lambda でFunction名の、指定をそれぞ
  れしている

※IAM がちゃんと働いているかを見ると、以下のコマンドは Denied される

C:\Users\xxxxx\go>aws s3 cp lambda.go s3://go-lambda-deploy
upload failed: .\lambda.go to s3://go-lambda-deploy/lambda.go An error occurred (AccessDenied) when calling the PutObject operation: Access Denied

※今回は、S3 に Bucket を作成する(s3:CreateBucket)、Lambda を作成する(lambda:CreateFunction)など CICD の事前準備に必要な権限は記載せず、あくまで CICD のパイプラインを動かすのに必要な権限を記載している

まとめとして

今回は zip を作成して Deploy をするという方法を取ったが、AWS SAMを使ったDeploy 方法もあり、今後は SAM テンプレートを使った Deploy もやってみたい。また、CICD を AWS Code シリーズで構築するなどもできると思うのでそういった事もやってみたい。

参考文献

実際に今回の CICD を構築する上で参考にさせて頂いたサイトは以下の 3 つ。

 ・ AWS CLI のプロファイル切り替えをいい感じにする
 ・ オブジェクトのコピー
 ・ aws s3 cp と sync の違い | AWS CLI


__________________________________

執筆者プロフィール:Katayama Yuta
SaaS ERPパッケージベンダーにて開発を2年経験。 SHIFTでは、GUIテストの自動化やUnitテストの実装などテスト関係の案件に従事したり、最近ではDevOpsの一環でCICD導入支援をする案件にも従事。 座学で読み物を読むより、色々手を動かして試したり学んだりするのが好きなタイプ。

お問合せはお気軽に
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/


みんなにも読んでほしいですか?

オススメした記事はフォロワーのタイムラインに表示されます!