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


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

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


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


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

AWSが掲げるLambdaへのデプロイフローは[ここ](https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/automating-deployment.html)にある通り、以下を構築していく形となります。

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

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

1. CodeBuildの用意
---

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

#### プロバイダの設定

![スクリーンショット 2018-04-02 13.13.35.png](https://qiita-image-store.s3.amazonaws.com/0/18331/9b9af859-3cf0-3bc8-dad6-29677ea0b963.png "スクリーンショット 2018-04-02 13.13.35.png")

#### ビルド方法

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

![スクリーンショット 2018-04-02 15.18.57.png](https://qiita-image-store.s3.amazonaws.com/0/18331/d253c8ed-cb4f-fbf7-f48a-8521559e041c.png "スクリーンショット 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](https://qiita-image-store.s3.amazonaws.com/0/18331/80dec6c0-e569-f295-4bd9-95adb76aa66a.png "スクリーンショット 2018-04-02 15.24.39.png")



2. CodePipeLineの準備
---

#### とりあえず準備

![スクリーンショット 2018-04-02 13.06.39.png](https://qiita-image-store.s3.amazonaws.com/0/18331/a6671888-8c32-4a86-1096-e955eec1814d.png "スクリーンショット 2018-04-02 13.06.39.png")

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

![スクリーンショット 2018-04-02 13.06.54.png](https://qiita-image-store.s3.amazonaws.com/0/18331/e5655e83-544d-7ea3-d714-0688dc500e50.png "スクリーンショット 2018-04-02 13.06.54.png")

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

![スクリーンショット 2018-04-02 15.31.53.png](https://qiita-image-store.s3.amazonaws.com/0/18331/40c885dc-dce6-465b-f427-668f4287a7ef.png "スクリーンショット 2018-04-02 15.31.53.png")

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


#### CodeBuildでの出力結果の確認

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

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


![スクリーンショット 2018-04-02 15.40.58.png](https://qiita-image-store.s3.amazonaws.com/0/18331/0012426f-3cbe-fac3-ed0e-262b9bbe2cd2.png "スクリーンショット 2018-04-02 15.40.58.png")

3. CloudFormationの準備
----

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

![スクリーンショット 2018-04-02 15.50.50.png](https://qiita-image-store.s3.amazonaws.com/0/18331/b56e1605-0d74-ee54-5ae8-f3f21b860f6a.png "スクリーンショット 2018-04-02 15.50.50.png")


![スクリーンショット 2018-04-02 15.52.51.png](https://qiita-image-store.s3.amazonaws.com/0/18331/76bb318a-0fdd-8d0e-503c-aadb2a9a1bd6.png "スクリーンショット 2018-04-02 15.52.51.png")

![スクリーンショット 2018-04-02 15.53.15.png](https://qiita-image-store.s3.amazonaws.com/0/18331/5b5e818d-3d5a-92a4-b649-c0daed74121c.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
            }
        }
    }
}

```

ここで、`LatestS3Bucket` や `LatestS3Key` という値が重要です。
この値に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の無限ループが始まってしまいました。なぜだ。