ECSへのデプロイ方法をローリングアップデートからblue/greenに変える手順を記す
事前確認
ローリングアップデートとblue/greenデプロイのどちらを使っているかの確認
- ECSへの直接デプロイはローリングアップデート
- CodeBuildを利用したデプロイはblue/greenデプロイ
主にこの構成のようだ。(2023/08/18現在)
デプロイ時にCodeDeployの履歴に何も増えなければ恐らくECSへの直接デプロイがされてるはず。
ターゲットグループを作る
特にblue用とgreen用のターゲットグループがあるというわけではなく
blue/greenデプロイでは2個のターゲットグループがスイッチングされる
- ALBのリスナールールに紐づくターゲットグループが更新される
- ECSに紐づくターゲットグループが更新される
という動作のようだ。
なので既にローリングアップデートしている場合は利用しているターゲットグループが1個はあるはずなので、それと同じようなターゲットグループをもう1個作れば良い。
2個のターゲットグループは同じALBに紐づくようにしておく。
サービスロールの作成
AWSCodeDeployRoleForECS のセット(?)でロールを作っておけば良いようだ
JSONを見てみると次の通り
{
"Version": "2012-10-17",
"Statement": [
{
"Action": [
"ecs:DescribeServices",
"ecs:CreateTaskSet",
"ecs:UpdateServicePrimaryTaskSet",
"ecs:DeleteTaskSet",
"elasticloadbalancing:DescribeTargetGroups",
"elasticloadbalancing:DescribeListeners",
"elasticloadbalancing:ModifyListener",
"elasticloadbalancing:DescribeRules",
"elasticloadbalancing:ModifyRule",
"lambda:InvokeFunction",
"cloudwatch:DescribeAlarms",
"sns:Publish",
"s3:GetObject",
"s3:GetObjectVersion"
],
"Resource": "*",
"Effect": "Allow"
},
{
"Action": [
"iam:PassRole"
],
"Effect": "Allow",
"Resource": "*",
"Condition": {
"StringLike": {
"iam:PassedToService": [
"ecs-tasks.amazonaws.com"
]
}
}
}
]
}
CodeDeployでデプロイアプリを作る
コンピューティングタイプ : Amazon ECS
デプロイグループを作る
Web Console の場合
- ECSのクラスタやサービス
- ECSで利用しているALB
- 2個のターゲットグループ
などを指定していく
この
サービスロールは事前に作っておけば選択肢に出てくる
ポートの指定は本番稼働用とテスト用で別のものを選ぶ
ALBのリスナールールに少なくとも2種類のポートのルールがなければいけないかもしれない
Terraformの場合
サンプルのコードを参考に組み立てる
resource "aws_codedeploy_app" "example" {
compute_platform = "ECS"
name = "example"
}
resource "aws_codedeploy_deployment_group" "example" {
app_name = aws_codedeploy_app.example.name
deployment_config_name = "CodeDeployDefault.ECSAllAtOnce"
deployment_group_name = "example"
service_role_arn = aws_iam_role.example.arn
auto_rollback_configuration {
enabled = true
events = ["DEPLOYMENT_FAILURE"]
}
blue_green_deployment_config {
deployment_ready_option {
action_on_timeout = "CONTINUE_DEPLOYMENT"
}
terminate_blue_instances_on_deployment_success {
action = "TERMINATE"
termination_wait_time_in_minutes = 5
}
}
deployment_style {
deployment_option = "WITH_TRAFFIC_CONTROL"
deployment_type = "BLUE_GREEN"
}
ecs_service {
cluster_name = aws_ecs_cluster.example.name
service_name = aws_ecs_service.example.name
}
load_balancer_info {
target_group_pair_info {
prod_traffic_route {
listener_arns = [aws_lb_listener.example.arn]
}
target_group {
name = aws_lb_target_group.blue.name
}
target_group {
name = aws_lb_target_group.green.name
}
}
}
}
デプロイ設定
AWSに最初からデプロイ設定のひながたが数種類あるので特に自分では設定しなくてOK
ECSサービスのデプロイタイプを変える
WebConsoleの場合
単に設定を変えるということは出来なさそう。
ECSサービス作成時にはデプロイタイプでブルー/グリーンが選べるのでいちどECSサービスを削除してから再度作成するのが良さそう。
(ちなみに新しいUIだとローリングアップデートしか選べなくなっているようだ)
Terraformの場合
デプロイタイプを変えて terraform apply をするとECSサービスが破棄・再作成されるようなので注意
blue/greenデプロイの場合はECSサービスに紐づくタスク定義の更新が出来なくなるようなのでタスク定義の変更をignoreしておく
ロードバランサも
(本当にこの対処で良いのかは調べきれていないが)
resource "aws_ecs_service" "default" {
...
deployment_controller {
type = "CODE_DEPLOY"
}
lifecycle {
ignore_changes = [task_definition, load_balancer]
}
}
CodePipelineのDeployステージの設定を変える
Web Consoleの場合
アクションプロバイダを Amazon ECS (ブルー/グリーン)に変える
あとはテストアプリケーション・テストグループなどを選んで行く
今回の例ではビルドステージを使っているので入力にはBuildのアーティファクトを選んでいる
Terraformの場合
ビルドステージをDeployの入力とする場合は次のような感じ
stage {
name = "Deploy"
action {
... いろいろ省略 ...
provider = "CodeDeployToECS"
input_artifacts = ["Build"]
configuration = {
ApplicationName = "deploy-app"
DeploymentGroupName = "deploy-group"
Image1ArtifactName = "Build"
Image1ContainerName = "IMAGE1_NAME"
TaskDefinitionTemplateArtifact = "Build"
TaskDefinitionTemplatePath = "taskdef.json"
AppSpecTemplateArtifact = "Build"
AppSpecTemplatePath = "appspec.yml"
}
}
}
appspec.yml を追加する
レポジトリのトップディレクトリに appspec.yml または appspec.json を設定する
<TASK_DEFINITION>
の部分はプレースホルダでCodeDeployがタスク定義のARNに変えてくれる
ContainerName / ContainerPort は既存のタスク定義を参照するなりして確認すればOK
version: '0.0'
Resources:
- TargetService:
Type: AWS::ECS::Service
Properties:
TaskDefinition: <TASK_DEFINITION>
LoadBalancerInfo:
ContainerName: container-name
ContainerPort: 80
ちなみにファイル名はCodePipelineのDeployステージの設定で変更可能
taskdef.json を作成する
レポジトリのトップにファイルを置いておく
既存のタスク定義の JSON をひな形にして作るのがやりやすい。
(AWS Web Console でもタスク定義画面からJSONが取れるはず)
ただ出力されたJSONと入力に使うJSONは異なるので修正する
- registeredAt
- registeredBy
- requiresAttributes
- status
- revision
とかは不要な項目のようだ
あとtags項目があるとエラーが起こるので自分が試した時は消しておいた
image はCodePipelineで指定したプレースホルダにする
{
"containerDefinitions": [
{
"name": "container-name",
"image": "<IMAGE1_NAME>",
ちなみにlocalファイルを指定してタスク定義登録をしようとすると不要項目がある場合は教えてくれるので試しても良い
aws ecs register-task-definition --cli-input-json file://taskdef.json
どうやってタスク定義の値を動的に変えるのか?
イメージのプレースホルダ以外は動的に変わらなそう。
調べると buildステージ ( buildspec.yml ) でsedコマンドを使うなりして環境に応じて無理やりファイル内容を変えてしまう方法が紹介されていた。
参考:
ECS Fargateデプロイ用CodePipelineのtaskdef.jsonに変数を持たせる方法
https://qiita.com/neruneruo/items/a5313beb3074a1af4df2
他には環境ごとにtaskdef.jsonを用意しておくなどやり方は色々あるだろうが
buildspec.yml を修正する
- ECS直接デプロイでは
imagedefinitions.json
を作成してたがこれをimageDetail.json
に変える - appspec をDeployステージ用の出力に含める
- appspec をDeployステージ用の出力に含める
post_build:
commands:
...
- printf '{"Version":"1.0","ImageURI":"%s"}' $REPOSITORY_URI:$IMAGE_TAG > imageDetail.json
artifacts:
files:
- imageDetail.json
- taskdef.json
- appspec.yml
いざ実行
- ECSサービスのサービス更新を試す
- ソースレポジトリ (Githubとか) に push する
などして動作を試す
チャットメンバー募集
何か質問、悩み事、相談などあればLINEオープンチャットもご利用ください。