mcpField
A
aws-ecs-codedeploy-blue-green

aws-ecs-codedeploy-blue-green

github.com/mizchiクラウド・DevOps41640 回インストール1 週間前に更新
リポジトリ

aws-ecs-codedeploy-blue-green について

AWS ECS のブルーグリーンデプロイ方式を、ALB の加重ルーティングと CodeDeploy の両面から解説するリファレンスです。トラフィック切り替え、インフラ設定、アラームによるロールバック、設定上の注意点を扱います。

主な機能

  1. 01ALB の重み付けによるトラフィック切り替え
  2. 02CodeDeploy ブルーグリーンデプロイの設定
  3. 03CloudWatch アラームによるロールバック
  4. 04Terraform のドリフトに関する注意点

活用シーン

  1. 01ECS ターゲットグループへのカナリアリリース
  2. 02既存の CodeDeploy パイプラインの継続
  3. 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 件発生しただけで、本番環境の負荷においてロールバックがトリガーされます。

コメント