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

如何在单EC2实例ECS集群中通过容量提供商与自动扩缩容实现任务重部署

问题描述

使用Terraform创建单EC2实例的ECS集群,搭配容量提供商与自动扩缩容,目标是让单个任务运行在该实例上。初始部署正常,但更新任务定义替换现有任务时,新任务一直卡在PROVISIONING状态,直到把ASG的max_size从1改成2,新任务才会部署到新实例,旧实例随后因CloudWatch告警(15分钟内15个数据点的CapacityProviderReservation < 100)被移除。现在需要在非生产环境实现:集群仅保留1个实例,且允许在同一实例上多次部署任务。

相关Terraform代码

ECS服务配置

# ECS service
resource "aws_ecs_service" "this" {
  name                 = "cluster"
  iam_role             = aws_iam_role.ecs_role.arn
  cluster              = aws_ecs_cluster.cluster.id
  task_definition      = aws_ecs_task_definition.task_definition.arn
  desired_count        = 1
  force_new_deployment = true

  load_balancer {
    target_group_arn = aws_alb_target_group.lb.arn
    container_name   = aws_ecs_task_definition.task_definition.family
    container_port   = 80
  }

  ordered_placement_strategy {
    type  = "binpack"
    field = "memory"
  }

  capacity_provider_strategy {
    capacity_provider = aws_ecs_capacity_provider.ecs_capacity_provider.name
    base              = 1
    weight            = 100
  }

  lifecycle {
    create_before_destroy = true
    ignore_changes = [
      desired_count
    ]
  }
}

自动扩缩容组配置

# Auto scaling
resource "aws_autoscaling_group" "ecs_asg" {
  name                  = "asg"
  vpc_zone_identifier   = [for subnet in var.public_subnet_ids : subnet]
  max_size              = 1
  min_size              = 1
  desired_capacity      = 1
  health_check_type     = "EC2"
  protect_from_scale_in = false

  launch_template {
    id      = aws_launch_template.template.id
    version = "$Latest"
  }

  instance_refresh {
    strategy = "Rolling"
  }

  lifecycle {
    create_before_destroy = true
  }
}

容量提供商配置

# capacity provider
resource "aws_ecs_capacity_provider" "ecs_capacity_provider" {
  name = "ecs_capacity_provider"

  auto_scaling_group_provider {
    auto_scaling_group_arn         = aws_autoscaling_group.ecs_asg.arn
    managed_termination_protection = "DISABLED"

    managed_scaling {
      maximum_scaling_step_size = 2
      minimum_scaling_step_size = 1
      status                    = "ENABLED"
      target_capacity           = 100
    }
  }
}

resource "aws_ecs_cluster_capacity_providers" "ecs_capacity_providers" {
  cluster_name = aws_ecs_cluster.cluster.name

  capacity_providers = [aws_ecs_capacity_provider.ecs_capacity_provider.name]

  default_capacity_provider_strategy {
    base              = 1
    weight            = 100
    capacity_provider = aws_ecs_capacity_provider.ecs_capacity_provider.name
  }
}

ECS任务定义配置

# ECS Task
resource "aws_ecs_task_definition" "task" {
  family = "task"
  container_definitions = jsonencode([
    {
      name      = "task",
      image     = "test",
      cpu       = "768",
      memory    = "4096",
      essential = true
      portMappings = [
        {
          containerPort = 80
          hostPort      = 80
          protocol      = "tcp"
        }
      ]
      logConfiguration = {
        logDriver = "awslogs",
        options = {
          "awslogs-group"         = aws_cloudwatch_log_group.logs.name,
          "awslogs-region"        = var.region,
          "awslogs-stream-prefix" = "app"
        }
      }
    }
  ])
  execution_role_arn = aws_iam_role.ecs_exec.arn
  task_role_arn      = aws_iam_role.ecs_task.arn
}

触发的告警信息

第二次部署任务时,会触发自动扩缩容关联的CloudWatch告警:

告警名称:TargetTracking-test-ecs-asg-AlarmHigh-e5a4556-5686-5669-26546-e745a5ed90cb
告警内容:目标追踪告警,当ECSServiceAverageCPUUtilization超过目标值(70%)时触发,触发条件为连续15个1分钟间隔的数据点超过阈值。

解决方案

核心问题分析

问题根源在于部署策略+容量提供商的目标容量配置:

  1. ECS默认采用先启动新任务、再停止旧任务的替换逻辑,但任务绑定了主机端口80,同一实例无法同时运行两个任务(端口冲突);
  2. 容量提供商target_capacity = 100的配置要求集群容量被100%利用,当部署新任务时,ECS判定当前实例无剩余资源,会触发ASG扩容,但max_size=1限制了扩容,导致新任务卡在PROVISIONING状态。

具体调整方案

1. 修改ECS服务部署策略,优先停止旧任务

在aws_ecs_service资源中添加部署控制配置,让ECS先停止旧任务再启动新任务,避免端口冲突:

resource "aws_ecs_service" "this" {
  # ... 原有配置 ...

  deployment_controller {
    type = "ECS"
  }

  deployment_configuration {
    maximum_percent         = 100  # 同时运行的任务数不超过期望数量的100%
    minimum_healthy_percent = 0    # 允许健康任务数降到0,确保先停旧任务再启动新任务
  }
}

2. 调整容量提供商目标容量阈值

将managed_scaling中的target_capacity从100降低,给ECS预留容量判断空间,避免触发不必要的扩容:

resource "aws_ecs_capacity_provider" "ecs_capacity_provider" {
  # ... 原有配置 ...
  auto_scaling_group_provider {
    # ... 原有配置 ...
    managed_scaling {
      maximum_scaling_step_size = 2
      minimum_scaling_step_size = 1
      status                    = "ENABLED"
      target_capacity           = 80  # 修改为80,避免容量全满时触发扩容逻辑
    }
  }
}

3. 可选:取消任务的主机端口绑定(推荐)

如果不需要固定主机端口,将portMappings中的hostPort设为0,让ECS自动分配随机端口,这样同一实例可同时运行多个任务,即使采用默认部署逻辑也不会冲突:

container_definitions = jsonencode([
  {
    # ... 原有配置 ...
    portMappings = [
      {
        containerPort = 80
        hostPort      = 0  # 改为0,自动分配主机端口
        protocol      = "tcp"
      }
    ]
    # ... 原有配置 ...
  }
])

验证调整效果

应用Terraform变更后,再次更新任务定义:

  • 新任务会在同一实例上先停旧任务、再启动新任务;
  • 不会触发ASG扩容,集群始终保持1个EC2实例;
  • CloudWatch扩容告警不会被误触发。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.27 12:05:00