如何让AWS::CloudFormation::Init在堆栈更新时重新执行?
解决AWS CloudFormation Init更新不生效的问题:区分初始化与更新操作
这个问题我帮不少开发者踩过坑——本质是AWS::CloudFormation::Init的默认逻辑就是仅在EC2实例首次启动时执行初始化动作,堆栈更新时除非你主动触发,否则不会自动重新应用变更。下面给你几个分场景的实用方案,从简单到进阶都有:
方案1:用ConfigSet区分初始化与更新任务
你可以在Metadata里定义两组配置集(ConfigSet),一组仅在首次运行时执行,另一组用于堆栈更新时触发,再通过实例内的标记文件判断执行逻辑。
模板配置示例
Resources: MyEC2Instance: Type: AWS::EC2::Instance Metadata: AWS::CloudFormation::Init: configSets: # 首次启动执行的配置序列 initial: - initial_config - mark_initial_done # 堆栈更新时执行的配置序列 update: - update_config initial_config: files: /etc/initial_config.conf: content: "首次初始化配置" mode: '0644' commands: 01_first_run_only: command: echo "首次执行日志" >> /var/log/initial.log mark_initial_done: commands: 01_create_marker: command: touch /var/lib/cloud/instance/initial_run_completed update_config: files: /etc/updated_config.conf: content: "更新后的配置内容" mode: '0644' commands: 01_run_on_update: command: echo "堆栈更新执行日志:$(date)" >> /var/log/update.log Properties: UserData: Fn::Base64: !Sub | #!/bin/bash INIT_MARKER="/var/lib/cloud/instance/initial_run_completed" if [ -f $INIT_MARKER ]; then # 已完成首次初始化,执行更新配置 /opt/aws/bin/cfn-init -v --stack ${AWS::StackName} --resource MyEC2Instance --region ${AWS::Region} --configset update else # 首次启动,执行初始化配置 /opt/aws/bin/cfn-init -v --stack ${AWS::StackName} --resource MyEC2Instance --region ${AWS::Region} --configset initial fi # 向CloudFormation发送执行完成信号 /opt/aws/bin/cfn-signal -e $? --stack ${AWS::StackName} --resource MyEC2Instance --region ${AWS::Region}
工作逻辑
- 首次启动:执行
initial配置集,生成标记文件; - 堆栈更新时:只要触发实例重新运行UserData(比如修改UserData内容、重启实例),就会检测到标记文件,执行
update配置集。
方案2:用cfn-hup自动检测配置变更
cfn-hup是CloudFormation官方提供的守护进程,会定期检测实例元数据的变化,自动触发cfn-init更新配置,无需手动重启实例。
配置步骤
- 在UserData中安装并配置
cfn-hup:
#!/bin/bash # 安装cfn-bootstrap工具 yum install -y aws-cfn-bootstrap # 配置cfn-hup的堆栈信息 cat > /etc/cfn/cfn-hup.conf <<EOF [main] stack=${AWS::StackName} region=${AWS::Region} EOF # 设置钩子:当Init配置变更时,自动执行更新配置集 cat > /etc/cfn/hooks.d/cfn-auto-reloader.conf <<EOF [cfn-auto-reloader-hook] triggers=post.update path=Resources.MyEC2Instance.Metadata.AWS::CloudFormation::Init action=/opt/aws/bin/cfn-init -v --stack ${AWS::StackName} --resource MyEC2Instance --region ${AWS::Region} --configset update runas=root EOF # 启动并开机自启cfn-hup service cfn-hup start chkconfig cfn-hup on
- 对应模板的
Metadata里,保留initial和update配置集(同方案1)。
优势
- 完全自动化:修改Init配置后,
cfn-hup会在几分钟内自动检测到变更并执行更新; - 无需重启实例:在现有实例上直接应用配置,适合不能中断服务的场景。
方案3:Auto Scaling组滚动更新(适合集群场景)
如果你的EC2实例属于Auto Scaling Group(ASG),可以通过UpdatePolicy让CloudFormation自动滚动替换实例,新实例会应用最新的Init配置。
模板示例
MyASG: Type: AWS::AutoScaling::AutoScalingGroup Properties: LaunchConfigurationName: !Ref MyLaunchConfig MinSize: 2 MaxSize: 3 UpdatePolicy: AutoScalingRollingUpdate: MinInstancesInService: 1 # 滚动更新时保留的最小可用实例数 MaxBatchSize: 1 # 每次替换的实例数 PauseTime: PT5M # 等待实例初始化的时间 WaitOnResourceSignals: true # 等待实例发送cfn-signal信号
适用场景
- 允许短暂替换实例的集群场景;
- 适合配置变更需要完全重建实例生效的情况(比如依赖系统级初始化的配置)。
方案4:自定义Lambda+SSM RunCommand(复杂场景)
对于需要精确控制更新流程的场景,你可以:
- 在堆栈更新时触发Lambda函数;
- Lambda通过SSM RunCommand在目标EC2实例上执行
cfn-init --update; - 等待命令执行完成后,向CloudFormation发送信号继续堆栈更新。
这种方式适合跨资源协调、需要批量更新多台实例的复杂场景。
内容的提问来源于stack exchange,提问作者DenCowboy
相关产品推荐
相关产品推荐

