如何让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,根据集群任务负载动态调整实例数量,包括自动终止无任务的空闲实例:
添加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 } }调整ASG配置
- 移除
ignore_desired_capacity_changes = true,或设为false,允许容量提供者修改ASG的期望容量 - 保留
min_size = 0,确保可以缩容至无实例状态
- 移除
方法2:ASG生命周期钩子+Lambda自定义逻辑
如果需要更灵活的控制逻辑,可通过生命周期钩子触发Lambda函数,检查实例的ECS任务状态后决定是否终止:
编写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" )添加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 }配置IAM权限
确保Lambda拥有ECS容器实例查询、ASG生命周期动作完成的权限;ASG拥有触发Lambda的权限。
关键注意事项
- 使用容量提供者时,需确保ECS集群与ASG在同一区域,且实例已正确注册到ECS集群
- 生命周期钩子方法需处理实例刚启动、尚未注册到ECS的情况,避免误终止
- 两种方法均要求ASG的
min_size设为0,才能实现完全缩容
内容的提问来源于stack exchange,提问作者october03
相关产品推荐
相关产品推荐

