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 连接到其中一个新实例,发现即使在堆栈更新完成后它仍在初始化过程中。

我的尝试

我已经阅读并重新阅读了有关 CreationPolicyUpdatePolicy 的文档,但未能确定我缺少什么。

查看上面使用的更新策略,我不明白它实际上在做什么。为什么WaitOnResourceSignals 是真的,但它没有等待?它是否有其他用途?

或者这些新实例不属于“滚动更新”政策?如果它们不属于那里,那么我希望它们属于创建政策,但这似乎也不适用。

因此,我真的不知道还能尝试什么。

我有一种偷偷摸摸的感觉,它按设计/预期运行,但如果是这样,那么 WaitOnResourceSignals 属性的意义何在?我如何才能满足上述预期?

【问题讨论】:

【参考方案1】:

AutoScalingRollingUpdate 策略处理轮换 Auto Scaling 组中的整个实例集,以响应对基础 LaunchConfiguration 的更改。它不适用于对现有组中实例数量的个别更改。根据UpdatePolicy Attribute 文档,

仅当您执行以下一项或多项操作时,AutoScalingReplacingUpdateAutoScalingRollingUpdate 政策才适用:

更改 Auto Scaling 组的 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 脚本中动态检索对 HookASG 的引用来避免循环引用。为此,您还需要允许对与 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 不等待更新/扩展的信号的主要内容,如果未能解决你的问题,请参考以下文章

VPC 内部的 CloudFormation?

扩大 Cloudformation 模板

Cloudformation + OpsWorks

SAM 模板和 Cloudformation 模板的区别

CloudFormation 嵌套堆栈名称

CloudFormation 的超时配置