如何编写我的云形成更具可重用性而不是重命名资源

How to write my cloud formation more reusable instead of renaming resources

我是 cloudformation 的新手,我正在为我的项目的基础设施编写一些 cfn 代码。但是在写代码的时候有个问题一直困扰着我。假设我要创建以下资源:

Resources: 
MyEC2Instance: 
  Type: "AWS::EC2::Instance"
  Properties: 
    ImageId: "ami-79fd7eee"
    KeyName: "testkey"
    BlockDeviceMappings: 
      - DeviceName: "/dev/sdm"
        Ebs: 
          VolumeType: "io1"
          Iops: "200"
          DeleteOnTermination: "false"
          VolumeSize: "20"
      - DeviceName: "/dev/sdk"
        NoDevice: {}

如您所见,我正在创建一个名为 MyEC2Instance 的资源。现在假设我有另一个名为 stg 的环境,它与上面的完全相同,所以一个简单的方法是使用上面的代码和不同的堆栈名称,但我被告知我需要重命名我的资源名称如下:

Resources: 
MyEC2InstanceStg1: 
  Type: "AWS::EC2::Instance"
  Properties: 
    ImageId: "ami-79fd7eee"
    KeyName: "testkey"
    BlockDeviceMappings: 
      - DeviceName: "/dev/sdm"
        Ebs: 
          VolumeType: "io1"
          Iops: "200"
          DeleteOnTermination: "false"
          VolumeSize: "20"
      - DeviceName: "/dev/sdk"
        NoDevice: {}

但对我来说这看起来并不专业,因为如果我有 10 个环境,这是否意味着我需要复制我的代码 10 次并重命名资源。有没有更好的方法?

我们团队处理这个问题的方式是我们有一个共享模板,用于为我们的每个环境生成堆栈:devstagingproduction。每个环境之间的逻辑 ID 将相同,但生成的物理 ID 将不同。

需要注意的是,如果同一帐户中有不同的环境堆栈,则必须确保任何 Name 属性都是唯一的。如果不需要,请不要设置它们,CloudFormation 将为您生成它们。在需要它们的情况下,我发现 {"Fn::Sub": "${AWS::StackName}-SomeName"} 对此很有帮助,因为它使每个物理资源名称都与其环境堆栈相关。例如,对于 CodeBuild,项目名称是必需的,因此我们执行如下操作:

Resources:
  Project:
    Type: AWS::CodeBuild::Project
    Properties:
      Name: !Sub "${AWS::StackName}-SlackBotLambda"
      ...

因此,如果我在 dev 堆栈上,这会使 CodeBuild 项目命名为 dev-SlackBotLambda

我们采用的另一种策略是传递一个 EnvironmentName 参数,这样我们就可以拥有在一个环境中创建的资源,而不是在另一个环境中创建的资源。例如,我通常希望构建在 Dev 中创建的工件,然后与 Staging 和 Production 共享,所以我有这样创建的存储桶:

Parameters:
  EnvironmentName:
    Type: String
    AllowedValues:
      - dev
      - staging
      - production

  Conditions:
    ShouldGenerateArtifactBucket: !Equals [!Ref EnvironemntName, dev]

  Resources:
    ArtifactBucket:
      Type: AWS::S3::Bucket
      Condition: ShouldGenerateArtifactBucket

只要您的堆栈名称足够独特,您甚至可以将第一个策略与第二个策略结合使用来创建一个存储桶,该存储桶具有足够可预测的名称,以至于一个堆栈可以引用第一个存储桶工件。

让我试着扩展一下情况,以便更好地理解。就云部署而言,10 个资源算不了什么。即使您设法为您的资源起一个很酷的唯一名称,在尝试管理这些大量资源时也无济于事。

此外,逻辑名称很敏感并且受字符和长度限制,因此您很快就会发现您无法正确地命名它们来实现您的需要。当您使用少量资源时,这种方式很有意义,但在适当的时候,资源往往会扩展,因为这样做很容易。

正如@jamie-starke 所建议的,您需要找到其他方法来帮助您跟踪这些资源。在我的团队中,我们使用大量模板参数来驱动堆栈应该做什么。这就是我们尝试帮助跟踪资源的方式,只要值是正确的即可。这很重要,因为我们最近不得不重命名标签值以确保在搜索过程中没有重叠。例如,我们有 Fe-MultiFEMulti-BESingleFEMulti-BESingle-BEMulti 这样的名称。当搜索 FE-Multi 时,所有三个值都被命中,因为控制台执行 StartsWith 条件过滤。

  • 尽可能为资源建立一个名称,例如使用!Sub "${AWS::StackName}-SlackBotLambda"!Join 或其他等价物。
  • 我们会尽可能使用标签对资源进行注释,同时始终提供名称标签。

我们以有助于模板更具可读性的方式命名模板中的资源。通常一些逻辑名称会出现在 aws 控制台中,因为它们不接受名称或标签,而这是我们目前接受的。对于其余资源,名称大多是装饰性的。资源名称也很敏感并且受字符和长度限制,因此它们无法表达。出于这个原因,我们选择主要通过标签来管理资源。标签没有限制,它们提供了一种用于查询的隐式语言。

了解 CloudFormation 模板有点笨拙并且最终它们依赖于其余 AWS 资源,因此需要复制和试验一些代码片段,尤其是在使用条件时。如果您需要有关此过程的更多信息,请告诉我。

因为我们不想将标签分配复制到每个资源,所以我们确保在堆栈创建期间提供正确的标签。为避免任何错误,我们不希望人们从控制台初始化堆栈,而只能从完全控制重要内容的 PowerShell 脚本初始化堆栈。