使用 AWS CloudFormation 添加环境变量会重置 AWS Beanstalk 应用程序

Posted

技术标签:

【中文标题】使用 AWS CloudFormation 添加环境变量会重置 AWS Beanstalk 应用程序【英文标题】:Adding environment variable with AWS CloudFormation resets AWS Beanstalk app 【发布时间】:2019-05-09 05:45:35 【问题描述】:

我有一个使用以下 CloudFormation 模板创建的 AWS Beanstalk 应用程序和环境:

MyApp:
    Type: 'AWS::ElasticBeanstalk::Application'
    Properties:
        ApplicationName: MyAppName

InitialApplication:
    Type: 'AWS::ElasticBeanstalk::ApplicationVersion'
    Properties:
        ApplicationName: !Ref MyApp
        Description: Version 1.0
        SourceBundle:
            S3Bucket: !Sub 'elasticbeanstalk-samples-$AWS::Region'
            S3Key: ecs-sample.zip

AppEnvironment:
    Type: 'AWS::ElasticBeanstalk::Environment'
    Properties:
        ApplicationName: !Ref MyApp
        Description: staging
        TemplateName: !Ref AppConfigurationTemplate
        VersionLabel: !Ref InitialApplication

AppConfigurationTemplate:
    Type: 'AWS::ElasticBeanstalk::ConfigurationTemplate'
    DependsOn:
        - MySecurityGroup
    Properties:
        ApplicationName: !Ref MyApp
        Description: My Configuration Template
        SolutionStackName: '64bit Amazon Linux 2018.03 v2.11.2 running Multi-container Docker 18.03.1-ce (Generic)'
        OptionSettings:
            # Lots of options here

            # Application environment variables
            - Namespace: aws:elasticbeanstalk:application:environment
              OptionName: MY_APP_OPTION
              Value: SOME_VALUE

问题是,如果我将环境变量添加到我的应用程序(即在aws:elasticbeanstalk:application:environment 命名空间中),Beanstalk 会将环境的应用程序版本重置为初始应用程序。因此,假设我在一年前创建了 CloudFormation 堆栈,并且已经部署了我的应用程序的 50 个版本……如果我随后通过 CloudFormation 添加环境变量,则环境的应用程序将重置为示例应用程序。这当然会破坏一切,因为数据库发生了变化等。我想要的当然是添加/修改环境变量而不更改 Beanstalk 环境中部署的应用程序版本。

为什么 Beanstalk 在进行此更改时会重置我的应用程序,有什么方法可以阻止它?

提前致谢!

【问题讨论】:

您进行了哪些更改?这很可能看起来像部署 Cloudformation 堆栈的经典案例,然后通过控制台/cli 等而不是在 cloudformation 内部修改相关资源。如果您尚未更新 Cloudformation 堆栈,那么这是预期的行为,Cloudformation 将看到比模板更新的版本作为漂移,并将尝试将其重置为正常,这是原始模板。我对 beanstalk 了解不多,但您可能想查看 InitialApplication 部分以确保它正在部署最新版本。 是的,我已经在 EB 控制台中部署了新的应用程序版本,还添加了环境变量。但在我这样做之前(在 CF 模板和 EB 环境之间存在偏差之前),在 CF 模板中添加一个新的环境变量也会回滚应用程序版本。也许是因为我部署了一个新应用程序。因此,CF 模板似乎需要始终与最新的应用程序版本保持同步。我希望只指定初始版本,因为这将允许我通过 EB 控制台进行部署。 是的,不断更新 cloudformation 是最佳实践,当然也值得推荐。如果你不这样做,那么你最终会陷入像你现在正在经历的尴尬场景中,这很不幸,但可以解决。您需要将 CF 模板中的捆绑源更新为最新版本,以便部署,它可能需要重建基础设施,因为这是一个不知道已经发生的重大变化。 是的,我只是不认为我必须通过 CF 部署;我认为应用程序版本只是最初的版本。我将不得不修补东西并注意不要破坏任何东西。非常感谢您的意见! 没问题,我不知道您是否希望我发布答案,以便您接受关闭线程或接受其他人的答案,请告诉我。 【参考方案1】:

不断更新 cloudformation 是最佳做法,当然也值得推荐。如果你不这样做,那么你最终会陷入像你现在正在经历的尴尬场景中,这很不幸,但可以解决。您需要将 CF 模板中的捆绑源更新为最新版本,以便部署,它可能需要重建基础设施,因为这是一个不知道发生的重大变化

【讨论】:

【参考方案2】:

这里要理解的一个重要概念是,如果您可以通过 EB 控制台(如您所说的那样)或 EB API 初始化并上传您的应用程序的新版本,那么为什么要实际使用 CloudFormation 来部署您的应用程序。 CloudFormation 是一种适用于您的应用程序的“配方”,它的设计目标是keep the infrastructure definition under source control。这就是您在仅更新“配方”中的环境变量部分时观察到回滚的原因 - CloudFormation 中所做的更改触发了应用程序中的更新,并且应用了模板中的定义 - 旧应用程序版本很难-编码。这就是为什么理想情况下你的CloudFormation template should be parametrized - 每次你想在你的应用中进行更新时,你应该将最新的版本名称传递给模板。

当然,您可以在其控制台中手动更新 EB 版本,但在这种情况下,使用 CloudFormation 的整个想法变得毫无意义,并且会导致您观察到的复杂情况。

正如您在评论中指定的那样,您可以将新的应用程序版本上传到 S3,然后通过将版本作为参数传递的 API 更新 CloudFormation。这样更新可以自动化。在堆栈中进行一些更新的示例 API 调用(AWS docs 中的解释):

aws cloudformation update-stack --stack-name mystack --template-url https://s3.amazonaws.com/sample/updated.template
--parameters ParameterKey=VPCID,ParameterValue=SampleVPCID ParameterKey=SubnetIDs,ParameterValue=SampleSubnetID1\\,UpdatedSampleSubnetID2

在您的情况下,要更新的参数将是 AWS::ElasticBeanstalk::ApplicationVersion,尤其是 SourceBundle。

旧答案(在 EB 控制台中更新应用程序,不涉及 CloudFormation)

我了解您不使用 Cloud Formation 模板部署应用程序(因为它已经有一年没有更新了),因此您必须使用 eb deploy 或在 Elastic Beanstalk 控制台中以编程方式进行。那为什么不在控制台更新环境变量呢?

您可以通过转到应用程序的控制台,点击左侧菜单中的“配置”,然后点击“软件”部分中的“修改”按钮来完成此操作。在底部,您可以添加在每次新部署后将存在于您的应用中的环境变量。

【讨论】:

由于CloudFormation重置应用程序版本的问题,这实际上是我一直在做的事情。为了解决这个问题,我在控制台中添加了环境变量。问题在于,这会导致实际配置偏离 CF 模板。因此,如果我要创建一个新的 CF 堆栈,事情将无法正常工作,因为我的应用程序期望的环境变量不存在于 CF 模板中。所以我需要这些由 CF 模板控制,而不需要为每次部署重置应用程序。 好的,看来您需要将应用程序版本作为参数传递给 CloudFormation,因为您的 Elastic Beanstalk 应用程序依赖此模板。看看this answer。 谢谢,这看起来很有趣。如果我正确理解答案,我实际上会通过 CloudFormation 部署新的应用程序版本。目前,我压缩了一个 zip 存档并将其上传到 Beanstalk 控制台中。这种新方法需要更改我的部署脚本。所以将新版本上传到S3(例如my-app-526),然后通过API进行堆栈更新并将my-app-526作为版本传递,然后用于初始应用程序。如果我没记错的话,那应该部署新应用程序? 是的,你没看错。我用更广泛的解释和一些链接更新了我的答案。 太棒了,这很有意义。我可能会通过 API 进行更改集,以便在应用更改之前查看更改。所以部署将在我执行更改集时发生。但这本质上是一样的,只是增加了一个手动步骤。 :-) 非常感谢您的帮助!【参考方案3】:

您需要通过 ChangeSet 执行环境变量更改,以便只执行环境变量更改,并使您的堆栈保持原样。

【讨论】:

这有什么不同吗?如果我进行堆栈更新,它会在提交更新之前向我显示更改集。如果我创建一个更改集,它会显示相同的更改,只是我可以创建更改集并稍后执行它。但最终结果不一样吗?这两种方法都表明配置模板将被替换,并且环境将被修改。由于某种原因,这会导致应用程序版本被重置。但是,我说这两种方法具有相同的最终结果是不是有什么误解?

以上是关于使用 AWS CloudFormation 添加环境变量会重置 AWS Beanstalk 应用程序的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Cloudformation 模板/CDK 中添加 AWS IoT 配置模板

将参数添加到 AWS CloudFormation / CodePipeline 堆栈

我无法在我的 cloudformation 代码中添加“AWS”:“*”

AWS Inspector 的 AWS CloudFormation 模板能否添加 SNS 主题

Cloudformation + OpsWorks

通过 CloudFormation 为现有表添加自动缩放到 AWS DynamoDB