CloudFormation
lambda
CodePipeline
CodeBuild

CodePipeLineを使ってLambdaへの自動デプロイ

CircleCIなどを使ってEC2に乗っかったWebアプリケーションの更新はよく記事を見かけるのですが、時代はやはりサーバーレスなのでLambdaへのデプロイも自動化できたら嬉しいですよね

現在、私が携わっているサービスである RODEM はユーザーがストレスなくサービスを利用していただくためにいかに早く処理を行うか、安定に提供できるかのためサーバーレス化に取り組んでいます。

「RODEM」って何?って思う方は以下のリンクから
https://rodem.valwebservices.com/

LambdaへのデプロイはCloudFormationの利用をします。
CodePipeLineにおけるCloudFormationへのデータのやり取りがかなり手間取ったので、今後使われる方のための参考になればと思っています。

AWSが掲げるLambdaへのデプロイフローはここにある通り、以下を構築していく形となります。

  1. CodePipeLineでフローの定義
  2. CodeBuildでのパッケージング
  3. CloudFormationでの反映
  4. CodeDeployを使ってのBlue/Greenデプロイといったデプロイ戦略

今回は1~3までを紹介します。

1. CodeBuildの用意

後ほど、CodePipeLineで読み込むためにCodeBuildの準備をします。

プロバイダの設定

スクリーンショット 2018-04-02 13.13.35.png

ビルド方法

ビルド設定に関しては、私はLambdaはnodejsで記述しているためnodejsを選びました。
プロジェクトのルートパスにbuildspec.ymlを用意する必要があります。

スクリーンショット 2018-04-02 15.18.57.png

buildspec.ymlに関して

私の場合特にlambdaがデフォルトで読み込めないライブラリは使ってないので、
zip圧縮できればオッケーなので以下のようになっています。

version: 0.1

phases:
  install:
    commands:
      - echo 'install phase'
  build:
    commands:
      - echo 'build phase'
artifacts:
  type: zip
  files:
    - "**/*"
  discard-paths: no

アーティファクトの設定

これを設定しておかないと、CodePipeLineからCodeBuildを呼び出すときに呼び出せるようにしてくれ!と怒られてしまいます。

スクリーンショット 2018-04-02 15.24.39.png

2. CodePipeLineの準備

とりあえず準備

スクリーンショット 2018-04-02 13.06.39.png

以下は変更検知の設定ですね。
CodeBuildで設定したリポジトリと同じものを選びました。

スクリーンショット 2018-04-02 13.06.54.png

プロジェクト名はさきほどつくったCodeBuildのプロジェクトを指定します。

スクリーンショット 2018-04-02 15.31.53.png

このあとのデプロイに関しては、あとで詳しく作りますので一旦飛ばします。
サービスロールなどは適切に設定してください。

CodeBuildでの出力結果の確認

さきほどつくったパイプラインの編集でCodeBuildのタスクが終わると出力結果として
buildspec.ymlで定義された成果がS3に出力されます。
これは私の場合はzip化したnodejsのコードになります。

特にここの出力アーティファクト名に関しては、この後記述するデプロイタスク、cloudformationでの記述でかなり大事になってきます。

スクリーンショット 2018-04-02 15.40.58.png

3. CloudFormationの準備

さきほどのパイプラインの編集であらたにタスクを定義します。

スクリーンショット 2018-04-02 15.50.50.png

スクリーンショット 2018-04-02 15.52.51.png

スクリーンショット 2018-04-02 15.53.15.png

CodePipeLineを少し触っていると毎回ビルドするたびに生成する新しい成果物にはどのようにCloudformationを定義するjsonからアクセスするんだろう?という疑問がでてきます。

先に答えをいえば、アドバンストの設定で新しい成果物を取得するような記述を行います。

cloudformationのテンプレートの場所は私の場合は、プロジェクトのルートパス配下にLambdaを定義する cloudformation.json を用意しました。

cloudformation.jsonの内容

{
    "AWSTemplateFormatVersion": "2010-09-09",
    "Description": "20180330",
    "Parameters": {
        "LatestS3Bucket": {
            "Type": "String"
        },
        "LatestS3Key": {
            "Type": "String"
        }
    },
    "Resources": {
        "MyLambda": {
            "Type": "AWS::Lambda::Function",
            "Properties": {
                "FunctionName": "my-function",
                "Code": {
                    "S3Bucket": {
                        "Ref": "LatestS3Bucket"
                    },
                    "S3Key": {
                        "Ref": "LatestS3Key"
                    }
                },
                "Description": "create by codepipeline",
                "Handler": "index.handler",
                "Role": "********* iam role ******************",
                "Runtime": "nodejs6.10",
                "Environment": {
                    "Variables": {
                        "HOGEHOGE": "あいうえお"
                    }
                },
                "Timeout": 10,
                "MemorySize": 128
            }
        }
    }
}

ここで、LatestS3BucketLatestS3Key という値が重要です。
この値にCodePipeLineから最新の成果物であるS3の場所をマッピングしてあげます。

そのマッピング元となるのがアドバンストという枠で以下の記述を行います。

{
    "LatestS3Bucket": {
        "Fn::GetArtifactAtt": [
            "MyAppBuild",
            "BucketName"
        ]
    },
    "LatestS3Key": {
        "Fn::GetArtifactAtt": [
            "MyAppBuild",
            "ObjectKey"
        ]
    }
}

これを行うことでcloudformation.jsonに記述されたLatestS3Keyの値が上書きされるようになり最新の成果物の位置を扱えるようになります。

CodePipeLineのオーバーライド関数については以下を参照
https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/continuous-delivery-codepipeline-parameter-override-functions.html

上記の設定をすれば、GithubのmasterにpushするたびにCodePipeLineが起動されLambdaにソースコードがデプロイされます。CodeDeployを使った戦略はまだなので、追い追い記事にできたらと思います。

遭遇したバグ?

CloudWatch Event -> Lambda連携をつかってCodePipeLineの現在の進捗状況をSlackに通知するものを一度つくりました。
ただ一度ビルドが始まるとCodePipeLineの無限ループが始まってしまいました。なぜだ。