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下一次部署时无法找到当前作为"蓝色"的目标组。
核心问题分析
首次部署后,CodeDeploy会将ALB监听器规则从blue切换到green目标组,并且终止blue目标组的任务。但你的Terraform代码中,监听器规则仍定义为指向blue目标组,这会导致:
- 后续Terraform执行会试图把监听器规则改回blue,破坏部署状态;
- 第二次部署时,CodeDeploy认为当前的"蓝色"目标组应该是green(因为流量现在指向它),但部署组配置里仍把blue作为初始蓝色组,同时监听器规则的实际状态与Terraform定义冲突,引发错误。
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

