CodeBuild + Serverless FrameworkでPythonアプリケーションをChatDeployする

記事タイトルとURLをコピーする

こんにちは。てるい@今年は雪が多い札幌です。

Serverless Frameworkで作ったPythonアプリのデプロイを効率化するために、CodeBuildによるデプロイ環境とそれをHubot(Slack)からの呼び出され方によって環境を切り替えつつデプロイできる仕組みを作ったのでご紹介したいと思います。

全体イメージ


こんなイメージで、Hubotへのコマンドの内容からデプロイ対象の環境を特定し、それに応じた環境変数などを与えながらPython3.6なServerlessアプリケーションをデプロイする仕組みです。

前提

  • ソースコードのホスト先はGitHub(Private)
  • Hubot環境はEC2上に構築したものを使用(ちなみにコチラの方法で作ったもの)
  • Serverless Frameworkのバージョンは 1.25 、Pythonは 3.6 を使用
  • リージョンは今回は us-west-2 を使用しています(他でも普通にできるはずです)

手順

①ECR(Elastic Container Registory)にCodeBuildで使用するDocker Imageをホストする

今回はNode.jsで動くServerless FrameworkとPythonで書かれたアプリケーションの組み合わせなので、CodeBuildが標準で用意しているNode.jsやPython向けのイメージでは片手落ちでデプロイすることが出来ません。また、CodeBuildの標準イメージはUbuntuベースでOSの設定がブラックボックスなので、Amazon Linux上で動いているLambdaではPythonのC拡張のライブラリなどを使用した場合に問題が起きる可能性があります。
そのため、今回はDocker Hubで公開されているAmazon Linuxの公式イメージからCodeBuildで使用するためのDockerイメージを作成してECRにホストさせ、そのイメージを使用してデプロイを行うようにします。

まずはECSの Repositories メニューからECRのリポジトリを作成します。ここは名前を決めてポチッとするだけです。


出来上がったリポジトリにDocker ImageをPushします。
まず、このような Dockerfile を用意します。

FROM amazonlinux:latest

RUN curl --silent --location https://rpm.nodesource.com/setup_8.x | bash -
RUN yum install nodejs python36 -y

これをBuildしてPushします。

$ `aws ecr get-login --no-include-email --region us-west-2`
$ docker build -t $YOUR_IMAGE_NAME .
$ docker tag $YOUR_IMAGE_NAME:latest $YOUR_ACCOUNT_NUMBER.dkr.ecr.us-west-2.amazonaws.com/$YOUR_IMAGE_NAME:latest
$ docker push $YOUR_ACCOUNT_NUMBER.dkr.ecr.us-west-2.amazonaws.com/$YOUR_IMAGE_NAME:latest

②ECRへのCodeBuildからのアクセスを許可する

CodeBuildからECRのイメージを取得させるにはそれを許可する設定が必要です。先程作成したECRリポジトリの Permissions タブから許可設定を行います。
CodeBuildのサービスドメインである codebuild.amazonaws.com を対象に許可設定を行います。


権限は Pull only actions を選択します。


③CodeBuildプロジェクトの作成

Managed Console上では最後の操作となる、CodeBuildのプロジェクトを作成を行います。基本的にはナビゲーションどおりに設定していきます。
ソースリポジトリはGitHubの対象となるリポジトリを指定します。

ビルド環境は先程作成したECRにホストしているDocker Imageを使用するよう指定します。


また、Serverless FrameworkはIAM Roleの管理も行うため基本的に Administrator Access のRoleを必要とするため、実行時のRoleにはそれを割り当てたRoleを指定します。もしくは、予め作成されたRoleを指定することもできるので、権限を絞りたい場合はそのようにすると良いでしょう。


④CodeBuildのビルド設定と実行スクリプトの準備

このような buildspec.yml を準備します。ポイントは必要な環境変数をデプロイ対象となるproduction,stagingなどの環境毎に予めSSM Parameter Storeに入れておくことです。これを、CodeBuild実行時に指定する環境変数を見て切り替えさせることで環境毎に異なる環境変数を動的に設定させるようにします。

version: 0.2

env:
  parameter-store:
    FOO_PROD: foo-prod
    FOO_STG: foo-stg

phases:
  install:
    commands:
      - npm install
  pre_build:
    commands:
      - chmod +x ./deploy.sh
  build:
    commands:
      - ./deploy.sh

これをこのようなデプロイスクリプトを利用することで環境変数切り替えさせつつデプロイを行います。ちなみに、 serverless-python-requirementsというPluginを使っているのでPythonライブラリのインストールも sls deploy だけでまとめて行えます。

#!/bin/bash -eu

export PATH=$PATH:$PWD/node_modules/.bin/

if [ $FOO_ENV = "production" ]; then
    export FOO=$FOO_PROD
elif [ $FOO_ENV = "staging" ]; then
    export FOO=$FOO_STG
fi

sls deploy

上記のようにすることで、 serverless.yml に以下のように書いておくことでLambdaの環境変数を環境毎に切り替えさせるという形です。

plugins:
  - serverless-python-requirements

custom:
  pythonRequirements:
    dockerizePip: false
    pythonBin: ${opt:pythonBin, self:provider.runtime}

provider:
  name: aws
  runtime: python3.6
  cfLogs: true
  stage: ${env:FOO_ENV}
  environment:
    FOO: ${env:FOO}

⑤Hubot Scriptの作成

最後に、作成したCodeBuildのプロジェクトを呼び出すHubot Scriptを作成します。実際のスクリプトを全部載せると長すぎるのでポイントのCodeBuild呼び出し部分だけ載せるとこんな感じです。ポイントは、先程の環境毎の環境変数を切り替えるための環境変数を environmentVariablesOverride の引数で指定することと、環境にマッピングされたGitHubのブランチ名を sourceVersion 引数で指定することです(まあこの辺は各位ポリシーあるかと思うので良い感じにやってください)

export default function(robot){
  robot.respond(/deploy (production|staging)/, (res) => {
    const environment = res.match[1];
    const codebuild = new AWS.CodeBuild({region: 'us-west-2'});
    const params = {
      projectName: `foo-deploy`,
      environmentVariablesOverride: [
        {
          name: 'FOO_ENV',
          value: environment,
          type: 'PLAINTEXT'
        },
      ],
      sourceVersion: environment
    };
    codebuild.startBuild(params, function(err, data) { /* snip */ });
  });
}

⑥Let's Deploy!!

これで準備は完了です!Slackからdeployコマンドを実行した結果を一応貼っておきますね。


Botの発言がウザい感じですが、これは「そういうキャラだから仕方がない!」としか言いようがないです・・・!(塗りつぶしてる部分を外すと私と同世代の方には納得してもらえると思うんですけど、一応社内の開発コードネームが元になっているので伏せさせてもらってます)

最後に

気持ちの問題かもですが、Serverless FrameworkはIAMの強い権限を求めるので、Circle CIとかだとCredentialをCircle CIに持たせるのが少し躊躇われますが、CodeBuildでRole baseで権限を持たせられると認証情報がテンポラリになるので少し安心できます。今回は都合上Chatからですが、CodePipelineと組み合わせることで特定のブランチの更新をトリガーに自動的にデプロイさせることも可能かと思います(その場合はたぶん環境変数の切り替えに一工夫いりそうです。たぶんプロジェクトを分けて環境変数を予め仕込んでおく感じになるかなと)

Serverlessでもデプロイは効率化して良いServerlessライフを送りましょう!!