CodePipeline+CodeBuild+LambdaでLambdaのデプロイフローを構築してみる

Serverless(2)アドベントカレンダーの3日目ですが、ちょうどre:Inventで公開されたCodeBuildで何かやりたいなーと思いLambda(python2.7)のデプロイフローを構築してみました。

## CodeBuildとは
これまで、AWSには下記の開発フロー支援系ツールがリリースされています。

- CodeCommit ... gitのリポジトリ管理
- CodePipeline ... CIサービス、GithubやCodeCommitと連携しつつ
- CodeDeploy ... コードのデプロイ支援サービス。

先日のre:inventではこちらに加えてCodeBuildという製品が新たに登場しました。
これは簡単に言えばマネージドなビルドサーバだと思っていただければわかりやすいかと思います。
それまではCodePipelineが受けとったコードの更新イベントからCircleCI等のサービスのようにビルドを実行するにはJenkinsを用意するなど自前でホスティングする必要があり、完全にマネージドなサービスのみで運用はできませんでした。

CodeBuildはそうしたビルドフェイズの作業をマネージドな環境で実行できるサービスです。
現状ではUbuntuの上でPythonやAndroidなどのビルド環境を提供しています。

課金体系も、インスタンス単位ではなく利用時間単位（分単位）で課金となっており、非常に利用しやすい価格ですね。

細かい機能等は公式ブログの方を参照ください。
https://aws.amazon.com/jp/blogs/news/aws-codebuild-fully-managed-build-service/

## Lambdaのデプロイフローを構築する
今回はこれまで自分の手元ではCircleCIで運用していたLambdaのデプロイフローをCodePipeline + CodeBuild + Lambdaで構築しようと思います。
Github上で管理しているlambdaのハンドラを、指定のLambdaに対してmasterへPushする度に自動でデプロイするようにしました。

### CodeBuildの設定
CodeBuild上では、Githubの対象リポジトリをSourceに指定し、残りは対象リポジトリに追加した `buildspec.yml` にて指定します。
今回はpythonのLambdaファンクションを作成したため、依存パッケージのinstallをCodeBuild上で実施しています。

```yaml
version: 0.1

phases:
  install:
    commands:
      - pip install -t . -r requirements.txt
  pre_build:
    commands:
      - echo Nothing to do in the pre_build phase...
  build:
    commands:
      - echo Nothing to do in the build phase...
artifacts:
  type: zip
  files:
    - "**/*"
  discard-paths: no

```

### デプロイ用Lambda
Lambdaでは、下記を実行しています。

- CodePipelineからデプロイ対象のLambdaのARNを含めたevent取得
- PipelineのInput Artifactsを直接対象のLambdaのソースとしてデプロイ
- 完了後、CodePipelineに成功した旨を通知（ここ大事です

実際のコード

```python:
#!-*-coding:utf-8-*-
from __future__ import print_function
import boto3

lambda_client = boto3.client('lambda')
code_pipeline = boto3.client('codepipeline')

def lambda_handler(event, context):
    print(event)
    job_id = event['CodePipeline.job']['id']
    job = event["CodePipeline.job"]["data"]
    target_lambda = job["actionConfiguration"]["configuration"]["UserParameters"]
    artifact = job["inputArtifacts"][0]
    s3Location = artifact["location"]["s3Location"]
    # Deploy zipped lambda code
    response = lambda_client.update_function_code(
        FunctionName=target_lambda,
        S3Bucket=s3Location["bucketName"],
        S3Key=s3Location["objectKey"],
        Publish=True
    )
    # Notify the task result to codepipeline.
    code_pipeline.put_job_success_result(jobId=job_id)
    return response
```

function作成にあたっては、S3へのReadアクセス、Lambdaへのデプロイ、CodePipelineへの成功・失敗の通知に関してLambdaのIAMロールのアクセスポリシーに追記しましょう。

また、注意点として、lambdaのデプロイに使えるzipのS3 Objectは、デプロイ対象のlambdaが配置されてるリージョンと同じ場所のS3バケットに配置されたもののみですので、もし東京リージョン等にデプロイしたい場合はs3オブジェクトをコピーする等の処理、もしくは一度zipを落としてくる必要があるかと思います。

### pipelineの構築
CodePipeline上では、
1. Githubの対象リポジトリのmasterブランチのPushを検知
2. CodeBuildにビルドを依頼
3. 先ほど作成したLambdaファンクションにArtifactsを渡す
というフローを用意します。

Pipelineの作成画面では、デプロイ対象のGithubリポジトリをSourceに、masterブランチを対象として設定します。
またBuildフェーズでは、選択肢の中からCodeBuildが選択できるかと思いますので、作成済みのCodeBuildプロジェクトを選択してください。
Deployフェーズは一旦なにも設定しなくて大丈夫です。

その後、先程作成したLambda functionをDeployフローとして追加します。
PipelineのEditから、Buildの直後にDeployという名前でStageを追加し、ActionとしてはLambdaを選択、下記のようにLambdaの設定を行ってください。

![lambda_setting.png](https://qiita-image-store.s3.amazonaws.com/0/182/4595f3b3-369c-b856-af26-92bf29c58f83.png)

完了するとコンソール上ではこのようになっています。

![スクリーンショット 2016-12-03 20.40.50.png](https://qiita-image-store.s3.amazonaws.com/0/182/abad924f-4a65-6502-2a8d-dd3cf940be10.png)

あとはGithubのmasterへのPushを検知して、自動的に対象のLambdaに対してデプロイが実行されるようになります。

## 最後に
普段社内ではCircleCIでLambdaのデプロイフローを構築しているのですが、CodePipelineからのデプロイも思ったより簡単に実現できますし、コストも意外と小さいなという印象です。
ちなみに、CodeBuildは小さいインスタンスならだいたい\$0.005/build、CodePipelineは一つのパイプラインにつき\$1、Lambdaは今回最小構成で実行時間が約1.1秒程度でしたのでおそらく無料枠の範囲内で運用出来るのではないでしょうか。

また、CodePipeline上ではLambdaを始めとして、柔軟かつ強力な連携が可能です。
最初の構築の手間こそあれど、様々なAWSサービスとのインテグレーションがし易いのがCodePipeline+CodeBuildの良いところだと思います。
ぜひ一度お試しください。

## 参考
- [Integrate AWS Lambda Functions into Pipelines in AWS CodePipeline](http://docs.aws.amazon.com/codepipeline/latest/userguide/how-to-lambda-integration.html)
- [AWS Codepipeline](https://aws.amazon.com/jp/codepipeline/)
- [AWS CodeBuildの紹介](https://aws.amazon.com/jp/blogs/news/aws-codebuild-fully-managed-build-service/)