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

如何让ECS EC2自动扩展组自动停止无运行任务的实例?

实现ECS EC2自动扩展组自动终止无运行任务的实例

现有配置

用户当前的Terraform ASG配置如下:

module "autoscaling" {
  source  = "terraform-aws-modules/autoscaling/aws"
  version = "~> 6.5"

  name = "${local.name}-asg"

  security_groups = [module.autoscaling_sg.security_group_id]
  ignore_desired_capacity_changes = true

  image_id = jsondecode(data.aws_ssm_parameter.ecs_optimized_ami.value)["image_id"]
  instance_type = "t4g.small"
  key_name = "dpgg-match"

  user_data = base64encode(local.user_data)
  
  network_interfaces = [
    {
      associate_public_ip_address = true
      delete_on_termination       = true
    }
  ]

  block_device_mappings = [
    {
      device_name = "/dev/xvda"
      ebs = {
        volume_size = 40
        volume_type = "gp2"
      }
    }
  ]

  create_iam_instance_profile = true
  iam_role_name               = local.name
  iam_role_description        = "ECS role for ${local.name}"
  iam_role_policies = {
    AmazonEC2ContainerServiceforEC2Role = "arn:aws:iam::aws:policy/service-role/AmazonEC2ContainerServiceforEC2Role"
    AmazonSSMManagedInstanceCore        = "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore"
  }

  vpc_zone_identifier = module.vpc.public_subnets
  health_check_type   = "EC2"
  min_size            = 0
  max_size            = 3
  desired_capacity    = 1

  termination_policies = ["OldestInstance"]

  autoscaling_group_tags = {
    ECS = "True"
    Terraform = "True"
  }

  tags = local.tags
}

问题分析

仅修改termination_policies为OldestInstance无法实现自动终止无任务实例,因为ASG本身无法感知ECS实例上的任务运行状态,需要结合ECS原生的容量管理机制,或自定义逻辑来实现。

解决方案

方法1:使用ECS容量提供者(推荐)

ECS容量提供者可自动关联ASG,根据集群任务负载动态调整实例数量,包括自动终止无任务的空闲实例:

  1. 添加ECS容量提供者配置
    在Terraform中新增以下资源:

    resource "aws_ecs_capacity_provider" "ecs_cap_provider" {
      name = "${local.name}-capacity-provider"
    
      auto_scaling_group_provider {
        auto_scaling_group_arn = module.autoscaling.autoscaling_group_arn
    
        managed_scaling {
          status                     = "ENABLED"
          target_capacity            = 100
          minimum_scaling_step_size  = 1
          maximum_scaling_step_size  = 3
        }
    
        managed_termination_protection = "DISABLED"
      }
    }
    
    # 将容量提供者绑定到ECS集群
    resource "aws_ecs_cluster_capacity_providers" "ecs_cluster_cap_providers" {
      cluster_name = aws_ecs_cluster.your_cluster.name # 替换为你的ECS集群名称
      capacity_providers = [aws_ecs_capacity_provider.ecs_cap_provider.name]
      default_capacity_provider_strategy {
        capacity_provider = aws_ecs_capacity_provider.ecs_cap_provider.name
        weight            = 1
        base              = 0
      }
    }
    
  2. 调整ASG配置

    • 移除ignore_desired_capacity_changes = true,或设为false,允许容量提供者修改ASG的期望容量
    • 保留min_size = 0,确保可以缩容至无实例状态

方法2:ASG生命周期钩子+Lambda自定义逻辑

如果需要更灵活的控制逻辑,可通过生命周期钩子触发Lambda函数,检查实例的ECS任务状态后决定是否终止:

  1. 编写Lambda检查函数

    import boto3
    
    ecs = boto3.client('ecs')
    autoscaling = boto3.client('autoscaling')
    
    def lambda_handler(event, context):
        instance_id = event['detail']['EC2InstanceId']
        cluster_name = "your-ecs-cluster-name" # 替换为你的ECS集群名称
    
        # 获取实例对应的ECS容器实例ARN
        response = ecs.list_container_instances(cluster=cluster_name, filter=f"ec2InstanceId == {instance_id}")
        if not response['containerInstanceArns']:
            # 实例未注册到ECS,允许终止
            autoscaling.complete_lifecycle_action(
                LifecycleHookName=event['detail']['LifecycleHookName'],
                AutoScalingGroupName=event['detail']['AutoScalingGroupName'],
                LifecycleActionToken=event['detail']['LifecycleActionToken'],
                LifecycleActionResult="CONTINUE"
            )
            return
    
        # 查询容器实例的运行任务数
        container_instance_arn = response['containerInstanceArns'][0]
        instance_details = ecs.describe_container_instances(cluster=cluster_name, containerInstances=[container_instance_arn])
        running_tasks_count = instance_details['containerInstances'][0]['runningTasksCount']
    
        if running_tasks_count == 0:
            # 无运行任务,允许终止
            autoscaling.complete_lifecycle_action(
                LifecycleHookName=event['detail']['LifecycleHookName'],
                AutoScalingGroupName=event['detail']['AutoScalingGroupName'],
                LifecycleActionToken=event['detail']['LifecycleActionToken'],
                LifecycleActionResult="CONTINUE"
            )
        else:
            # 有运行任务,取消终止
            autoscaling.complete_lifecycle_action(
                LifecycleHookName=event['detail']['LifecycleHookName'],
                AutoScalingGroupName=event['detail']['AutoScalingGroupName'],
                LifecycleActionToken=event['detail']['LifecycleActionToken'],
                LifecycleActionResult="ABANDON"
            )
    
  2. 添加ASG生命周期钩子

    resource "aws_autoscaling_lifecycle_hook" "ecs_instance_termination_hook" {
      name                   = "${local.name}-termination-hook"
      autoscaling_group_name = module.autoscaling.autoscaling_group_name
      lifecycle_transition   = "autoscaling:EC2_INSTANCE_TERMINATING"
      heartbeat_timeout      = 300
      default_result         = "ABANDON"
    
      notification_target_arn = aws_lambda_function.ecs_task_check_lambda.arn # 替换为你的Lambda ARN
      role_arn                = aws_iam_role.lifecycle_hook_role.arn # 授予ASG触发Lambda的权限角色ARN
    }
    
  3. 配置IAM权限
    确保Lambda拥有ECS容器实例查询、ASG生命周期动作完成的权限;ASG拥有触发Lambda的权限。

关键注意事项

  • 使用容量提供者时,需确保ECS集群与ASG在同一区域,且实例已正确注册到ECS集群
  • 生命周期钩子方法需处理实例刚启动、尚未注册到ECS的情况,避免误终止
  • 两种方法均要求ASG的min_size设为0,才能实现完全缩容

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.30 07:25:37