aws-ecs-codedeploy-blue-green について
AWS ECS のブルーグリーンデプロイ方式を、ALB の加重ルーティングと CodeDeploy の両面から解説するリファレンスです。トラフィック切り替え、インフラ設定、アラームによるロールバック、設定上の注意点を扱います。
主な機能
- 01ALB の重み付けによるトラフィック切り替え
- 02CodeDeploy ブルーグリーンデプロイの設定
- 03CloudWatch アラームによるロールバック
- 04Terraform のドリフトに関する注意点
活用シーン
- 01ECS ターゲットグループへのカナリアリリース
- 02既存の CodeDeploy パイプラインの継続
- 03アラーム発生時のデプロイロールバック
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ mizchi/ skills --skill aws-ecs-codedeploy-blue-greenインストール後、Claude Code で/aws-ecs-codedeploy-blue-greenを入力して読み込まれたか確認します。
同梱ファイルREADME.md
詳細
AWS ECS ブルー/グリーンデプロイ
推奨:ALB ネイティブの重み付けルーティング(推奨)
CodeDeploy を使うと、IAM ロール、appspec.json の設定、独立したコントロールプレーンが必要になります。ほとんどの ECS ブルー/グリーンのニーズには、ALB の重み付けターゲットグループを使えば、より少ない設定で同じ結果が得られます。
仕組み
2 つのターゲットグループ(ブルー/グリーン)と、重みに応じてトラフィックを振り分ける単一のリスナールールを定義します。
resource "aws_lb_target_group" "blue" {
name = "myapp-blue"
port = 8080
protocol = "HTTP"
vpc_id = var.vpc_id
target_type = "ip"
health_check {
path = "/health"
interval = 15
healthy_threshold = 2
unhealthy_threshold = 3
}
}
resource "aws_lb_target_group" "green" {
# identical to blue
name = "myapp-green"
# ...
}
resource "aws_lb_listener_rule" "weighted" {
listener_arn = aws_lb_listener.main.arn
priority = 100
condition {
path_pattern { values = ["/*"] }
}
action {
type = "forward"
forward {
target_group {
arn = aws_lb_target_group.blue.arn
weight = 100
}
target_group {
arn = aws_lb_target_group.green.arn
weight = 0
}
stickiness {
enabled = true
duration = 300 # keep users on the same TG during rollout
}
}
}
}
トラフィックの切り替え手順
# Canary: send 10% to green
aws elbv2 modify-rule --rule-arn <rule-arn> \
--actions '[{"Type":"forward","ForwardConfig":{"TargetGroups":[{"TargetGroupArn":"<blue-arn>","Weight":90},{"TargetGroupArn":"<green-arn>","Weight":10}]}}]'
# Full cutover
aws elbv2 modify-rule --rule-arn <rule-arn> \
--actions '[{"Type":"forward","ForwardConfig":{"TargetGroups":[{"TargetGroupArn":"<blue-arn>","Weight":0},{"TargetGroupArn":"<green-arn>","Weight":100}]}}]'
# Rollback: flip back to blue
aws elbv2 modify-rule --rule-arn <rule-arn> \
--actions '[{"Type":"forward","ForwardConfig":{"TargetGroups":[{"TargetGroupArn":"<blue-arn>","Weight":100},{"TargetGroupArn":"<green-arn>","Weight":0}]}}]'
または、Terraform で重みを更新して適用します。appspec も CodeDeploy IAM ロールも、独立したコントロールプレーンも不要です。
CodeDeploy を代わりに使う場合
- チームですでに CodeDeploy パイプラインを使用しており、そのまま使い続けたい場合
- カスタムスクリプトなしで、CloudWatch アラームをトリガーとする自動ロールバックが必要な場合
- 移行/スモークテストの手順にフック(BeforeInstall、AfterInstall、AfterAllowTraffic)が必要な場合
CodeDeploy ECS ブルー/グリーン(リファレンス)
見落としやすい重要なポイント:lifecycle.ignore_changes
CodeDeploy を使って ECS のブルー/グリーンデプロイを管理する場合、CodeDeploy は ALB リスナーの default_action.target_group_arn をブルーとグリーンのターゲットグループ間で動的に切り替えます。OpenTofu/Terraform でこの変更を抑制しないと、その後の tofu plan のたびにドリフトが表示され、元のターゲットグループに戻そうとします。その結果、デプロイのたびに CodeDeploy と競合します。
解決策:リスナーに必ず lifecycle.ignore_changes を追加します。
resource "aws_lb_listener" "bg_demo" {
load_balancer_arn = aws_lb.main.arn
port = 8080
protocol = "HTTP"
default_action {
type = "forward"
target_group_arn = aws_lb_target_group.bg_demo_blue.arn # initial state only
}
# CodeDeploy swaps default_action.target_group_arn between blue and green.
# Without this, tofu plan constantly shows drift.
lifecycle {
ignore_changes = [default_action]
}
}
全体のパターン
2 つのターゲットグループ(ブルーとグリーン)
resource "aws_lb_target_group" "bg_demo_blue" {
name = "myapp-blue"
port = 8080
protocol = "HTTP"
vpc_id = var.vpc_id
target_type = "ip"
health_check {
path = "/health"
interval = 15
healthy_threshold = 2
unhealthy_threshold = 3
}
deregistration_delay = 10 # short for faster deployments
}
resource "aws_lb_target_group" "bg_demo_green" {
# identical to blue
name = "myapp-green"
# ...
}
CodeDeploy アプリとデプロイグループ
resource "aws_iam_role" "codedeploy_ecs" {
name = "myapp-codedeploy-ecs"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [{ Effect = "Allow", Principal = { Service = "codedeploy.amazonaws.com" }, Action = "sts:AssumeRole" }]
})
}
resource "aws_iam_role_policy_attachment" "codedeploy_ecs" {
role = aws_iam_role.codedeploy_ecs.name
policy_arn = "arn:aws:iam::aws:policy/AWSCodeDeployRoleForECS"
}
resource "aws_codedeploy_app" "app" {
compute_platform = "ECS"
name = "myapp"
}
resource "aws_codedeploy_deployment_group" "app" {
app_name = aws_codedeploy_app.app.name
deployment_group_name = "myapp-dg"
service_role_arn = aws_iam_role.codedeploy_ecs.arn
deployment_style {
deployment_option = "WITH_TRAFFIC_CONTROL"
deployment_type = "BLUE_GREEN"
}
blue_green_deployment_config {
deployment_ready_option {
action_on_timeout = "CONTINUE_DEPLOYMENT" # auto-shift, no manual confirmation
}
terminate_blue_instances_on_deployment_success {
action = "TERMINATE"
termination_wait_time_in_minutes = 5 # give blue tasks 5 min to finish in-flight requests
}
}
# Traffic shift configs:
# CodeDeployDefault.ECSCanary10Percent5Minutes — 10% for 5 min, then 100%
# CodeDeployDefault.ECSLinear10PercentEvery1Minutes — 10% per minute (10 min total)
# CodeDeployDefault.ECSAllAtOnce — immediate 100%
deployment_config_name = "CodeDeployDefault.ECSCanary10Percent5Minutes"
ecs_service {
cluster_name = aws_ecs_cluster.main.name
service_name = aws_ecs_service.app.name
}
load_balancer_info {
target_group_pair_info {
prod_traffic_route {
listener_arns = [aws_lb_listener.bg_demo.arn] # listener ARN, not rule ARN
}
target_group { name = aws_lb_target_group.bg_demo_blue.name }
target_group { name = aws_lb_target_group.bg_demo_green.name }
}
}
auto_rollback_configuration {
enabled = true
events = ["DEPLOYMENT_FAILURE", "DEPLOYMENT_STOP_ON_ALARM"]
}
alarm_configuration {
enabled = true
alarms = [aws_cloudwatch_metric_alarm.blue_5xx.alarm_name, aws_cloudwatch_metric_alarm.green_5xx.alarm_name]
}
}
自動ロールバック用の CloudWatch アラーム
DEPLOYMENT_STOP_ON_ALARM が設定されている場合、CodeDeploy はカナリアフェーズ中にこれらのアラームを監視します。いずれかのアラームが発報すると、デプロイは停止してロールバックされます。
resource "aws_cloudwatch_metric_alarm" "blue_5xx" {
alarm_name = "myapp-blue-5xx"
metric_name = "HTTPCode_Target_5XX_Count"
namespace = "AWS/ApplicationELB"
statistic = "Sum"
period = 60
evaluation_periods = 1
threshold = 1 # production: use 5-10 to reduce false positives
comparison_operator = "GreaterThanOrEqualToThreshold"
treat_missing_data = "notBreaching"
dimensions = {
LoadBalancer = aws_lb.main.arn_suffix
TargetGroup = aws_lb_target_group.bg_demo_blue.arn_suffix
}
}
# identical for green
デプロイのトリガー(appspec.json)
CodeDeploy では、新しいタスク定義を指定する appspec.json が必要です。これはデプロイ時に渡すものであり、Terraform では管理されません。
{
"version": 1,
"Resources": [{
"TargetService": {
"Type": "AWS::ECS::Service",
"Properties": {
"TaskDefinition": "<NEW_TASK_DEF_ARN>",
"LoadBalancerInfo": {
"ContainerName": "app",
"ContainerPort": 8080
}
}
}
}]
}
aws deploy create-deployment \
--application-name myapp \
--deployment-group-name myapp-dg \
--revision revisionType=AppSpecContent,appSpecContent={content="$(cat appspec.json)"}
よくある落とし穴
prod_traffic_route.listener_arnsにはリスナー ARN を指定する必要があり、リスナールール ARN は指定できません。 ここでルール ARN を使うと、CodeDeploy は失敗しても何も通知しないか、分かりにくいエラーを返します。- ブルーとグリーンの両方のターゲットグループで、ヘルスチェックの設定を同一にする必要があります。
healthy_threshold/unhealthy_thresholdの値が一致していないと、一方の TG が常に異常と判定されます。 - ブルー/グリーンデプロイ中に
aws_ecs_serviceのdesired_countを変更しないでください。 デプロイ中のタスク数は CodeDeploy が個別に管理します。 - 本番環境の CloudWatch アラームのしきい値:
thresholdには 1 ではなく 5~10 を使用してください。カナリア期間中に偶発的な 5xx が 1 件発生しただけで、本番環境の負荷においてロールバックがトリガーされます。