如何解决单实例ASG更新时先终止旧实例引发的停机问题?
问题场景
我有一个运行在单个EC2实例上的遗留应用,使用以下Auto Scaling Group(ASG)配置:
const asg = new autoscaling.AutoScalingGroup(this, `${stage}ASG`, { vpc, minCapacity: 1, maxCapacity: 1, launchTemplate, updatePolicy: autoscaling.UpdatePolicy.rollingUpdate({}), autoScalingGroupName: `${stage}ASG`, terminationPolicies: [autoscaling.TerminationPolicy.OLDEST_INSTANCE], healthCheck: autoscaling.HealthCheck.elb({ grace: cdk.Duration.minutes(30), }), });
更新启动模板时,ASG会先移除旧实例再启动新实例,导致新实例用户脚本完成前出现大量停机。希望实现ASG等待新实例用户脚本完成且负载均衡器健康检查通过后,再终止旧实例。
根据AWS官方文档说明:
实例先终止后启动:当Auto Scaling组中只有一个实例时,启动实例刷新可能会导致中断。这是因为Amazon EC2 Auto Scaling会先终止一个实例,然后再启动一个新实例。
但文档未给出该场景的缓解方案。
解决方案
要实现“先启动健康新实例,再终止旧实例”的无停机更新,需调整ASG的容量配置和更新策略,具体步骤如下:
调整ASG容量上限
将ASG的maxCapacity设置为2,minCapacity保持1。这样ASG在更新时会优先启动新实例,而非直接终止旧实例。推荐长期保持max=2,避免后续更新重复调整。修改后的CDK配置示例:
const asg = new autoscaling.AutoScalingGroup(this, `${stage}ASG`, { vpc, minCapacity: 1, maxCapacity: 2, // 调整为2 launchTemplate, updatePolicy: autoscaling.UpdatePolicy.rollingUpdate({ minInstancesInService: 1, // 确保更新期间至少1个实例在线 maxBatchSize: 1, // 每次仅更新1个实例 pauseTime: cdk.Duration.minutes(30), // 匹配健康检查宽限期 waitOnResourceSignals: true, // 等待实例初始化完成信号 }), autoScalingGroupName: `${stage}ASG`, terminationPolicies: [autoscaling.TerminationPolicy.OLDEST_INSTANCE], healthCheck: autoscaling.HealthCheck.elb({ grace: cdk.Duration.minutes(30), }), });配置滚动更新关键参数
minInstancesInService: 1:强制ASG在更新过程中始终保持至少1个健康实例在线,彻底避免停机。waitOnResourceSignals: true:让ASG等待实例的用户脚本完成后发送的成功信号,确保应用初始化完成后才继续更新流程。maxBatchSize: 1:每次只启动一个新实例,避免负载突增影响应用可用性。
在用户脚本中添加健康信号
确保EC2实例的用户数据脚本在完成所有初始化操作后,向CloudFormation发送成功信号,示例Bash脚本如下:#!/bin/bash # 执行应用初始化逻辑 ./your-app-init-script.sh # 发送初始化完成信号 /opt/aws/bin/cfn-signal -e 0 --stack ${AWS::StackName} --resource ${ASGLogicalID} --region ${AWS::Region}保留现有终止策略
当前配置的OLDEST_INSTANCE终止策略会优先终止旧实例,完全符合“先启新再删旧”的需求,无需修改。
内容的提问来源于stack exchange,提问作者benito_h
相关产品推荐
相关产品推荐

