ChaliceのCDパイプラインをシンプルに構築する


What is chalice?

AWSが提供するPythonのサーバーレスフレームワークで、flaskみたいな書き心地でLambdaなどにデプロイできます。
今回はこのChaliceのCDを構築するお話。

TL;DR

Chaliceは自動でCDパイプラインを構築する機能がある。でもなんか難しくて結局自分で作った。

使用バージョン

  • chalice==1.24.1

Deploy Command

公式ドキュメントのQuickstartではローカル環境からCLIでデプロイする方法が紹介されています。

$ chalice deploy --stage dev

1コマンドで簡単!
しかしこれだとチームで開発している場合は問題になります。
Chaliceは作成したAWSリソースを把握するため、.chalice/deployed/配下に下記のようなJSONを作成します。

deployed.json
{
  "resources": [
    {
      "name": "foo_role",
      "resource_type": "iam_role",
      "role_arn": "arn:aws:iam::...",
      "role_name": "foo"
    },
    {
      "name": "foo_lambda",
      "resource_type": "lambda_function",
      "lambda_arn": "arn:aws:lambda:ap-northeast-1:..."
    },
    {
      "name": "foo_event",
      "resource_type": "s3_event",
      "bucket": "foo-dev",
      "lambda_arn": "arn:aws:lambda:ap-northeast-1:..."
    }
  ],
  "schema_version": "2.0",
  "backend": "api"
}

ローカルでデプロイする際にこのJSONがチームで共有できていないとリソースの作成・更新・削除がうまくできないわけです。
このあたりはterraformのtfstateに似てる。

CI/CD pipeline generation

そのため、ChaliceはCodePipelineによるCDパイプラインを自動的に構築する機能を持っています。
https://aws.github.io/chalice/topics/cd.html

$ chalice generate-pipeline --pipeline-version v2 pipeline.json

このコマンドを打つとCloudFormation用の設定ファイルが吐き出されます。
吐き出された設定ファイルを元にCloudFormationを使うことでCDパイプラインが出来上がるという二段構えです。ややこしい

CloudFormation??

CDパイプラインのための設定が自動で出来上がっていいじゃないか。最初はそう思いました。
デフォルトの設定で十分な方はこれでもいいかもしれません。しかしドキュメントにはこんなワードがありました。

Chalice can generate a CloudFormation template that will create a starter CD pipeline.

そうstarterなのです。
プッシュしたら自動的にデプロイしてほしい。デプロイしたら通知がほしい。そういうオマケは自前で作る必要があります。
「でも実は私、CloudFormation使ったことない。」

さあ困った。CloudFormationがわかる方は生成された設定をいじればいいと思います。
以降は私のようなCloudFormationワカラナイ人向けです。

Be Simple

そもそもローカルでデプロイするときはchalice deployの1コマンドで済んだのに、CDになると複雑すぎない?という疑問もあり、勝手知ったるCodeBuildでchalice deployさせる方針を取ります。
問題になるのは状態を持っているdeployed.jsonですが、tfstateのようにS3に入れてしまいましょう。

buildspec.yml
version: 0.2

phases:
  install:
    runtime-versions:
      python: 3.8
    commands:
      - python -V
  pre_build:
    commands:
      - echo Install dependencies...
      - pip install -r requirements.txt
      - aws s3 cp s3://chalice-deployed/${STAGE}.json .chalice/deployed/${STAGE}.json || true
  build:
    commands:
      - echo Deploy ${STAGE}...
      - chalice deploy --stage ${STAGE}
  post_build:
    commands:
      - aws s3 cp .chalice/deployed/${STAGE}.json s3://chalice-deployed/${STAGE}.json
      - echo Build completed at `date '+%Y-%m-%d %H:%M:%S'`

うん。シンプル。
初回は${STAGE}.jsonがなくダウンロードに失敗するので|| trueでごまかしてます。
念の為、CodeBuildの同時ビルド制限を1にしておきます。
完成!

ハマったところ

今回Chaliceで作成したLambda関数は一部Layerでpyodbcを入れていました。
requirements.txtに入れてもChaliceがパッケージングできないライブラリだからです。

こうしたrequirements.txtに書いていないライブラリがあるとCodeBuildでchalice deployしたときに「pyodbc入ってないよ」というエラーが出ます。
とりあえずインポートエラーを補足するワークアラウンドでしのぎました。

app.py
import warnings

try:
    import pyodbc
except ImportError:
    warnings.warn("pyodbcをインポートできません。CD環境の場合はこの警告を無視できます。")

まとめ

ローカルでもCDでも同じ方法でデプロイすることができました。
CloudFormation版では何をしたかったのかを読み解けておらず、まずい点があったら教えていただきたいです。