こんばんは!ヒカリです。
Webエンジニアを目指してプログラミングを勉強している初心者の方向けに情報を発信しています。
今流行の仮想通貨を自動売買してみました!
リモートワークでおすすめのグッズ紹介してみました!
今回はこのような質問をいただきました。
CloudFormationですべて管理してるみたいだけど、どうやってデプロイしてるの?
私がインフラを担当した案件で「インフラのすべての管理をCloudFormationで行う」という要件の案件がありました。
そこで、その案件ではどのようにデプロイを行っているのかという質問がきました。
今回はこちらの質問について解説していきます!
前提条件
- CloudFormationでECSを構築していること
- CloudFormationのパラメータでイメージタグを指定できること
インフラ構成
本題はどのようにCloudFormationを更新しているかという部分になりますが、その前にインフラ構成を載せておきます。
CloudFormationのパラメータでECSのイメージタグを管理しています。
そのため、デプロイするにはCloudFormationのパラメータを変更する必要があります。
また、CloudFormationテンプレートはS3に配置されているものとします。

流れとしては以下のようになっています。
- GitHub ActionsからECRへイメージをプッシュ
- ECRへプッシュされたことをトリガーにCodePipelineを実行
- CodePipelineでCodeBuildを実行
- CodeBuildからCloudFormationのパラメータを変更
CodeBuildからCloudFormationのパラメータを変更する
では今回のメインである「CodeBuildからCloudFormationのパラメータを変更」について解説していきます。
CodeBuildからということでbuildspec.ymlの解説になります。
CodeBuildでは以下のことを行っています。
- AWS CLI v2のインストール
- ECRから最新のイメージタグを取得する
- CloudFormationのパラメータを作成する
- CloudFormationを更新する
各ステップはphasesで別れています。
- ECRリポジトリ名:sample-repository
- CloudFormationスタック名:sample-stack
- CloudFormationテンプレート配置S3バケット名:sample-bucket
version: 0.2
phases:
install:
commands:
# AWS CLI v2
- curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
- unzip awscliv2.zip
- ls -l /root/.pyenv/shims/aws
- ./aws/install --bin-dir /root/.pyenv/shims --install-dir /usr/local/aws-cli --update
pre_build:
commands:
# GET IMAGE TAG
- IMAGE_DIGEST=$(aws ecr list-images --repository-name sample-repository --query "imageIds[?imageTag=='latest'] | [0].imageDigest")
- IMAGE_DIGEST=`echo $IMAGE_DIGEST | sed "s/\"/'/g"`
- IMAGE_TAG=$(aws ecr list-images --repository-name sample-repository --query "imageIds[?imageDigest==$IMAGE_DIGEST && imageTag!='latest'] | [0].imageTag")
build:
commands:
# CREATE PARAMETERS
- 'PARAMETERS=$(aws cloudformation describe-stacks --output json --stack-name sample-stack | jq ".Stacks[].Parameters |= .+[{ \"ParameterKey\": \"ImageTag\", \"ParameterValue\": $IMAGE_TAG }]" | jq ".Stacks[].Parameters")'
- 'INPUT="{ \"StackName\": \"sample-stack\", \"TemplateURL\": \"https://sample-bucket.s3-ap-northeast-1.amazonaws.com/cfn/application/master.yaml\", \"Parameters\": $PARAMETERS }"'
- echo $INPUT > input.json
- cat ./input.json | jq .
post_build:
# UPDATE
commands:
- aws cloudformation update-stack --cli-input-json file://./input.json
次に詳しく見ていきます。
AWS CLI v2のインストール
- curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
- unzip awscliv2.zip
- ls -l /root/.pyenv/shims/aws
- ./aws/install --bin-dir /root/.pyenv/shims --install-dir /usr/local/aws-cli --update
プラットフォームはAmazon Linux 2を使っていますが、AWS CLI v1しか入っていないのでAWS CLI v2をインストールします。
こちらの解説は特に不要ですね。
ECRから最新のイメージタグを取得する
aws ecr list-iamgesコマンドでECRのイメージ一覧を取得します。
実行すると以下のようなJSONを取得できます。
{
"imageIds": [
{
"imageDigest": "sha256:99c6fb4377e9a420a1eb3b410a951c9f464eff3b7dbc76c65e434e39b94b6570",
"imageTag": "latest"
},
{
"imageDigest": "sha256:99c6fb4377e9a420a1eb3b410a951c9f464eff3b7dbc76c65e434e39b94b6570",
"imageTag": "v1.13.8"
},
{
"imageDigest": "sha256:99c6fb4377e9a420a1eb3b410a951c9f464eff3b7dbc76c65e434e39b94b6570",
"imageTag": "v1.13.7"
},
{
"imageDigest": "sha256:4a1c6567c38904384ebc64e35b7eeddd8451110c299e3368d2210066487d97e5",
"imageTag": "v1.13.6"
}
]
}
このJSONから最新のイメージタグを取得します。
一般的には最新のイメージタグはlatestとvX.X.Xの2つが存在すると思います。
そのうちのlatestではないほうを取得します。
このサンプルJSONの場合はv1.13.8となります。
- IMAGE_DIGEST=$(aws ecr list-images --repository-name sample-repository --query "imageIds[?imageTag=='latest'] | [0].imageDigest")
1回でイメージタグを取得するのは困難なため、まずは最新イメージのimageDigestを取得します。
iamgeTag==‘latest’のイメージのimageDigestを取得します。
- IMAGE_DIGEST=`echo $IMAGE_DIGEST | sed "s/\"/'/g"`
取得した値の前後にダブルクォートが含まれてしまっているのでsedコマンドで取り除きます。
- IMAGE_TAG=$(aws ecr list-images --repository-name sample-repository --query "imageIds[?imageDigest==$IMAGE_DIGEST && imageTag!='latest'] | [0].imageTag")
最後にimageDigest==[取得したimageDigest]、imageTag!=’latest’のイメージのimageTagを取得します。
CloudFormationのパラメータを作成する
aws cloudformation describe-stacksコマンドでCloudFormationのスタック情報を取得して、その中からパラメータの情報を抽出します。
それと同時に先ほど取得したイメージタグの情報をパラメータにセットします。
その後、すべてのパラメータをJSONファイルに出力しておきます。
- 'PARAMETERS=$(aws cloudformation describe-stacks --output json --stack-name sample-stack | jq ".Stacks[].Parameters |= .+[{ \"ParameterKey\": \"ImageTag\", \"ParameterValue\": $IMAGE_TAG }]" | jq ".Stacks[].Parameters")'
スタック名を指定してスタック情報からパラメータ情報を抽出します。
取得したパラメータ情報に先ほど取得したイメージタグの情報を追加します。
作成したパラメータ情報には、もともと存在したイメージタグの情報と新たに追加したイメージタグの情報の2つが存在することになりますが、後勝ちとなるので追加したイメージタグの情報が優先されます。
そのため、もともと存在したイメージタグの情報の削除は不要です。
- 'INPUT="{ \"StackName\": \"sample-stack\", \"TemplateURL\": \"https://sample-bucket.s3-ap-northeast-1.amazonaws.com/cfn/application/master.yaml\", \"Parameters\": $PARAMETERS }"'
取得したパラメータ情報に、スタック名、テンプレートURL(テンプレートの配置先)を追加してCloudFormation更新用のパラメータを作成します。
- echo $INPUT > input.json
取得したJSONをinput.jsonに書き込みます。
- cat ./input.json | jq .
エラーが起きたときのためにパラメータをログを出力しておきます。
CloudFormationを更新する
- aws cloudformation update-stack --cli-input-json file://./input.json
最後に先ほど出力したJSONを指定してCloudFormationを更新します。
これでCloudFormationのスタック更新が始まるのでCloudFormationが正常であればデプロイが完了します。
まとめ
デプロイ全体の流れは以下のようになります。
- GitHub ActionsからECRへイメージをプッシュ
- ECRへプッシュされたことをトリガーにCodePipelineを実行
- CodePipelineでCodeBuildを実行
- CodeBuildからCloudFormationのパラメータを変更
また、CodeBuildでは以下のようなことを行っています。
- AWS CLI v2のインストール
- ECRから最新のイメージタグを取得する
- CloudFormationのパラメータを作成する
- CloudFormationを更新する
あとがき
CloudFormationでイメージタグの管理まで行うは少し大変ですね。
環境構築までをCloudFormationに任せて、デプロイはCodePipeline、CodeDeployに任せたいです。
活用する場面は少ないと思いますが、機会があれば参考にしてみてください!