EC2实例终止前如何优雅关闭ECS服务?
解决ECS EC2实例终止前优雅关闭服务的方案
1. 给Auto Scaling Group配置生命周期钩子(核心)
ASG默认滚动更新会直接终止实例,必须通过生命周期钩子在终止前注入ECS实例注销与任务排空逻辑:
- 在CDK中为ASG添加
INSTANCE_TERMINATING类型的生命周期钩子,绑定Lambda函数处理注销流程 - Lambda核心逻辑:
- 调用
ecs.deregisterContainerInstance并设置force=false,触发ECS任务排空 - 轮询等待ECS实例状态变为
DRAINING,直到所有任务完成或被重新调度 - 调用
autoscaling:CompleteLifecycleAction,允许实例继续终止
- 调用
- 设置足够的钩子超时时间(如30分钟),避免任务排空超时导致ASG更新失败
CDK代码示例:
// 假设已定义ASG和ECS集群实例 const lifecycleHook = new autoscaling.LifecycleHook(this, 'EcsTerminationHook', { autoScalingGroup: asg, lifecycleTransition: autoscaling.LifecycleTransition.INSTANCE_TERMINATING, heartbeatTimeout: Duration.minutes(30), defaultResult: autoscaling.DefaultResult.ABANDON, // 超时则放弃终止,避免实例僵死 }); // 处理注销逻辑的Lambda const drainHandler = new lambda.Function(this, 'EcsDrainHandler', { runtime: lambda.Runtime.PYTHON_3_11, code: lambda.Code.fromAsset('lambda'), handler: 'drain.handler', environment: { CLUSTER_NAME: cluster.clusterName, }, }); // 给Lambda添加必要权限 drainHandler.addToRolePolicy(new iam.PolicyStatement({ actions: ['ecs:DeregisterContainerInstance', 'ecs:DescribeContainerInstances'], resources: [cluster.clusterArn], })); drainHandler.addToRolePolicy(new iam.PolicyStatement({ actions: ['autoscaling:CompleteLifecycleAction'], resources: [asg.autoScalingGroupArn], })); // 绑定钩子与Lambda lifecycleHook.addTarget(new autoscalingLifecycleHookTargets.LambdaHook(drainHandler));
2. 配置ECS服务与负载均衡的优雅排空参数
- ECS服务层面:设置容器停止超时时间,确保请求处理完成后再退出;同时调整部署配置避免服务中断:
const taskDef = new ecs.Ec2TaskDefinition(this, 'AppTaskDef', { containers: { app: { image: ecs.ContainerImage.fromRegistry('your-app-image'), stopTimeout: Duration.seconds(60), // 容器优雅关闭超时 }, }, }); const service = new ecs.Ec2Service(this, 'AppService', { cluster: cluster, taskDefinition: taskDef, desiredCount: 2, deploymentConfiguration: { maximumPercent: 200, minimumHealthyPercent: 100, }, });
- 负载均衡目标组层面:调整注销延迟时间,给正在处理的请求留足完成时间:
const targetGroup = new elbv2.ApplicationTargetGroup(this, 'AppTargetGroup', { port: 80, targetType: elbv2.TargetType.INSTANCE, deregistrationDelay: Duration.seconds(300), // 注销后继续接受请求的时长 healthCheck: { interval: Duration.seconds(10), timeout: Duration.seconds(5), }, });
3. 优化ASG滚动更新配置
调整ASG滚动更新参数,确保新实例完全注册到ECS并通过健康检查后,再终止旧实例:
const asg = new autoscaling.AutoScalingGroup(this, 'EcsAsg', { // 其他基础配置(启动模板、实例类型等) updatePolicy: autoscaling.UpdatePolicy.rollingUpdate({ minInstancesInService: 2, // 与服务最小运行实例数匹配 maxBatchSize: 1, pauseTime: Duration.minutes(10), // 延长暂停时间,确保新实例完成ECS注册 waitOnResourceSignals: true, // 可选:等待实例发送就绪信号(需在启动脚本中添加cfn-signal) }), });
额外注意事项
- 确保使用的ECS优化AMI包含最新版本的ecs-agent,旧版本可能存在注销逻辑bug
- 手动触发ASG实例终止,验证生命周期钩子是否正常触发ECS排空流程
- 监控ECS实例的
DRAINING状态和ASG生命周期动作,排查超时或失败情况
内容的提问来源于stack exchange,提问作者nicou
相关产品推荐
相关产品推荐

