服务
关于
CloudProse博客
开发人员经验

CloudFormation嵌套堆栈入门

遵循嵌套堆栈实践,您可以利用CloudFormation的无限潜力。
 贾里德·崔特(Jared Short Trek)
贾里德·肖特(Jared Short) | 2019年7月9日

最近,我看到了有关CloudFormation及其一些局限性和组织实践的讨论。

CloudFormation嵌套堆栈为许多挑战提供了优雅的解决方案。但是,不要只相信我的话。

但是,了解所有可用的技巧和功能是冒险冒险阅读AWS文档中众所周知的Moria Mines。

嵌套堆栈如何工作

首先要意识到的是,嵌套堆栈的处理方式与CloudFormation模板中的任何其他资源一样。这种情况没有什么独特或令人不舒服的。堆栈就是要创建和管理的资源,就像您要管理的任何其他资源一样。

# example template resources for a template stack
Resources:
  MyNestedStack:
    Type: AWS::CloudFormation::Stack # required
      Properties: 
        NotificationARNs: # not required
          - String
        Parameters: # conditionally required if the nested stack requires the parameters
          Key : Value
        Tags: # not required
          - Tag
        TemplateURL: String # required
        TimeoutInMinutes: Integer # not required

通常,当我启动一个项目时,我会创建一个“根”堆栈。该根堆栈仅用于定义基础架构中存在的其他堆栈。

AWSTemplateFormatVersion: '2010-09-09'
Parameters:
  Stage:
    Type: String

Resources:
  DataStores:
    Type: AWS::CloudFormation::Stack
    Properties:
      TemplateURL: shared/datastores/template.yaml
      Parameters:
        Stage: !Ref Stage
  InternalJobs:
    Type: AWS::CloudFormation::Stack
    Properties:
      TemplateURL: internal/jobs/template.yaml
      Parameters:
        Stage: !Ref Stage
  PublicAPI:
    Type: AWS::CloudFormation::Stack
    Properties:
      TemplateURL: public/api/template.yaml
      Parameters:
        Stage: !Ref Stage

This top level template.yaml is supported by a directory tree of the following.

.
├── internal
│   └── jobs
│       └── template.yaml
├── public
│   └── api
│       └── template.yaml
├── shared
│   └── datastores
│       └── template.yaml
└── template.yaml

Finally, for things to go smoothly, we need to embrace the AWS CLI to do 所有 the heavy lifting for us when it comes to managing a template and its lifecycle. aws cloudformation package and aws cloudformation deploy are going to be our core commands for this post.

我发现采用原生的AWS处事方式可能需要一些前期工作,但在项目工作数小时内即可实现投资回报。 您不想推出自己的cloudformation包装。 如果那是您从这篇文章中学到的唯一东西,那就顺其自然。

You'll note that TemplateURL is a file path above. aws cloudformation package manages the process walking a tree of nested stacks and uploading 所有 necessary assets to S3 and rewriting the designated locations in an output template.

部署方式& Management

After a quick aws cloudformation package --template-file template.yaml --output-template packaged.yaml --s3-bucket {your-deployment-s3-bucket} on the root template, you'll get output to packaged.yaml that reflects a new "packaged" template with 所有 necessary assets uploaded to your deployment s3 bucket.

如果您发现自己经常使用CloudFormation,特别是如果您已经在使用VS Code, 查看Matthew Hodgkins的这篇文章。它引入了一些很棒的工具,例如在模板上整理。在您输入错误的资源名称下获得很少的红色花招是完成项目和辩论职业转变之间的区别。

AWSTemplateFormatVersion: '2010-09-09'
Parameters:
  Stage:
    Type: String
Resources:
  DataStores:
    Type: AWS::CloudFormation::Stack
    Properties:
      TemplateURL: //s3.amazonaws.com/serverless-deployment-454679818906-us-east-2/71243994a4224bb9eb149de44022813a.template
      Parameters:
        Stage:
          Ref: Stage
  InternalJobs:
    Type: AWS::CloudFormation::Stack
    Properties:
      TemplateURL: //s3.amazonaws.com/serverless-deployment-454679818906-us-east-2/71243994a4224bb9eb149de44022813a.template
      Parameters:
        Stage:
          Ref: Stage
  PublicAPI:
    Type: AWS::CloudFormation::Stack
    Properties:
      TemplateURL: //s3.amazonaws.com/serverless-deployment-454679818906-us-east-2/71243994a4224bb9eb149de44022813a.template
      Parameters:
        Stage:
          Ref: Stage

嵌套堆栈的优点

这些技巧将使我们能够轻松地管理多个堆栈并在逻辑上拆分资源。我们不必担心200个资源的限制,我们可以更轻松地使用模板,但是资源之间的依赖关系又如何呢?

我们如何让AWS Lambda函数在一个堆栈中知道,并如何将其委派给DynamoDB表或S3存储桶?

Let's talk about CloudFormation Outputs and References. Let's say we have a DynamoDB table that we want to use in our Lambda function. Both in our API and internal compute jobs.

我们创建表,并告诉CloudFormation使动态表名称和ARN可用作输出。

它几乎 总是 允许CloudFormation命名资源的最佳实践。从长远来看,它可以更轻松地管理和维护您的堆栈,并且在一个帐户/区域中拥有多个堆栈而不会发生冲突。

# shared/datastores/template.yaml

AWSTemplateFormatVersion: '2010-09-09'
Parameters:
  Stage:
    Type: String

Resources:
  HelloTable:
    Type: AWS::DynamoDB::Table
    Properties: 
      AttributeDefinitions: 
        - AttributeName: id
          AttributeType: S
      BillingMode: PAY_PER_REQUEST
      KeySchema: 
        - AttributeName: id
          KeyType: HASH

Outputs:
  HelloTable:
    Value: !Ref HelloTable
  HelloTableArn:
    Value: !GetAtt HelloTable.Arn

接下来,我们使用无服务器转换创建一个AWS Lambda函数,该转换可以访问DynamoDB表,并且还传递一个带有表名的环境变量。

You may note we don't have code in here; rather it points to a file relative to the location of the template. This code gets packaged up and put on S3 for us as part of the single cloudformation package command on the root stack.

If you are curious about what other magic lies in the cloudformation package command, 查看文档 !

# internal/jobs/template.yaml
AWSTemplateFormatVersion: '2010-09-09'
Transform: 'AWS::Serverless-2016-10-31'

Parameters:
  Stage:
    Type: String
  HelloTable:
    Type: String
    MinLength: 1
  HelloTableArn:
    Type: String
    MinLength: 1

Resources:
  HelloLambda:
    Type: AWS::Serverless::Function
    Properties:
      Handler: handler.py
      Runtime: python3.7
      CodeUri: src/
      Policies:
        - AWSLambdaExecute
        - Version: '2012-10-17'
          Statement:
            - Effect: Allow
              Action:
                - dynamodb:BatchGetItem
                - dynamodb:GetItem
                - dynamodb:Query
                - dynamodb:Scan
                - dynamodb:BatchWriteItem
                - dynamodb:PutItem
                - dynamodb:UpdateItem
              Resource: !Ref HelloTableArn
      Environment:
        Variables:
          TABLE_NAME: !Ref HelloTable
       大事记 :
        PIIScan:
          Type: Schedule
          Properties:
            Schedule: rate(1 day)

该树(包括AWS Lambda函数及其源代码)看起来可能类似于以下内容。

.
├── internal
│   └── jobs
│       ├── src
│       │   └── handler.py
│       └── template.yaml
├── packaged.yaml
├── public
│   └── api
│       ├── src
│       │   └── handler.py
│       └── template.yaml
├── shared
│   └── datastores
│       └── template.yaml
└── template.yaml

Wiring up these parameters uses something we are quite familiar with, the GetAtt function. On our root level stack, we add in a couple of new Parameters on our InternalJobs stack.

# Top level stack metadata...
Resources:
  DataStores:
    # stack info / resource config
  InternalJobs:
    Type: AWS::CloudFormation::Stack
    Properties:
      TemplateURL: internal/jobs/template.yaml
      Parameters:
        Stage: !Ref Stage
        # ------------- ADDED LINES -------------
        HelloTable: !GetAtt DataStores.Outputs.HelloTable
        HelloTableArn: !GetAtt DataStores.Outputs.HelloTableArn
        # ------------- ADDED LINES -------------
  # stack continues...

现在,如果我们的DynamoDB表由于任何原因更改了名称或ARN,则我们的作业堆栈将基于这些传入参数动态引用更新。开发迭代更快,更自然地实现了正确性。

CloudFormation 进口 / 出口 仅在完全暴露其他服务所依赖的值的情况下,才应依赖功能。请注意,一旦导出已被另一个堆栈“导入”,就无法更改其值。

导出的常见原因是服务可发现性。相反,看看使用 SSM参数 要么 AWS云地图 .

模板的可重用性

Let's say you have a canonical way of using s3 buckets within your 要么 ganization. Its configuration sets up 所有 sorts of lifecycle policies, policies, encryption, etc. Your 要么 ganization creates and provides this template 要么 g.s3.template.yaml.

您的团队希望在您的项目中有3个存储桶。您可以抓取组织模板,并且在您的根模板中,可以使用同一模板多次使用不同行为的参数, CloudFormation条件 在s3模板中,可以简化模板的控制界面。

# Top level stack metadata...
Resources:
  ProcessStore:
    Type: AWS::CloudFormation::Stack
    Properties:
      TemplateURL:  要么 g.s3.template.yaml
  UploadsStore:
    Type: AWS::CloudFormation::Stack
    Properties:
      TemplateURL:  要么 g.s3.template.yaml
      Parameters:
        AllowPublicUploads: true
  AssetStore:
    Type: AWS::CloudFormation::Stack
    Properties:
      TemplateURL:  要么 g.s3.template.yaml
      Parameters:
        IncludePublicCDN: true

部署和管理注意事项

Once your stack is packaged we need to move to deployment. The deployment should reference your output packaged.yaml template.

aws cloudformation deploy --region us-east-2 --template-file packaged.yaml --stack-name blog-post --parameter-overrides Stage=demo

When deploying things that using template transforms 要么 things that create IAM roles/policies you likely need to including --capabilities CAPABILITY_AUTO_EXPAND CAPABILITY_IAM to your command acknowledging you are aware of the activities taking place.

Security best practices dictate that you use --role-arn to delegate an IAM Role to the CloudFormation service to run your stack as, rather than running as your credentials issuing the deployment. Learn more about 在AWS文档中使用服务角色.

粗糙的边缘

CloudFormation变更集功能变得毫无用处。您无法使用它来了解子堆栈中将要发生的变化。虽然它不是最佳选择,但是如果您依靠变更集来了解将要发生的情况,我建议改为转而使用更干净的git合并,并将源代码控制作为事实的预言(所有代码均已定义?对吗?),而不是变更集。您仍要确保您有一个可靠的促销流程(开发人员->产品),并明智地使用 堆叠政策 以防止您的数据消失。

仍然可以进行漂移检测,但是您需要枚举请求漂移检测的每个子堆栈,并收集它们以了解发生了什么变化。

进入不良状态并从不良状态中恢复可能很难理解。 AWS文档, 有值得研究的细节.

如果您使用的是嵌套堆栈或与无服务器远程相关的任何内容,请通过以下网址查看我们的小型社区项目 //serverless.help。我们很乐意帮助您指导正确的方向!

作者
 贾里德·崔特(Jared Short Trek)
贾里德·肖特(Jared Short)