[AWS CDK] CircleCIでサーバーレスアプリ(WebAPI)を自動デプロイしてみた(本番環境と開発環境)

AWS CDK(Cloud Development Kit)とCircleCIを組み合わせて、自動デプロイする仕組みを作ってみました。

これで運用がますます楽々になりまね!

なお、デプロイのみを対象とします。(テストは扱いません)

目次

環境

項目 バージョン
macOS High Mojave 10.14.6
AWS CLI aws-cli/1.16.220 Python/3.7.2 Linux/4.15.0-1043-aws botocore/1.12.210
Python 3.7
Node.js 10.15.3
npm 6.4.1
AWS CDK 1.4.0 (build 175471f)
リポジトリ GitHub

主にDocker Imageで使用しています。

リポジトリとブランチ運用

Gitリポジトリは、GitHubフローみたいな運用とし、Pushされたタグを本番環境にデプロイします。

自動デプロイの対象は、下記とします。デプロイ先は、同じAWSアカウントです。

種類 名前の例 環境 AWSスタック名
ブランチ master 開発環境 AWS-CDK-CircleCI-Deploy-Sample-App-Stack-dev
ブランチ issues/123 開発環境 AWS-CDK-CircleCI-Deploy-Sample-App-Stack-dev
タグ v1.2.3 本番環境 AWS-CDK-CircleCI-Deploy-Sample-App-Stack-prod

AWS CDKのインストール

インストール済みの場合はSkipしてください。

$ npm install -g aws-cdk

AWS CDKで適当にサーバーレスアプリ(WebAPI)を作る

WebAPIの仕様

Method Path
GET /message/:id

たとえば、/message/hogeに対してアクセスしたとき、下記のJSONを取得します。

{
    "env": "dev",
    "message": "your request message id is hoge"
}

envの値は、開発環境ならdevで、本番環境ならprodとします。

AWS CDKプロジェクトの構築

フォルダを作成し、AWS CDKプロジェクトの初期化を行います。

$ mkdir AWSCDK-CircleCIDeploySample
$ cd AWSCDK-CircleCIDeploySample
$ cdk init app --language=typescript

必要なライブラリをインストールします。

$ npm install --save @aws-cdk/aws-lambda
$ npm install --save @aws-cdk/aws-apigateway
$ npm install --save @aws-cdk/aws-iam

Lambdaコードを書く!

まずはファイルを作ります。

$ mkdir -p src/lambda/
$ touch src/lambda/app.ts

続いてコードを下記にします。

インフラをコードで書く!!! (AWS CDK)

2つのStackを作成します。

  • サーバーレスアプリのStack
  • CircleCI用ユーザのStack

サーバーレスアプリのStack

デフォルトで作成されているlib/awscdk-circle_ci_deploy_sample-stack.tslib/app-stack.tsにリネームします。

$ mv lib/awscdk-circle_ci_deploy_sample-stack.ts lib/app-stack.ts

内容は下記です。

対象環境の情報をコンストラクタの引数(systemEnv)で受け取っています。

CircleCI用ユーザのStack

lib/circleci-user-stack.tsを新規作成します。

$ touch lib/circleci-user-stack.ts

内容は下記です。

簡略化のため雑に作成しています。よりセキュアに作るためには、必要最低限のポリシーを作成するなど、ベストプラクティス等を参照してください。 IAM のベストプラクティス

スタックのデプロイ準備をする

bin/awscdk-circle_ci_deploy_sample.tsを下記にします。

環境変数から「環境名」を取得し、スタック名に含めています。

ビルドする

ビルドエラーが無ければOKです。

$ npm run build

bootstrapの実行

Lambdaコード格納用のS3バケットを作るcdk bootstrapは、最初の1回だけ実行すればOKです。

$ cdk bootstrap

CircleCIの事前準備

Python仮想環境の構築

AWS CLI自体は、Python上で動いているため、Python仮想環境を作成してからインストールします。

pyenvの導入

導入済みの場合は、次へどうぞ!

pyenvを導入します。

$ brew install pyenv
$ echo 'eval "$(pyenv init -)"' >> ~/.bash_profile
$ exec $SHELL -l

Python仮想環境の作成

Pythonをインストールします。すでにある場合は次へ。

$ pyenv install 3.7.2

pipenvを導入します。すでにある場合は次へ。

$ pip install pipenv

Python仮想環境を作成します。

$ pipenv install --python 3.7.2

Python仮想環境に入ります。

$ pipenv shell

AWS CLIの導入

$ pipenv install awscli

これで、AWS CLIが使えるようになりました!

ついでに、.gitignoreも更新しておきます。

$ echo ".venv" >> .gitignore

AWS認証情報のセットファイルを作成

$ mkdir scripts
$ touch scripts/set_aws.sh
$ chmod 755 scripts/set_aws.sh

set_aws.shの内容は下記とします。AWS CLIを使うための環境変数を設定します。

#!/bin/bash

# https://docs.aws.amazon.com/ja_jp/cli/latest/userguide/cli-configure-envvars.html

echo AWS ENV setting: $1

export SYSTEM_ENV=$1
export AWS_DEFAULT_REGION="ap-northeast-1"
export AWS_DEFAULT_OUTPUT="json"

if [ $1 = "dev" ]; then
    export AWS_ACCESS_KEY_ID=$AWS_ACCESS_KEY_ID_DEV
    export AWS_SECRET_ACCESS_KEY=$AWS_SECRET_ACCESS_KEY_DEV
else
    export AWS_ACCESS_KEY_ID=$AWS_ACCESS_KEY_ID_PROD
    export AWS_SECRET_ACCESS_KEY=$AWS_SECRET_ACCESS_KEY_PROD
fi

CircleCI用の設定ファイルを作成

.circelciディレクトリを作成し、その中にconfig.ymlを作成します。

$ mkdir .circleci
$ touch .circleci/config.yml

続いて、config.ymlファイルを下記にします。

CircleCI用のユーザー作成

ローカルにて、スタック指定しつつcdk deployを実行してユーザーを作成します。

$ SYSTEM_ENV=dev cdk deploy AWS-CDK-CircleCI-Deploy-Sample-User-Stack-dev
$ SYSTEM_ENV=prod cdk deploy AWS-CDK-CircleCI-Deploy-Sample-User-Stack-prod

AWS認証情報の取得(アクセスキーIDとシークレットキー)

まずは、開発環境用のユーザです。忘れないようメモしておきます。

$ aws iam create-access-key --user-name aws-cdk-circleci-deploy-user-dev
{
    "AccessKey": {
        "UserName": "aws-cdk-circleci-deploy-user-dev",
        "AccessKeyId": "aaaaa",
        "Status": "Active",
        "SecretAccessKey": "bbbbb",
        "CreateDate": "2019-08-19T03:59:42Z"
    }
}

続いて、本番環境用のユーザです。同じくメモしておきます。

$ aws iam create-access-key --user-name aws-cdk-circleci-deploy-user-prod
{
    "AccessKey": {
        "UserName": "aws-cdk-circleci-deploy-user-prod",
        "AccessKeyId": "ccccc",
        "Status": "Active",
        "SecretAccessKey": "ddddd",
        "CreateDate": "2019-08-19T03:59:57Z"
    }
}

リポジトリのPush

ここまでの内容をmasterブランチにコミットし、pushしておきます。

$ git add .
$ git commit -m "create sample"
$ git push origin master

CircleCIの設定

ここから先は、Web画面で行います。

ログイン

CircleCIにログインします。

CircleCIにログインする

CircleCIのプロジェクトを作成

「ADD PROJECT」を選択し、さきほどGitHubにPushしたリポジトリを選択します。

対象リポジトリを選択する

続いて、「Start building」を選択します。

「Start building」を選択する

初めてのジョブが走りますが、AWS関連の環境変数が未設定なので失敗します。

期待通り失敗する

環境変数の設定

プロジェクト一覧の設定マークを押し、設定画面に移ります。

プロジェクトの設定画面に移動する

Environment Variablesを選択します。

環境変数を設定する

次の環境変数を追加します。

Name Value
AWS_ACCESS_KEY_ID_DEV 取得したAccessKeyId(開発用)
AWS_ACCESS_KEY_ID_PROD 取得したAccessKeyId(本番用)
AWS_SECRET_ACCESS_KEY_DEV 取得したSecretAccessKey(開発用)
AWS_SECRET_ACCESS_KEY_PROD 取得したSecretAccessKey(本番用)

CircleCIでデプロイする!

開発環境にデプロイ

さきほど失敗したWorkflowsの「Return」を選択し、そこの「Return from failed」を選択します。

「Return from failed」を選択する

再びデプロイが始まり、しばらくすると成功します!!! (ブラウザによっては自動更新してくれないため、手動更新してください)

開発環境にデプロイ成功!

本番環境にデプロイ

タグを付けて、そのタグをPushします!

$ git tag v1.0.0
$ git push origin v1.0.0

CircleCIによる本番環境へのデプロイが始まり、しばらくすると完了します!!!

本番環境にデプロイ成功!

動作確認

開発環境

WebAPIのエンドポイントを取得します。

$ aws cloudformation describe-stacks \
    --stack-name AWS-CDK-CircleCI-Deploy-Sample-App-Stack-dev \
    --query 'Stacks[].Outputs'
[
    [
        {
            "OutputKey": "sampleapiEndpoint1234",
            "OutputValue": "https://xxxxx.execute-api.ap-northeast-1.amazonaws.com/prod/"
        }
    ]
]

適当にWebAPIを叩きます。

$ curl https://xxxxx.execute-api.ap-northeast-1.amazonaws.com/prod/message/aiueo
{"env":"dev","message":"your request message id is aiueo"}

OKですね!

本番環境

同じく、WebAPIのエンドポイントを取得します。

$ aws cloudformation describe-stacks \
    --stack-name AWS-CDK-CircleCI-Deploy-Sample-App-Stack-prod \
    --query 'Stacks[].Outputs'
[
    [
        {
            "OutputKey": "sampleapiEndpoint1234",
            "OutputValue": "https://yyyyy.execute-api.ap-northeast-1.amazonaws.com/prod/"
        }
    ]
]

適当にWebAPIを叩きます。

$ curl https://yyyyy.execute-api.ap-northeast-1.amazonaws.com/prod/message/kakikukeko
{"env":"prod","message":"your request message id is kakikukeko"}

こちらもOKですね!

AWSの様子

ブラウザでログインして、それぞれの様子を確認しました。バッチリです!

CloudFormation

CloudFormationの様子

API Gateway

API Gatewayの様子

Lambda

Lambdaの様子

さいごに

慣れない部分もあって手こずりましたが、実現できました。

まだまだ荒削りなので、ブラッシュアップしつつ、もっと活用していきたいです。

参考