CloudFormation AutoScalingGroup 不等待更新/扩展的信号
Posted
技术标签:
【中文标题】CloudFormation AutoScalingGroup 不等待更新/扩展的信号【英文标题】:CloudFormation AutoScalingGroup not waiting for signal on update/scale-up 【发布时间】:2017-06-20 10:30:57 【问题描述】:我正在使用一个 CloudFormation 模板,该模板会根据我的请求调出尽可能多的实例,并希望在堆栈创建/更新被视为完成之前等待它们完成初始化(通过用户数据)。
期待
创建或更新堆栈应等待来自所有新创建实例的信号,以确保它们的初始化已完成。
如果任何创建的实例未能初始化,我不希望堆栈创建或更新被视为成功。
现实
CloudFormation 似乎只在首次创建堆栈时等待来自实例的信号。更新堆栈和增加实例的数量似乎忽略了信号。更新操作很快就成功完成,而实例仍在初始化中。
由于更新堆栈而创建的实例可能无法初始化,但更新操作已被视为成功。
问题
使用 CloudFormation,我怎样才能让现实达到预期?
我希望在创建堆栈时应用相同的行为,以在更新堆栈时应用。
类似问题
我只找到了与我的问题相匹配的以下问题:UpdatePolicy in Autoscaling group not working correctly for AWS CloudFormation update
已经开了一年了,还没有收到回复。
我正在创建另一个问题,因为我有更多信息要添加,我不确定这些细节是否与该问题中作者的匹配。
复制
为了演示这个问题,我根据Auto Scaling Group header on this AWS documentation page 下面的示例创建了一个模板,其中包括信号。
创建的模板已被改编为:
它使用 Ubuntu AMI(区域ap-northeast-1
)。考虑到这一变化,cfn-signal
命令已被引导并在必要时调用。
一个新参数指定在 Auto Scaling 组中启动多少个实例。
在发出信号之前添加了 2 分钟的睡眠时间,以模拟初始化时花费的时间。
这是模板,保存到template.yml
:
Parameters:
DesiredCapacity:
Type: Number
Description: How many instances would you like in the Auto Scaling Group?
Resources:
AutoScalingGroup:
Type: AWS::AutoScaling::AutoScalingGroup
Properties:
AvailabilityZones: !GetAZs ''
LaunchConfigurationName: !Ref LaunchConfig
MinSize: !Ref DesiredCapacity
MaxSize: !Ref DesiredCapacity
CreationPolicy:
ResourceSignal:
Count: !Ref DesiredCapacity
Timeout: PT5M
UpdatePolicy:
AutoScalingScheduledAction:
IgnoreUnmodifiedGroupSizeProperties: true
AutoScalingRollingUpdate:
MinInstancesInService: 1
MaxBatchSize: 2
PauseTime: PT5M
WaitOnResourceSignals: true
LaunchConfig:
Type: AWS::AutoScaling::LaunchConfiguration
Properties:
ImageId: ami-b7d829d6
InstanceType: t2.micro
UserData:
'Fn::Base64':
!Sub |
#!/bin/bash -xe
sleep 120
apt-get -y install python-setuptools
TMP=`mktemp -d`
curl https://s3.amazonaws.com/cloudformation-examples/aws-cfn-bootstrap-latest.tar.gz | \
tar xz -C $TMP --strip-components 1
easy_install $TMP
/usr/local/bin/cfn-signal -e $? \
--stack $AWS::StackName \
--resource AutoScalingGroup \
--region $AWS::Region
现在我使用单个实例创建堆栈,通过:
$ aws cloudformation create-stack \
--region=ap-northeast-1 \
--stack-name=asg-test \
--template-body=file://template.yml \
--parameters ParameterKey=DesiredCapacity,ParameterValue=1
等待几分钟创建完成后,我们来看看一些关键堆栈事件:
$ aws cloudformation describe-stack-events \
--region=ap-northeast-1 \
--stack-name=asg-test
...
"Timestamp": "2017-02-03T05:36:45.445Z",
...
"LogicalResourceId": "AutoScalingGroup",
...
"ResourceStatus": "CREATE_COMPLETE",
...
,
"Timestamp": "2017-02-03T05:36:42.487Z",
...
"LogicalResourceId": "AutoScalingGroup",
...
"ResourceStatusReason": "Received SUCCESS signal with UniqueId ...",
"ResourceStatus": "CREATE_IN_PROGRESS"
,
"Timestamp": "2017-02-03T05:33:33.274Z",
...
"LogicalResourceId": "AutoScalingGroup",
...
"ResourceStatusReason": "Resource creation Initiated",
"ResourceStatus": "CREATE_IN_PROGRESS",
...
...
您可以看到 Auto Scaling 组在 05:33:33 开始启动。 05:36:42(启动后 3 分钟),它收到了成功信号。这使得 Auto Scaling 组仅在片刻之后,即 05:36:45 达到自己的成功状态。
太棒了 - 像魅力一样工作。
现在让我们尝试通过更新堆栈将这个 Auto Scaling 组中的实例数增加到 2:
$ aws cloudformation update-stack \
--region=ap-northeast-1 \
--stack-name=asg-test \
--template-body=file://template.yml \
--parameters ParameterKey=DesiredCapacity,ParameterValue=2
在等待更新完成的时间短得多之后,让我们看看一些新的堆栈事件:
$ aws cloudformation describe-stack-events \
--region=ap-northeast-1 \
--stack-name=asg-test
"ResourceStatus": "UPDATE_COMPLETE",
...
"ResourceType": "AWS::CloudFormation::Stack",
...
"Timestamp": "2017-02-03T05:45:47.063Z"
,
...
"ResourceStatus": "UPDATE_COMPLETE",
...
"LogicalResourceId": "AutoScalingGroup",
"Timestamp": "2017-02-03T05:45:43.047Z"
,
"ResourceStatus": "UPDATE_IN_PROGRESS",
...,
"LogicalResourceId": "AutoScalingGroup",
"Timestamp": "2017-02-03T05:44:20.845Z"
,
"ResourceStatus": "UPDATE_IN_PROGRESS",
...
"ResourceType": "AWS::CloudFormation::Stack",
...
"Timestamp": "2017-02-03T05:44:15.671Z",
"ResourceStatusReason": "User Initiated"
,
....
现在您可以看到,虽然 Auto Scaling 组在 05:44:20 开始更新,但它在 05:45:43 完成 - 完成时间不到一分半钟,考虑到用户数据中的休眠时间为 120 秒。
然后堆栈更新继续完成,而 Auto Scaling 组从未收到任何信号。
新实例确实存在。
在我的实际用例中,我通过 SSH 连接到其中一个新实例,发现即使在堆栈更新完成后它仍在初始化过程中。
我的尝试
我已经阅读并重新阅读了有关 CreationPolicy
和 UpdatePolicy
的文档,但未能确定我缺少什么。
查看上面使用的更新策略,我不明白它实际上在做什么。为什么WaitOnResourceSignals
是真的,但它没有等待?它是否有其他用途?
或者这些新实例不属于“滚动更新”政策?如果它们不属于那里,那么我希望它们属于创建政策,但这似乎也不适用。
因此,我真的不知道还能尝试什么。
我有一种偷偷摸摸的感觉,它按设计/预期运行,但如果是这样,那么 WaitOnResourceSignals
属性的意义何在?我如何才能满足上述预期?
【问题讨论】:
【参考方案1】:AutoScalingRollingUpdate
策略处理轮换 Auto Scaling 组中的整个实例集,以响应对基础 LaunchConfiguration
的更改。它不适用于对现有组中实例数量的个别更改。根据UpdatePolicy Attribute 文档,
仅当您执行以下一项或多项操作时,
更改 Auto Scaling 组的AutoScalingReplacingUpdate
和AutoScalingRollingUpdate
政策才适用:AWS::AutoScaling::LaunchConfiguration
。 更改 Auto Scaling 组的VPCZoneIdentifier
属性 更新包含与当前LaunchConfiguration
不匹配的实例的 Auto Scaling 组。
更改 Auto Scaling 组的 DesiredCapacity
属性不在此列表中,因此 AutoScalingRollingUpdate
策略不适用于此类更改。
据我所知,在完全预置添加到 Auto Scaling 组的任何新实例之前,不可能(使用标准 AWS CloudFormation 资源)延迟完成修改 DesiredCapacity
的堆栈更新。
这里有一些替代选项:
-
不是只修改
DesiredCapacity
,而是同时修改一个LaunchConfiguration
属性。这将触发AutoScalingRollingUpdate
到所需容量(缺点是它还会更新现有实例,实际上可能不需要修改)。
将AWS::AutoScaling::LifecycleHook
资源添加到您的Auto Scaling 组,并在cfn-signal
之外调用aws autoscaling complete-lifecycle-action
,以发出生命周期挂钩完成的信号。这不会根据需要延迟您的 CloudFormation 堆栈更新,但它将延迟各个自动缩放实例进入InService
状态,直到收到生命周期信号。 (有关详细信息,请参阅 Lifecycle Hooks 文档。)
作为 #2 的扩展,应该可以将 Lifecycle Hook 添加到 Auto Scaling 组,以及轮询 Auto Scaling 组并且仅在 Auto Scaling 组包含 @987654344 时完成的 Custom Resource @ 全部处于InService
状态的实例数。
【讨论】:
感谢您提供清晰详细的答案以及替代选项。我认为我将能够使用生命周期钩子,使未能初始化的实例被认为是不健康的,从而从组中删除。 感谢您的精彩回复。我正在努力实现:cloudFomration 告诉我我已经创建了一个循环引用,因为Hook
ref ASG
ref LaunchConfiguration
ref Hook & ASG
。你如何解决?通过在 cloudformation 之外创建一些资源? (这意味着手动创建钩子,然后通过其名称在 cloudformation 中使用它,而不是使用对其动态创建名称的引用)
@Bruno 您可以通过使用 AWS CLI:aws cloudformation describe-stack-resource --region=$AWS::Region --stack-name=$AWS::StackName --logical-resource-id=MyHookOrASG --output=text --query=StackResourceDetail.PhysicalResourceId
在您的 LaunchConfiguration
的 UserData 脚本中动态检索对 Hook
和 ASG
的引用来避免循环引用。为此,您还需要允许对与 LaunchConfiguration 关联的 IAM 实例配置文件执行 cloudformation:DescribeStackResource
操作。
是的,我做了几乎一样的事情:hookExists=$(aws autoscaling describe-lifecycle-hooks --auto-scaling-group-name $autoScalingGroupName --region=", "Ref" : "AWS::Region" , " --lifecycle-hook-name my-lifecycleHook --output=text | wc -l)
`
您能否评论一下是否可以很好地解决此问题,如果可以,请在此处发布完整的详细解决方案。谢谢。【参考方案2】:
滚动更新仅适用于现有实例。文档说:
滚动更新使您能够指定 AWS CloudFormation 是批量更新 Auto Scaling 组中的实例还是一次全部更新。
因此,要对此进行测试,请根据您的模板创建一个堆栈。而不是对启动配置进行小修改(例如,将 sleep 120 设置为 121)并更新堆栈。现在您应该会看到滚动更新。
【讨论】:
感谢您指出这一点 - 很好,它可以在更新实例的情况下工作。但是,当基于 CloudFormation 堆栈的更新创建新实例时,信号功能仍然无法正常工作。我只想在更新堆栈时应用与创建堆栈时相同的行为。你知道这是否可能吗?以上是关于CloudFormation AutoScalingGroup 不等待更新/扩展的信号的主要内容,如果未能解决你的问题,请参考以下文章