如何通过CloudFormation更新关联LaunchTemplate的AutoScaling组
1. 需求合理性与CloudFormation正确实现方式
你的更新需求完全合理,之前改UserData不生效、改LaunchTemplate名触发回滚都是配置存在疏漏,根本不需要单独找什么AWS::EC2::LaunchTemplateVersion资源——你在CloudFormation里修改AWS::EC2::LaunchTemplate下的LaunchTemplateData内容时,CloudFormation会自动创建新的LaunchTemplate版本,不用手动调CLI创建。
之前改完UserData实例没更新,核心是两个配置错误:
- 你给ASG配的
UpdatePolicy只开了AutoScalingReplacingUpdate.WillReplace=true,这个配置只有在整个ASG资源被替换重建的时候才会触发新实例创建。你只是改了关联的LaunchTemplate内容,ASG资源本身没被替换,哪怕引用的版本号变了,也不会自动触发实例滚动替换,跑的自然还是几个月前的旧实例。 - 你配的cfn-hup监听路径写错了,运行中的实例根本收不到配置更新的触发信号。
要实现正常更新只需要做两个调整:
第一,给ASG补全滚动更新策略,把原来只配ReplacingUpdate的UpdatePolicy块替换成下面的内容,参数可以根据你的环境调整:
UpdatePolicy: AutoScalingRollingUpdate: MinInstancesInService: 0 # 生产环境建议至少设1,保证更新时服务不中断 MaxBatchSize: 1 # 每次替换的实例数量,集群规模大可以适当调大 MinSuccessfulInstancesPercent: 100 PauseTime: PT15M WaitOnResourceSignals: true AutoScalingReplacingUpdate: WillReplace: true
第二,修正cfn-hup的配置错误,你现在/etc/cfn/hooks.d/cfn-auto-reloader.conf里写的监听路径是Resources.EC2.Metadata.AWS::CloudFormation::Init,但你模板里LaunchTemplate的逻辑ID是ec2LaunchTemplate,正确路径应该是Resources.ec2LaunchTemplate.Metadata.AWS::CloudFormation::Init。路径写错的话cfn-hup根本监听不到元数据变化,不会自动执行cfn-init拉取新配置。
至于你之前改LaunchTemplate名触发全量回滚,本质是替换整个LaunchTemplate和ASG的时候,新实例的cfn-signal没正常返回给CloudFormation——要么是UserData里的命令执行报错,要么是nginx、Nitro Enclave服务启动失败返回了非0状态码,你又配了100%实例成功才算完成,单台启动失败自然就触发回滚了。可以先在测试环境把cfn-init、cfn-signal的日志开全,排查下实例启动阶段的报错即可。
2. 替代方案
如果不想靠ASG自带的滚动更新策略替换实例,也可以选几个更可控的方案:
- 直接用ASG自带的实例刷新功能,配好最小健康实例占比、分批大小,等LaunchTemplate新版本创建完后,手动或者靠EventBridge事件触发自动刷新,按批次替换旧实例。
- 写简单的Lambda函数配合CloudFormation自定义资源,每次栈更新完自动调用ASG的实例刷新API,不用改ASG的UpdatePolicy配置,更新逻辑更灵活。
- 如果对更新时效要求不高,不用特意做替换操作,配好ASG的实例终止策略,等实例运行到设定时长、或者后续扩缩容的时候,新启动的实例自然会用最新的LaunchTemplate版本,旧实例会被逐步淘汰。
3. 机制说明
你之前对LaunchTemplate的更新机制理解有偏差:CloudFormation托管AWS::EC2::LaunchTemplate时,所有对LaunchTemplateData的修改都会自动创建新版本,!GetAtt ec2LaunchTemplate.LatestVersionNumber会自动返回最新的版本号,不需要单独维护版本资源。
整套更新的逻辑可以按这几个核心规则梳理,不用找零散的命令文档:
- LaunchTemplate修改完新版本,ASG不会自动替换存量实例,必须显式配置滚动更新策略、或者手动触发实例刷新,新启动的实例才会用新版本的配置。
- UserData脚本只会在实例首次启动的时候执行一次,运行中的实例不会重新跑UserData。要给运行中的实例推配置变更,要么替换实例,要么靠cfn-hup触发cfn-init拉取最新的元数据配置。
- 配置滚动更新的时候,PauseTime一定要留够,要覆盖实例启动、服务初始化、发送成功信号的全流程时长,不然CloudFormation等超时就会判定更新失败,直接触发回滚。
内容的提问来源于stack exchange,提问作者mark

