CloudFormation と GitHub Actions と CodeDeploy で ECS の Blue/Green デプロイする
アルダグラムでエンジニアをしている @sukechannnn です!
最近、社内でデプロイ基盤を刷新するプロジェクトを進めていたのですが、その中で CloudFormation と GitHub Actions と CodeDeploy を使った ECS の Blue/Green デプロイをしようとしたところ、調べても情報が出てこなくて困りました。
公式ドキュメントを読みながら設定した結果、上手くできたので紹介します。
違う、そうじゃない
「CodeDeploy CFn Fargate Blue/Green デプロイ」でググってよく出てくる方法は、公式に書いてある AWS::CodeDeploy::BlueGreen hook を使った方法 です。このやり方は「CFn で CodeDeploy の Blue/Green デプロイをトリガーする」方法であり、やりたいことと微妙に違いました。
今回やりたかったのは以下のような方法です。
- CFn は CodeDeploy のセットアップ、ECS Service, TaskDefinition などAWSリソースの構成管理にのみ利用
- CodeDeploy による ECS の Blue/Green デプロイのトリガーは GitHub Actions 経由で行う
設定方法
CloudFormation の設定
CFn の yaml の例は以下の通りです(CodeDeploy に関する設定のみ書いています)。
Import しているリソースは、予め作られている想定です。
- demo-ecs-cluster
- demo-ecs-service
- demo-target-group
- demo-alb-listener
- demo-ecs-codedeploy-role
AWSTemplateFormatVersion: "2010-09-09"
Description: CodeDeploy with ECS Blue/Green deploy
Resources:
EcsCodeDeploy:
Type: AWS::CodeDeploy::Application
Properties:
ApplicationName: "demo-ecs-codedeploy"
ComputePlatform: ECS
EcsDeploymentGroup:
Type: AWS::CodeDeploy::DeploymentGroup
Properties:
ApplicationName: !Ref EcsCodeDeploy
AutoRollbackConfiguration:
Enabled: True
Events:
- "DEPLOYMENT_FAILURE"
BlueGreenDeploymentConfiguration:
DeploymentReadyOption:
ActionOnTimeout: CONTINUE_DEPLOYMENT
WaitTimeInMinutes: 0
TerminateBlueInstancesOnDeploymentSuccess:
Action: TERMINATE
TerminationWaitTimeInMinutes: 5
DeploymentConfigName: "CodeDeployDefault.ECSAllAtOnce"
DeploymentGroupName: "demo-ecs-codedeploy-group"
DeploymentStyle:
DeploymentOption: "WITH_TRAFFIC_CONTROL"
DeploymentType: "BLUE_GREEN"
ECSServices:
- ClusterName:
Fn::ImportValue: "demo-ecs-cluster"
ServiceName:
Fn::ImportValue: "demo-ecs-service"
LoadBalancerInfo:
TargetGroupPairInfoList:
- TargetGroups:
- Name:
Fn::ImportValue: "demo-target-group-blue"
- Name:
Fn::ImportValue: "demo-target-group-green"
ProdTrafficRoute:
ListenerArns:
- Fn::ImportValue: "demo-alb-listener"
ServiceRoleArn:
Fn::ImportValue: "demo-ecs-codedeploy-role-arn"
ポイントは以下の通りです。
- AWS::CodeDeploy::Application
- ComputePlatform を ECS に設定する
- AWS::CodeDeploy::DeploymentGroup
- DeploymentConfigName を "CodeDeployDefault.ECSAllAtOnce" に設定する(公式ドキュメント)
-
DeploymentStyle を以下の設定にする
- DeploymentOption: "WITH_TRAFFIC_CONTROL"
- DeploymentType: "BLUE_GREEN"
- Blue/Green デプロイをする時は
AutoScalingGroups
,LoadBalancerInfo
,Deployment
properties を設定してはいけない
あとは BlueGreenDeploymentConfiguration で好きな設定にすればOKです。上記の設定では、タイムアウトはなし、デプロイが完了した時の古いインスタンスを終了するまでの時間を5分で設定してます。
GitHub Actions の設定
次は GitHub Actions の設定です。main ブランチにマージしたらデプロイする想定で書いています。
name: Deploy app to Amazon ECS
pull_request:
types: [ closed ]
branches:
- 'main'
# permission can be added at job level or workflow level
permissions:
id-token: write
jobs:
ecr-push:
name: Deploy
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v1-node16
with:
role-to-assume: arn:aws:iam::xxxxx:role/GithubActionsRole
role-session-name: GitHubActions-${{ github.run_id }}
aws-region: ap-northeast-1
- name: Login to Amazon ECR
id: login-ecr
uses: aws-actions/amazon-ecr-login@v1
# DockerイメージをビルドしてECRにプッシュ
- name: Docker image build and push to Amazon ECR
id: build-image
env:
ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
ECR_REPOSITORY: path/to/demo-app
IMAGE_TAG: ${{ github.sha }}
run: |
echo $ECR_REGISTRY/$ECR_REPOSITORY
docker login -u AWS -p $(aws ecr get-login-password) https://xxxx.dkr.ecr.ap-northeast-1.amazonaws.com
docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG -f Dockerfile --build-arg AWS_ACCOUNT_ID="xxxxx" --build-arg IMAGE_TAG=$IMAGE_TAG .
docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG
echo "image=$ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG" >> $GITHUB_OUTPUT
# amazon-ecs-render-task-definition を使うために TaskDefinition を ECS から取ってくる
# TaskDefinition を CFn で管理しているためこうしている
- name: Fetch TaskDefinition from ECS and generate task-def-api.json (api)
run: |
aws ecs describe-task-definition --task-definition app-task-definition | \
jq '.taskDefinition | del (.taskDefinitionArn, .revision, .status, .requiresAttributes, .compatibilities)' > task-def-api.json
# TaskDefinition の image を push した最新のものに書き換える
- name: Render TaskDefinition
id: render-container-api
uses: aws-actions/amazon-ecs-render-task-definition@v1
with:
task-definition: task-def-api.json
container-name: app-service-container
image: ${{ steps.login-ecr.outputs.registry }}/path/to/demo-app:${{ github.sha }}
# デプロイする
- name: Deploy app with CodeDeploy Blue/Green deployment
uses: aws-actions/amazon-ecs-deploy-task-definition@v1
with:
task-definition: ${{ steps.render-container-api.outputs.task-definition }}
cluster: demo-ecs-cluster
service: demo-ecs-service
image はデプロイの度にビルドして ECR に push し、その最新の image を用いてデプロイするようにしています。
以下の Action を利用して TaskDefinition の更新とデプロイを行っています。
先ほど CFn で demo-ecs-cluster の demo-ecs-service に対して CodeDeploy で Blue/Green デプロイを設定しているので、amazon-ecs-deploy-task-definition@v1 でそのクラスター/サービスを指定してデプロイすることで、CodeDeploy 経由で Blue/Green デプロイすることができます。
まとめ
CloudFormation と GitHub Actions を用いて CodeDeploy で Blue/Green デプロイする方法について紹介しました。すべてコード化/自動化できてとても便利ですね!
CloudFormation, GitHub Actions と書きましたが、CodeDeploy の設定は Terraform でもできますし、GitHub Actions で行っていることも AWS CLI を使えばできると思うので、利用している技術に応じて適宜読み替えてもらえればと思います。
株式会社アルダグラムのTech Blogです。 世界中のノンデスクワーク業界における現場の生産性アップを実現する現場DXサービス「KANNA」を開発しています。 採用情報はこちら herp.careers/v1/aldagram0508/
Discussion