使用 ebextensions 动态设置每个环境的 EC2 实例类型

Posted

技术标签:

【中文标题】使用 ebextensions 动态设置每个环境的 EC2 实例类型【英文标题】:Dynamically set the EC2 Instance Type per Environment using ebextensions 【发布时间】:2020-09-14 17:18:44 【问题描述】:

我想在所有环境中创建 EC2 实例类型 t3.medium,在生产环境中创建 m5.large

我像这样使用.ebextensions (YAML):

选项 1:

Mappings:
  EnvironmentMap:
    "production":
      TheType: "m5.large"
      SecurityGroup: "foo"
      ...
    "staging":
      TheType: "t3.medium"
      SecurityGroup: "bar"
      ...

option_settings:
  aws:autoscaling:launchconfiguration:
    IamInstanceProfile: "aws-elasticbeanstalk-ec2-role"
    InstanceType: !FindInMap
      - EnvironmentMap
      - !Ref 'AWSEBEnvironmentName'
      - TheType
    SecurityGroups:
      - "Fn::FindInMap": ["EnvironmentMap", "Ref": "AWSEBEnvironmentName", "SecurityGroup"]

选项 2:

    InstanceType: "Fn::FindInMap": ["EnvironmentMap", "Ref": "AWSEBEnvironmentName", "EC2InstanceType"]

选项 3:

    InstanceType:
      - "Fn::FindInMap": ["EnvironmentMap", "Ref": "AWSEBEnvironmentName", "EC2InstanceType"]

结果

选项 1 因 Yaml 无效而失败(但我从这个 AWS example 获取了这个。

选项 2 和 3 因同样的问题而失败。 FindInMap 函数未被“调用”: Invalid option value: '"Fn::FindInMap":["EnvironmentMap","EC2InstanceType"],"Ref":"AWSEBEnvironmentName"' (Namespace: 'aws:autoscaling:launchconfiguration', OptionName: 'InstanceType'): Value is not one of the allowed values: [c1.medium, c1.xlarge, c3.2xlarge, .... 它试图将整个函数/事物解释为一个字符串。

SecurityGroups 属性有效,InstanceType 无效。

我不能动态地做到这一点,我在 AWS doc、SO 或其他任何地方都找不到如何实现这一点。我会假设这是简单的东西。我错过了什么?


编辑:

选项 4:使用条件

Conditions:
  IsProduction: !Equals [ !Ref AWSEBEnvironmentName, production ]

option_settings:

  aws:autoscaling:launchconfiguration:
    InstanceType: !If [ IsProduction, m5.large, t3.medium ]
    SecurityGroups:
      - "Fn::FindInMap": ["EnvironmentMap", "Ref": "AWSEBEnvironmentName", "SecurityGroup"]

错误:YAML exception: Invalid Yaml: could not determine a constructor for the tag !Equals in...

但这来自conditions 和if 的文档。


编辑 2:

我最终发现InstanceType 的选项是obsolute,我们应该使用:

aws:ec2:instances
  InstanceTypes: "t3.medium"

但是很可惜,这也不能解决问题,因为我也不能在这里使用替换功能(Fn:findInMap)。

【问题讨论】:

【参考方案1】:

FindInMapoption_settings 中不起作用的原因是,那里只允许四个内在函数(来自docs):

参考 Fn::GetAtt Fn::加入 Fn::GetOptionSetting

我不相信SecurityGroups 有效。我认为您的脚本在SecurityGroups 中的FindInMap 有机会被评估之前失败了。

但是,我试图找到一种方法使用Resources。我得到的关闭是使用以下config 文件:

Mappings:
  EnvironmentMap:
    production:
      TheType: "t3.medium"
    staging:
      TheType: "t2.small"

Resources:
  AWSEBAutoScalingLaunchConfiguration:
    Type: AWS::AutoScaling::LaunchConfiguration
    Properties:
      InstanceType:
        ? "Fn::FindInMap"
        :
          - EnvironmentMap
          - 
            Ref: "AWSEBEnvironmentName"
          - TheType

虽然这更近了一步,但它最终还是失败了。原因是当 EB 将我们的 Resources 配置文件与它自己的模板连接时,它会产生以下内容:

"InstanceType": 
  "Ref": "InstanceType", # <--- this should NOT be here :-(
  "Fn::FindInMap": [
    "EnvironmentMap",
    
      "Ref": "AWSEBEnvironmentName"
    ,
    "TheType"
  ]
,

而不是

"InstanceType": 
  "Fn::FindInMap": [
    "EnvironmentMap",
    
      "Ref": "AWSEBEnvironmentName"
    ,
    "TheType"
  ]
,

而出现这种情况是因为原来的InstanceType(联合操作前)是:

"InstanceType":"Ref":"InstanceType",

因此,EB 不是用我们的配置文件中提供的自定义InstanceType 替换 InstanceType,而是合并它们。

【讨论】:

感谢您的回答。我已经学到了一些东西。我认为这是一个基本操作。在 dev 上使用更小、更便宜的 EC2,在 prod 上使用更大更快的 EC2。这怎么这么难?那我们应该如何达到同样的效果呢? @Vetras 我目前没有答案。我也尝试使用Fn::GetOptionSetting,但没有运气。也许还有其他方法,更简单。我暂时不知道。对不起。 @Vetras 作为旁注,如果您使用 CloudFormation 来配置您的 EB 环境,您可以在那里轻松完成,而不是从 .ebextensions 现在整个项目都是ebextensions,有点大。我计划很快将其克隆到 terraform 中。感谢您的帮助。

以上是关于使用 ebextensions 动态设置每个环境的 EC2 实例类型的主要内容,如果未能解决你的问题,请参考以下文章

在 ebextensions 文件中使用环境变量

AWS Elastic Beanstalk:如何在 ebextensions 中使用环境变量?

从 .ebextensions 配置文件访问 Elastic Beanstalk 环境属性

使用配置文件 (.ebextensions) 在自定义 VPC 中为 TCP 直通配置 Elastic Beanstalk 环境的负载均衡器

在 Elastic Beanstalk 中,如何使用 .ebextensions 将现有安全组设置为负载均衡器?

如何为每个 ElasticBeanstalk 环境唯一的 DynamoDB 表名添加前缀