CloudFormationとAWS CLIで作るCodeDeploy(Blue/Green)環境

CodeDeploy(Blue/Green)動作検証用の環境を利用したく、構築しましたのでアウトプットです。 Auto Scalingで使用するAMIはPackerで作成し、AWSの各種サービスはCloudFormation、スタックの作成等にAWS CLIを利用しています。CloudFormation、Packerのテンプレート、CodeDeploy AppSpec File等は、GitHubに格納していますので、こちらを利用し構築していきます。
2019.04.14

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

こんにちは、坂巻です。

CodeDeploy(Blue/Green)動作検証用の環境がほしく、構築しましたのでアウトプットしておきます。

はじめに

Auto Scalingで使用するAMIはPackerで作成し、AWSの各種サービスはCloudFormation、スタックの作成等にAWS CLIを利用しています。CloudFormation、Packerのテンプレート、CodeDeploy AppSpec File等は、GitHubに格納していますので、こちらを利用し構築していきます。

上記を任意のディレクトリに保存します。テンプレート等を格納したファイルは以下の構成になっています。

$ tree
.
├── cloudformation
│   └── template.yml
├── codedeploy
│   ├── app
│   │   └── index.html
│   └── appspec.yml
└── packer
    ├── scripts
    │   ├── codedeploy.sh
    │   └── httpd.sh
    └── template.json

目次

  • 構成
  • 環境構築
  • AMI作成
  • スタック作成
  • デプロイグループ作成
  • リビジョン作成/アップロード
  • デプロイ実行

構成

構築する構成のイメージは以下となります。構築後はBlue/GreenデプロイにてEC2上のWebコンテンツを置き換えたいと思います。

00

環境構築

AMI作成

Packerにてゴールデンイメージを作成します。最新のAmazon Linux 2に、CodeDeployエージェント、Apacheをインストールします。処理の詳細はtemplate.jsonをご確認ください。なお、CodeDeployエージェントのインストールは、公式ドキュメントにしたがっています。

作成するAMI名は任意の名称でtemplate.jsonに、コマンドライン引数にて受け渡すようにしてあります。本エントリでは、受け渡す値は環境変数を利用したいと思います。

$ AMI_NAME=GoldenYotsugi
$ echo ${AMI_NAME}
GoldenYotsugi

packerディレクトリに移動し、テンプレートの書式をチェックします。

$ cd packer
$ packer validate -var ami-name=${AMI_NAME} template.json
Template validated successfully.

ビルドします。

$ packer build -var ami-name=${AMI_NAME} template.json
amazon-ebs output will be in this color.

==> amazon-ebs: Prevalidating AMI Name: test-yotsugi
    amazon-ebs: Found Image ID: ami-0f9ae750e8274075b
    
(省略)

Build 'amazon-ebs' finished.

==> Builds finished. The artifacts of successful builds are:
--> amazon-ebs: AMIs were created:
ap-northeast-1: ami-0ca082db551b278cb

ビルドが正常に終了すると、AMIのIDが出力されます。こちらのIDは後続で使用するので、環境変数にセットしておきます。

LAUNCH_CONFIG_IMAGE_ID=`aws ec2 describe-images \
  --filters "Name=tag-key,Values=Name" Name=tag-value,Values=${AMI_NAME} | jq -r '.Images[].ImageId'`

ビルド結果に出力されたAMI IDと同様か確認してください。

$ echo ${LAUNCH_CONFIG_IMAGE_ID}
ami-0ca082db551b278cb

スタック作成

作成するスタック名、テンプレートファイルのパス、テンプレート名を環境変数にセットします。

STACK_NAME=CodeDeployAppStack
TEMPLATE_DIR=../cloudformation/
TEMPLATE_NAME=template.yml

セットした内容を確認します。

cat << ETX
  STACK_NAME    : ${STACK_NAME}
  TEMPLATE_DIR  : ${TEMPLATE_DIR}
  TEMPLATE_NAME : ${TEMPLATE_NAME}
ETX

CloudFormationスタックを作成します。 このテンプレートは、スタック作成時に以下パラメーターの入力が必要になります。

パラメーター名 説明
projectName 各種AWSリソースに付与する任意の名称(prefixとして利用)
eC2KeyPair EC2で使用するキーペアを指定(事前に作成が必要)
launchConfigImageId Launch Configに指定するAMI ID(先程作成したAMI ID)
snsSubscription SNSサブスクリプションに設定するEmailアドレス

上記、パラメーターに指定する値を環境変数にセットします。

PROJECT_NAME=test-yotsugi
EC2_KEY_PAIR=test-yotsugi-key
SNS_SUBSCRIPTION=xxxxxx@xxxxxxxx.jp

セットした内容を確認します。

cat << ETX
  EC2_KEY_PAIR     : ${EC2_KEY_PAIR}
  PROJECT_NAME     : ${PROJECT_NAME}
  SNS_SUBSCRIPTION : ${SNS_SUBSCRIPTION}
ETX

スタックを作成します。必要な値は、環境変数にセットされているので、以下のコマンドのまま実行できます。

aws cloudformation create-stack \
  --stack-name ${STACK_NAME} \
  --template-body file://${TEMPLATE_DIR}${TEMPLATE_NAME} \
  --parameters \
    ParameterKey=eC2KeyPair,ParameterValue=${EC2_KEY_PAIR} \
    ParameterKey=launchConfigImageId,ParameterValue=${LAUNCH_CONFIG_IMAGE_ID} \
    ParameterKey=projectName,ParameterValue=${PROJECT_NAME} \
    ParameterKey=snsSubscription,ParameterValue=${SNS_SUBSCRIPTION} \
  --capabilities 'CAPABILITY_NAMED_IAM'

コマンドが正常に実行されるとStackIdが出力されます。

{
    "StackId": "arn:aws:cloudformation:ap-northeast-1:XXXXXXXXXXXX:stack/CodeDeployAppStack/28a0e190-5e44-11e9-bd44-0e89097244e0"
}

スタックの作成が完了したことを確認します。

$ aws cloudformation describe-stacks --stack-name ${STACK_NAME} | jq '.Stacks[].StackStatus'
"CREATE_COMPLETE"

なお、スタックが作成中の場合はCREATE_IN_PROGRESSとなります。正常に完了した場合は、SNSより指定したアドレスに、サブスクリプションの確認メールが送付されているので適宜有効化してください。

デプロイグループ作成

現時点では、EC2 Auto ScalingのBlue/Greenデプロイについては、CloudFormationではサポートされていないようです。

注記 Blue/Green デプロイは、AWS CloudFormation では AWS Lambda コンピューティングプラットフォームでのみサポートされます。

デプロイグループの作成はAWS CLIを利用します。作成に必要な情報を環境変数にセットします。

  • GROUP_NAME…作成する任意のデプロイグループ名
  • APP_NAME…CodeDeployアプリケーション名(このまま入力)
  • ROLE_ARN…CodeDeployサービスロールのARN(このまま入力)
  • ASG_NAME…デプロイ対象のAuto Scalingグループ(このまま入力)
  • TARGET_GROUP_NAME…デプロイ対象のターゲットグループ(このまま入力)
  • ACCOUNT_ID…現在構築を行っているAWSのアカウントID(このまま入力)
GROUP_NAME=${PROJECT_NAME}-deploygroup-blue-green
APP_NAME=${PROJECT_NAME}-codedeploy-app
ASG_NAME=${PROJECT_NAME}-asg
TARGET_GROUP_NAME=${PROJECT_NAME}-tg
ACCOUNT_ID=`aws sts get-caller-identity --query 'Account' --output text`
ROLE_ARN=arn:aws:iam::${ACCOUNT_ID}:role/${PROJECT_NAME}-codedeploy-role

セットした内容を確認します。

cat << ETX
  GROUP_NAME        : ${GROUP_NAME}
  APP_NAME          : ${APP_NAME}
  ROLE_ARN          : ${ROLE_ARN}
  ASG_NAME          : ${ASG_NAME}
  TARGET_GROUP_NAME : ${TARGET_GROUP_NAME}
  ACCOUNT_ID        : ${ACCOUNT_ID}
ETX

デプロイグループを作成します。デプロイタイプはBlue/Greenで、成功後に置き換え前のインスタンスはすぐに削除されるようにしました。

aws deploy create-deployment-group \
  --application-name ${APP_NAME} \
  --deployment-group-name ${GROUP_NAME} \
  --service-role-arn ${ROLE_ARN} \
  --auto-scaling-groups ${ASG_NAME} \
  --deployment-style deploymentType="BLUE_GREEN",deploymentOption="WITH_TRAFFIC_CONTROL" \
  --blue-green-deployment-configuration terminateBlueInstancesOnDeploymentSuccess={action="TERMINATE"},deploymentReadyOption={actionOnTimeout=CONTINUE_DEPLOYMENT},greenFleetProvisioningOption={action=COPY_AUTO_SCALING_GROUP} \
  --load-balancer-info targetGroupInfoList=[{name=${TARGET_GROUP_NAME}}]

コマンドの詳細については、以下を確認にしてください。

コマンドが正常に実行されるとdeploymentGroupIdが出力されます。

{
    "deploymentGroupId": "fdd101f9-5c8b-4164-85a8-ad43eff90181"
}

デプロイメントグループが作成されたことを確認します。

aws deploy list-deployment-groups --application-name ${APP_NAME}

正常に作成できていれば、指定した名称のデプロイメントグループが出力されます。

{
    "deploymentGroups": [
        "test-yotsugi-deploygroup-blue-green"
    ]
}

リビジョン作成/アップロード

リビジョン(ソースコード/コンテンツ/実行ファイル等のアーカイブ)を作成します。リビジョンには、AppSpecファイル(デプロイの手順書)を含める必要があります。

ここでのAppSpecファイルはindex.htmlを、Apacheのドキュメントルートにデプロイする内容としています。AppSpecファイルが格納してあるディレクトリに移動し、アーカイブします。

$ cd ../codedeploy/
$ zip -r revision.zip .
  adding: app/ (stored 0%)
  adding: app/index.html (deflated 17%)
  adding: appspec.yml (deflated 11%)

作成したリビジョンをリポジトリにアップロードします。ここでは、リポジトリにS3を利用しました。CodeDeployがサポートしているリポジトリについては、以下に記載あります。

$ aws s3 ls s3://${PROJECT_NAME}-bucket-${ACCOUNT_ID}
$ aws s3 cp ./revision.zip s3://${PROJECT_NAME}-bucket-${ACCOUNT_ID}
upload: ./revision.zip to s3://<PROJECT_NAME>-bucket-<ACCOUNT_ID>/revision.zip

正常にアップロードできたことを確認します。

$ aws s3 ls s3://${PROJECT_NAME}-bucket-${ACCOUNT_ID}
2019-04-11 19:44:38       1061 revision.zip

デプロイ実行

デプロイ実行前のWebコンテンツを確認します。ALBのDNS名にアクセスします。

01

Blue/Greenデプロイを実行します。

aws deploy create-deployment \
  --application-name ${APP_NAME} \
  --deployment-config-name CodeDeployDefault.AllAtOnce \
  --deployment-group-name ${GROUP_NAME} \
  --s3-location bundleType="zip",bucket=${PROJECT_NAME}-bucket-${ACCOUNT_ID},key=revision.zip \
  --file-exists-behavior "OVERWRITE"

コマンドの詳細については、以下を確認にしてください。

コマンドが正常に実行されるとdeploymentIdが出力されます。

{
    "deploymentId": "d-XRFTQUWJY"
}

CodeDeployのコンソールより、デプロイの状態を確認してみます。「ステップ 1」では、Auto Scalingグループがコピーされ、EC2が起動します。

02

このステップが完了すると、指定したAuto Scalingグループがコピーされていることが確認できます。

03

EC2が新たに起動されています。

04

「ステップ 2」では、新たに起動されたEC2に、AppSpecファイルの定義にしたがいデプロイが実行されます。「ステップ 3」では、指定したターゲットグループ配下のEC2の置き換えが実施されます。

05

ターゲットグループ配下にEC2が追加されています。 06

デプロイの進捗により、登録済みターゲットのステータスが変化していきます。 07 08

このステップが完了すると、ターゲットグループ配下のEC2が置き換えられます。

12

「ステップ 4」では、置き換え前のリソースが削除されます。Auto Scalingグループ(EC2)の削除が行われています。 09 10 11

デプロイが完了しましたので、Webコンテンツを確認します。

13

Webコンテンツが置き換わっていることを確認できました。なお、デプロイでエラーになる場合は、以下を参考にログを確認してください。

さいごに

今回は環境構築をメインに書きました。次回は本エントリで作成した環境を利用し、Blue/Greenデプロイの動作について細かく確認したいと思います。

参考