You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ECS多服务CodeDeploy蓝绿部署二次部署失败问题排查

问题背景

我有一个运行N个面向公网服务的AWS ECS集群,每个服务对应不同的容器化应用,通过AWS CodeDeploy管理蓝绿部署。HTTPS流量通过应用负载均衡器(ALB)基于主机头路由规则分发(每个服务对应公共根域名下的唯一子域名,如one.example.com、two.example.com等)。所有基础设施通过Terraform管理。

蓝绿配置模块代码(blue-green-config/main.tf)

# Blue/Green networking configuration for ECS service

resource "aws_lb_target_group" "blue" {
  name                 = "${var.env}-${var.service_name}-blue"
  port                 = 80
  protocol             = "HTTP"
  vpc_id               = var.vpc_id
  deregistration_delay = 60

  health_check {
    path     = var.health_check_path
    protocol = "HTTP"
  }

  lifecycle {
    create_before_destroy = true
  }
}

resource "aws_lb_target_group" "green" {
  name                 = "${var.env}-${var.service_name}-green"
  port                 = 80
  protocol             = "HTTP"
  vpc_id               = var.vpc_id
  deregistration_delay = 60

  health_check {
    path     = var.health_check_path
    protocol = "HTTP"
  }

  lifecycle {
    create_before_destroy = true
  }
}

resource "aws_lb_listener_rule" "service_host_rule" {
  listener_arn = var.https_listener_arn

  action {
    type             = "forward"
    target_group_arn = aws_lb_target_group.blue.arn
  }

  condition {
    host_header {
      values = [var.service_domain_name]
    }
  }
}

如代码所示,服务子域名对应的监听器规则当前仅将流量转发至blue目标组。

ECS服务模块的部署配置(ecs-service/deployment.tf)

# deployment.tf in ECS service module
resource "aws_codedeploy_deployment_group" "this" {
  app_name               = var.code_deploy.app_name
  deployment_config_name = "CodeDeployDefault.ECSAllAtOnce"
  deployment_group_name  = var.ecs_service_name
  service_role_arn       = var.code_deploy.service_role.arn

  auto_rollback_configuration {
    enabled = true
    events  = ["DEPLOYMENT_FAILURE"]
  }

  dynamic "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 = 10
    }
  }

  deployment_style {
    deployment_option = "WITH_TRAFFIC_CONTROL"
    deployment_type   = "BLUE_GREEN"
  }

  ecs_service {
    cluster_name = var.cluster_name
    service_name = var.ecs_service_name
  }

  load_balancer_info {
    target_group_pair_info {
      target_group {
        # The Blue target group created in the above module
        name = var.alb_target_groups.blue.name
      }

      target_group {
        # The Green target group created in the above module
        name = var.alb_target_groups.green.name
      }

      prod_traffic_route {
        listener_arns = [var.alb_listener_arn]
      }
    }
  }
}

基础设施结构

infrastructure/
 |- modules/
 |  |- ecs-service/
 |  |  |- modules/
 |  |  |  |- blue-green-config/
 |  |  |  |  |- main.tf  
 |  |  |- main.tf
 |  |  |- deployment.tf

ECS服务模块会单独实例化蓝绿配置模块。

问题现象

CI/CD流水线流程:先执行Terraform创建目标组、监听器规则和部署组,随后通过AWS CLI执行aws deploy create-deployment ...启动部署。

  • 首次部署可正常完成,流量全部路由至green目标组,blue组中的任务被终止。
  • 第二次新版本部署时,CodeDeploy返回错误:

The ELB could not be updated due to the following error: Primary taskset target group must be behind listener arn:aws:elasticloadbalancing:xxxxxx:xxxxxx:listener-rule/app/xxxx/xxxx/xxxx/xxxx.

推测原因是CodeDeploy期望当前流量路由至blue目标组,但上次部署后的状态导致下一次部署失败。想确认配置是否存在问题,或是部署完成后缺少某个手动步骤?


解决方案

问题核心在于Terraform定义的监听器规则静态绑定到blue目标组,但CodeDeploy完成蓝绿部署后会修改监听器规则指向green目标组,导致Terraform期望状态与AWS实际状态冲突,同时CodeDeploy下一次部署时无法找到当前作为"蓝色"的目标组。

核心问题分析

  1. 首次部署后,CodeDeploy会将ALB监听器规则从blue切换到green目标组,并且终止blue目标组的任务。但你的Terraform代码中,监听器规则仍定义为指向blue目标组,这会导致:

    • 后续Terraform执行会试图把监听器规则改回blue,破坏部署状态;
    • 第二次部署时,CodeDeploy认为当前的"蓝色"目标组应该是green(因为流量现在指向它),但部署组配置里仍把blue作为初始蓝色组,同时监听器规则的实际状态与Terraform定义冲突,引发错误。
  2. terminate_blue_instances_on_deployment_success配置为TERMINATE,会导致旧的目标组对应的任务被销毁,下一次部署时没有可用的"蓝色"任务集作为基础。

修复步骤

1. 修改Terraform监听器规则,不再硬编码目标组

保留Terraform对监听器规则的创建管理,但允许CodeDeploy动态调整目标组,添加ignore_changes忽略目标组ARN的变化:

resource "aws_lb_listener_rule" "service_host_rule" {
  listener_arn = var.https_listener_arn

  action {
    type             = "forward"
    target_group_arn = aws_lb_target_group.blue.arn
  }

  condition {
    host_header {
      values = [var.service_domain_name]
    }
  }

  lifecycle {
    ignore_changes = [action.0.target_group_arn]
  }
}

2. 调整CodeDeploy部署组配置

将终止旧实例的配置改为KEEP_ALIVE,保留旧目标组任务作为下一次部署的蓝色组:

terminate_blue_instances_on_deployment_success {
  action                           = "KEEP_ALIVE"
  termination_wait_time_in_minutes = 10
}

保持deployment_style中的deployment_option为WITH_TRAFFIC_CONTROL,让CodeDeploy全权负责流量切换。

3. 同步第一次部署后的Terraform状态

第一次部署后,监听器规则已指向green,执行以下命令让Terraform更新本地状态以匹配AWS实际状态:

terraform apply -refresh-only

4. 调整CI/CD流水线

确保每次部署后,若必须执行Terraform,要保证ignore_changes已正确配置,避免Terraform覆盖CodeDeploy的流量配置。

额外说明

对于多服务共享同一个ALB的场景,每个服务的蓝绿部署独立管理自己的监听器规则目标组是可行的,但必须确保Terraform不干涉CodeDeploy对目标组的修改。使用ignore_changes是最稳妥的方式,既保留Terraform对规则的创建管理,又允许CodeDeploy动态调整目标组。


内容的提问来源于stack exchange,提问作者Jesse

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.02 15:41:47