CodePiplineをつかってLambdaのデプロイフローを作成する

要件

  • lambdaのビルド&デプロイフローを構築する (lambdaの言語はGo)
  • Githubdevelop, masterへのpushを検知してdeployを行いたい
  • Jenkinsなどをつかって自前でホスティングはしたくない。

構成図

f:id:kinoue3:20180602233220p:plain

CodeBuild

buildspec.ymlに処理手順を書きます。 今回はGo言語でのデプロイとなるので以下のようになりました。

行なっていることは

  • dep ensure
  • build & zip
  • S3のファイルと比較して変更があった場合のみS3にupload (upload_S3.sh)
version: 0.2

env:
  variables:
    path: "src/github.com/xxx/yyy"
phases:
  install:
    commands:
      - export GOPATH=${HOME}/go
      - go get github.com/golang/dep/cmd/dep
      - chmod +x /go/bin/dep
  pre_build:
    commands:
      - mkdir -p ${GOPATH}/${path}
      - mv * ${GOPATH}/${path}/.
      - cd ${GOPATH}/${path} && dep ensure
  build:
    commands:
      - GOOS=linux GOARCH=amd64 go build -o XXX main.go
      - zip XXX.zip ./XXX
  post_build:
      commands:
        - ./upload_s3.sh ${S3_PATH}

upload_s3.sh

やっていることは以下
* S3から既存のコードを取得 * 今回のデプロイ対象のコードと既存のコードを比較 * 差分があるファイルのみS3にアップロード

#!/bin/bash

SOURCE_PATH="src/github.com/xxx/yyy"

cd ${GOPATH}/${SOURCE_PATH}
mkdir -p deploy
cd deploy

aws s3 cp s3://$1/ . --recursive
changes=()

for v in "ZZZ"
do
    unzip $v.zip
    IS_DIFF=`diff $v ../$v | wc -l`
    if [ $IS_DIFF != 0 ]; then
        changes+=( $v )
    fi
done


for v in ${changes[@]}; do
    aws s3 cp $v.zip s3://$1/
done

CodePipeline

CodePipelineでは、以下のように設定します。 (詳細は割愛)

  1. 名前
  2. ソース
  3. ビルド
  4. デプロイ
  5. サービスロール

デプロイ用lambda

デプロイ用lambdaでは、以下を行なっています。

  • S3にアップロードされたことをhookに動作する
  • S3にアップロードされたファイルをLambdaのソースとしてデプロイ
const aws = require('aws-sdk');
const s3 = new aws.S3({ apiVersion: '2006-03-01' });
const lambda = new aws.Lambda();

exports.handler = (event, context, callback) => {
    if (!event.Records) {
         callback(null, null);
    }
    event.Records.forEach(record => {
        const key = decodeURIComponent(record.s3.object.key.replace(/\+/g, ' '));
        const s3params = {
            Bucket: record.s3.bucket.name,
            Key: key,
        };
        s3.getObject(s3params, (err, data) => {
        if (err) {
            callback(`Error getting object ${key}. Make sure they exist and your bucket is in the same region as this function.`);
        } else {
            const fileName = key.replace('.zip', '')
            const lambdaParams = {
                FunctionName: "functionName",
                S3Bucket: record.s3.bucket.name,
                S3Key: key
            };
            lambda.updateFunctionCode(lambdaParams, (err, res) => {
                if (err) {
                    console.log(err, err.stack);
                }
                else {
                    console.log(res)
                }
            });
            callback(null, data.ContentType);
        }
        });
    })
};