Posted at

CodeBuildからCloudFormationを使ってデプロイする時にテンプレートファイルの文法チェックやスタックの存在有無をチェックしてからデプロイする


はじめに

CodeBuildからCloudFormationを使ってデプロイする際に、CloudFormationのテンプレートファイルに問題がないか確認したり、今からデプロイ(作成)しようとしているスタックが既に存在するか確認してからデプロイを実行したいケースがあります。

以前書いた以下記事では、単純にスタックが既に存在しようとなかろうと一旦スタックを削除してから、作成(デプロイ)するという流れで書きました。

aws CodeBuildを使ってlambda(node.js)環境をテスト→デプロイする

しかし、実際運用するとなると、冒頭と述べたように事前に問題ないか確認してからデプロイの実行をしたくなってきます。

またある程度システムの規模が大きくなったり、色々な処理を増やしていくと、CodeBuildで実行する処理を用途ごとにスクリプトファイルにまとめたり、パラメータをまとめて1箇所で定義したり、したくなってきます。

本記事では、上記2点を踏まえ、CodeBuildのbuildspec.ymlを作っていきます。


方針

buildspec.ymlを作成する上で、以下2つの方針を念頭に改良していきます。 


  • チェックの仕組みを入れる

  • スクリプトファイルを管理しやすいように分割して整理


前置き


ファイルはGitHubに上がっています

今回説明に使用したプロジェクトのファイル一式は以下GitHubに上げてます。

GitHub lambda-helloworld


全体構成図

前回と同様、今回も全体の構成は以下のようになります。

68747470733a2f2f71696974612d696d6167652d73746f72652e73332e616d617a6f6e6177732e636f6d2f302f3139313837332f64303736643838332d656533362d396137662d633262312d6133663338616236643932652e706e67.png


やること

前回と比較して、

- デプロイされる成果物は変えない

- buildspec.ymlは変える

- シェルスクリプトは変える

ことで、より再利用しやすい汎用性の高いデプロイの仕組みに改良します。


buidspec.yml

buildspec.ymlは以下のようになります。


buildspec.yml

version: 0.2

phases:
install:
commands:
- npm install
pre_build:
commands:
- sh prj_test.sh
build:
commands:
- zip lambda.zip *.js *.json -r node_modules -q
post_build:
commands:
- sh upload.sh
- sh deploy.sh


post_buildフェーズに処理を直接書くのではなく、外部のスクリプトファイルとして持ちます。


シェルスクリプト

シェルスクリプトは以下3つのファイルに分けます。


  • upload.sh


    • テンプレートファイルおよびlambdaアーカイブファイルをアップロードする



  • param.sh


    • パラメータを持つ



  • deploy.sh


    • チェックしてからデプロイする




upload.sh

アップロードをするシェルスクリプトは以下になります。


upload.sh

#!/bin/bash

. ./param.sh

echo "S3_DEPLOY_DIR: ${S3_DEPLOY_DIR}"
echo "S3_TEMPLATE_DIR: ${S3_TEMPLATE_DIR}"
aws s3 cp lambda.zip $S3_DEPLOY_DIR
aws s3 cp template.yaml $S3_TEMPLATE_DIR
aws s3 ls $S3_TEMPLATE_DIR


まずparam.shを読み込んでパラメータを読み込みます。

CodeBuildではsourceコマンドは使えないため、代わりに"."コマンドを使って外部ファイルを読み込んでいます。後は、アップロード先のS3のパスを変数に置き換えます。


param.sh

パラメータを変数として定義しているシェルスクリプトは以下になります。


param.sh

#!/bin/bash

S3_BUCKET_NAME="sample-bucket"
S3_PRJ_NAME="helloworld"
S3_DEPLOY_DIR="s3://${S3_BUCKET_NAME}/${S3_PRJ_NAME}/deploy/"
S3_TEMPLATE_DIR="s3://${S3_BUCKET_NAME}/${S3_PRJ_NAME}/template/"
STACK_NAME="helloworld"
TEMPLATE_URL="https://s3-ap-northeast-1.amazonaws.com/${S3_BUCKET_NAME}/${S3_PRJ_NAME}/template/template.yaml"


param.shでは、upload.shおよびdeploy.shでパラメータとして変数化できるものを、変数として定義します。


deploy.sh

デプロイするシェルスクリプトは以下になります。


deploy.sh

#!/bin/bash

. ./param.sh

echo "TEMPLATE_URL: ${TEMPLATE_URL}"

aws cloudformation validate-template --template-url $TEMPLATE_URL 2>&1 1>/dev/null | grep ValidationError
if [ $? = 0 ]; then
echo "cloudformation template validate error"
exit 1
fi

aws cloudformation describe-stacks --stack-name $STACK_NAME 2>&1 1>/dev/null | grep error
if [ $? = 0 ]; then
echo "stack doesn't exist. creating new stack."
aws cloudformation create-stack --stack-name $STACK_NAME --template-url $TEMPLATE_URL --tags Key=COST,Value=cf-helloworld --region ap-northeast-1
echo "waiting stack create complete..."
aws cloudformation wait stack-create-complete --stack-name $STACK_NAME
else
echo "stack exists. updating the stack."
aws cloudformation update-stack --stack-name $STACK_NAME --template-url $TEMPLATE_URL --tags Key=COST,Value=cf-helloworld --region ap-northeast-1
echo "waiting stack update complete..."
aws cloudformation wait stack-update-complete --stack-name $STACK_NAME
fi

aws cloudformation describe-stacks --stack-name $STACK_NAME


deploy.shでは以下の処理を実施しています。


  • パラメータを変数に置き換え

  • テンプレートファイルのチェック


    • aws cloudformation validate-templateでテンプレートファイルの文法チェックができます

    • エラーがある場合、標準エラー出力にValidationErrorを含む文字列が出力されます

    • そのためここでは標準エラー出力をgrepにパイプしてValidationErrorがあればエラーとして扱っています



  • スタックの存在有無のチェック


    • aws cloudformation describe-stacksでスタック名を指定してスタック情報を取得できます

    • ただしスタックが存在しない場合、標準エラー出力にerrorを含む文字列が出力されます

    • そのためここでは標準エラー出力をgrepにパイプしてerrorがあればエラーとして扱っています



  • スタックが存在しなかったら、スタック新規作成


    • create-stackでスタックを新規作成します



  • スタックが存在したら、スタックを更新


    • update-stackでスタックを更新します



※ --tagsオプションは任意です。必須ではありません。

以上で、buildspec.ymlおよびシェルスクリプトの記述が完成しました。


まとめ

前回の構成を改良して、lambdaをデプロイする際のパラメータをparam.shファイルに外だしして、シェルスクリプトやbuildspec.ymlの汎用性を高めました。これでparam.shだけ変更すれば、スクリプトファイルやbuildspec.ymlはある程度使いまわせる構成に改良できました。


awsで開発作業を自動化するのまとめ記事

本記事はawsで開発作業を自動化するシリーズの1つです。その他の記事については以下を参照ください。

aws環境でnode.js、lambda、CodeCommit、CodeBuild、CloudFormation、CodePipelineを使って開発作業を自動化する