如何在 AWS Cloud Formation 中实施嵌套堆栈?

Posted

技术标签:

【中文标题】如何在 AWS Cloud Formation 中实施嵌套堆栈?【英文标题】:How do I implement Nested Stacks in AWS Cloud formation? 【发布时间】:2021-10-26 14:02:09 【问题描述】:

我正在开发一个 AWS Lambda 应用程序。这是一个 REST API,所以我使用的是 AWS Lambda、API Gateway 和 AWS RDS。我正在使用 aws sam 进行部署和所有操作。

我的 REST API 中有 193 个端点,这意味着 193 个 Lambda 函数。当我尝试部署它时,我收到以下错误消息。

Waiting for changeset to be created..
Error: Failed to create changeset for the stack: aaa-restapi, ex: Waiter ChangeSetCreateComplete failed: 
Waiter encountered a terminal failure state: For expression "Status" we matched expected path: 
"FAILED" Status: FAILED. Reason: Template format error: 
Number of resources, 583, is greater than maximum allowed, 500

下面是我template.yaml 的一小部分。 请注意,在下面的代码中,我唯一要删除的是 100 多个 Lambda 函数。所有 Lambda 函数看起来都一样,除了它们为 REST API 打开的端点外没有区别。其他一切都在那里。

AWSTemplateFormatVersion: '2010-09-09'
    Transform: AWS::Serverless-2016-10-31
    Description: >
      aws-restapi
    
      Sample SAM Template for aws-restapi
      
    
    Globals:
      Function:
        Timeout: 30
        VpcConfig:
            SecurityGroupIds:
              - sg-041f2459xxx921e8e
            SubnetIds:
              - subnet-03xxdb2d
              - subnet-c4dxx4cb
              - subnet-af5xxx8
              - subnet-748xxf28
              - subnet-d13xxx9c
              - subnet-e9exxxx7
    
    Resources:
      GetAllAccountingTypesFunction:
        Type: AWS::Serverless::Function 
        Properties:
          CodeUri: aws-restapi/
          Handler: source/accounting-types/accountingtypes-getall.getallaccountingtypes
          Runtime: nodejs14.x
          Events:
            GetAllAccountingTypesAPIEvent:
              Type: Api 
              Properties:
                Path: /accountingtypes/getall
                Method: get
      GetAccountingTypeByIDFunction:
        Type: AWS::Serverless::Function 
        Properties:
          CodeUri: aws-restapi/
          Handler: source/accounting-types/accountingtypes-byid.getbyid
          Runtime: nodejs14.x
          Events:
            GetAllAccountingTypesAPIEvent:
              Type: Api 
              Properties:
                Path: /accountingtypes/getbyid
                Method: get
        
    
    
      GetUserRoleByIDFunction:
        Type: AWS::Serverless::Function 
        Properties:
          CodeUri: aws-restapi/
          Handler: source/user-role/userrole-getbyid.getUserRoleByID
          Runtime: nodejs14.x
          Events: 
            GetUserRoleByIDAPIEvent:
              Type: Api 
              Properties:
                Path: /userrole/getbyid
                Method: get
        
      GetUserRoleByUserFunction:
        Type: AWS::Serverless::Function 
        Properties:
          CodeUri: aws-restapi/
          Handler: source/user-role/userrole-getbyuser.getUserRoleByUser
          Runtime: nodejs14.x
          Events:
            GetUserRoleByUserAPIEvent:
              Type: Api 
              Properties:
                Path: /userrole/getbyuser
                Method: get
        
      GetUserRoleByRoleFunction:
        Type: AWS::Serverless::Function 
        Properties:
          CodeUri: aws-restapi/
          Handler: source/user-role/userrole-getbyrole.getAllUsersByRole
          Runtime: nodejs14.x
          Events:
            GetUserRoleByRoleAPIEvent:
              Type: Api 
              Properties:
                Path: /userrole/getbyrole
                Method: get
        
      SaveUserRoleFunction:
        Type: AWS::Serverless::Function 
        Properties:
          CodeUri: aws-restapi/
          Handler: source/user-role/userrole-save.saveUserRole
          Runtime: nodejs14.x
          Events:
            SaveUserRoleAPIEvent:
              Type: Api 
              Properties:
                Path: /userrole/save
                Method: post
        
      UpdateUserRoleFunction:
        Type: AWS::Serverless::Function 
        Properties:
          CodeUri: aws-restapi/
          Handler: source/user-role/userrole-update.updateeUserRole
          Runtime: nodejs14.x
          Events:
            UpdateUserRoleAPIEvent:
              Type: Api 
              Properties:
                Path: /userrole/update
                Method: post
        
    
         LambdaRole:
        Type: 'AWS::IAM::Role'
        Properties:
          AssumeRolePolicyDocument:
            Version: "2012-10-17"
            Statement:
              - Effect: Allow
                Principal:
                  Service:
                    - lambda.amazonaws.com
                Action:
                  - 'sts:AssumeRole'
          Path: /
          ManagedPolicyArns:
            - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
          Policies:
            - PolicyName: root
              PolicyDocument:
                Version: "2012-10-17"
                Statement:
                  - Effect: Allow
                    Action:
                      - ec2:DescribeNetworkInterfaces
                      - ec2:CreateNetworkInterface
                      - ec2:DeleteNetworkInterface
                      - ec2:DescribeInstances
                      - ec2:AttachNetworkInterface
                    Resource: '*'
    
    Outputs:
     
      HelloWorldApi:
        Description: "API Gateway endpoint URL for Prod stage for functions"
        Value: !Sub "https://$ServerlessRestApi.execute-api.$AWS::Region.amazonaws.com/Prod/"
    

我查看了互联网并发现我需要做嵌套堆栈。在我看过的所有教程中,他们都在谈论将你的堆栈划分为安全、网络、管理等等。我不需要它们中的任何一个,这是一个简单的 REST API,我所需要的只是在不遇到 AWS 限制错误的情况下部署它。

我不明白如何用我的代码实现嵌套堆栈。以我上面的代码为例,有人可以告诉我如何实现嵌套堆栈吗?然后我可以查看代码,从中学习并将其实现到完整的应用程序中。

----更新-------

按照罗伯特的建议,我设法制作了嵌套堆栈。然而我们有一个问题。它为每个堆栈创建了不同的 API Gateway URL。但我只想要一个 API 网关 URL。下面是我的代码。

template.yaml

AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: >
  aws-restapi

  Sample SAM Template for aws-restapi
  
# More info about Globals: https://github.com/awslabs/serverless-application-model/blob/master/docs/globals.rst
Globals:
  Function:
    Timeout: 5
    VpcConfig:
        SecurityGroupIds:
          - sg-041f2459dcd921e8e
        SubnetIds:
          - subnet-0381db2d
          - subnet-c4d5c4cb
          - subnet-af5c03c8
          - subnet-7487df28
          - subnet-d139d69c
          - subnet-e9e88bd7

Resources:
  GetAllAccountingTypesFunction:
    Type: AWS::Serverless::Function # More info about Function Resource: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#awsserverlessfunction
    Properties:
      CodeUri: aws-restapi/
      Handler: source/accounting-types/accountingtypes-getall.getallaccountingtypes
      Runtime: nodejs14.x
      Events:
        GetAllAccountingTypesAPIEvent:
          Type: Api # More info about API Event Source: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#api
          Properties:
            Path: /accountingtypes/getall
            Method: get
  GetAccountingTypeByIDFunction:
    Type: AWS::Serverless::Function # More info about Function Resource: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#awsserverlessfunction
    Properties:
      CodeUri: aws-restapi/
      Handler: source/accounting-types/accountingtypes-byid.getbyid
      Runtime: nodejs14.x
      Events:
        GetAllAccountingTypesAPIEvent:
          Type: Api # More info about API Event Source: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#api
          Properties:
            Path: /accountingtypes/getbyid
            Method: get
   # DependsOn: GetAllAccountingTypesFunction

  NestedStack:
    Type: AWS::CloudFormation::Stack
    Properties:
      TemplateURL: template_user.yaml
  

  LambdaRole:
    Type: 'AWS::IAM::Role'
    Properties:
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: Allow
            Principal:
              Service:
                - lambda.amazonaws.com
            Action:
              - 'sts:AssumeRole'
      Path: /
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
      Policies:
        - PolicyName: root
          PolicyDocument:
            Version: "2012-10-17"
            Statement:
              - Effect: Allow
                Action:
                  - ec2:DescribeNetworkInterfaces
                  - ec2:CreateNetworkInterface
                  - ec2:DeleteNetworkInterface
                  - ec2:DescribeInstances
                  - ec2:AttachNetworkInterface
                Resource: '*'

Outputs:
  # ServerlessRestApi is an implicit API created out of Events key under Serverless::Function
  # Find out more about other implicit resources you can reference within SAM
  # https://github.com/awslabs/serverless-application-model/blob/master/docs/internals/generated_resources.rst#api
  HelloWorldApi:
    Description: "API Gateway endpoint URL for Prod stage for functions"
    Value: !Sub "https://$ServerlessRestApi.execute-api.$AWS::Region.amazonaws.com/Prod/"

template_user.yaml

AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: >
  aws-restapi

  Sample SAM Template for aws-restapi

    Globals:
      Function:
        Timeout: 5
        VpcConfig:
            SecurityGroupIds:
              - sg-041f2****cd921e8e
            SubnetIds:
              - subnet-03***b2d
              - subnet-c4d***cb
              - subnet-af5***8
              - subnet-74***f28
              - subnet-d139***c
              - subnet-e9***bd7
      
    
    Resources:
      GetUserRoleByIDFunction:
        Type: AWS::Serverless::Function # More info about Function Resource: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#awsserverlessfunction
        Properties:
          CodeUri: aws-restapi/
          Handler: source/user-role/userrole-getbyid.getUserRoleByID
          Runtime: nodejs14.x
          Events: 
            GetUserRoleByIDAPIEvent:
              Type: Api # More info about API Event Source: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#api
              Properties:
                Path: /userrole/getbyid
                Method: get
    
      GetUserRoleByUserFunction:
        Type: AWS::Serverless::Function # More info about Function Resource: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#awsserverlessfunction
        Properties:
          CodeUri: aws-restapi/
          Handler: source/user-role/userrole-getbyuser.getUserRoleByUser
          Runtime: nodejs14.x
          Events:
            GetUserRoleByUserAPIEvent:
              Type: Api # More info about API Event Source: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#api
              Properties:
                Path: /userrole/getbyuser
                Method: get
       # DependsOn: GetUserRoleByIDFunction
      GetUserRoleByRoleFunction:
        Type: AWS::Serverless::Function # More info about Function Resource: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#awsserverlessfunction
        Properties:
          CodeUri: aws-restapi/
          Handler: source/user-role/userrole-getbyrole.getAllUsersByRole
          Runtime: nodejs14.x
          Events:
            GetUserRoleByRoleAPIEvent:
              Type: Api # More info about API Event Source: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#api
              Properties:
                Path: /userrole/getbyrole
                Method: get
        #DependsOn: GetUserRoleByUserFunction
      SaveUserRoleFunction:
        Type: AWS::Serverless::Function # More info about Function Resource: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#awsserverlessfunction
        Properties:
          CodeUri: aws-restapi/
          Handler: source/user-role/userrole-save.saveUserRole
          Runtime: nodejs14.x
          Events:
            SaveUserRoleAPIEvent:
              Type: Api # More info about API Event Source: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#api
              Properties:
                Path: /userrole/save
                Method: post
       # DependsOn: GetUserRoleByRoleFunction
      UpdateUserRoleFunction:
        Type: AWS::Serverless::Function # More info about Function Resource: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#awsserverlessfunction
        Properties:
          CodeUri: aws-restapi/
          Handler: source/user-role/userrole-update.updateeUserRole
          Runtime: nodejs14.x
          Events:
            UpdateUserRoleAPIEvent:
              Type: Api # More info about API Event Source: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#api
              Properties:
                Path: /userrole/update
                Method: post
        #DependsOn: SaveUserRoleFunction

在此示例中,我有两个堆栈,API Gateway 确实创建了 2 个 URL。

    template.yaml URL - https://ez5khz***.execute-api.us-east-1.amazonaws.com/Prod/ template_user.yaml URL - https://7imy9b6***.execute-api.us-east-1.amazonaws.com/Prod/

我希望将使用 template.yaml 创建的 URL 应用于所有 lambda 函数,无论它位于哪个嵌套堆栈中。我还计划稍后为其分配一个域。

我怎样才能让它在一个 URL 下工作?

【问题讨论】:

【参考方案1】:

您可以使用Cloudformation Stack Resource 在 CloudFormation 中实现嵌套堆栈。模板 url 将使用 Cloudformation Package command 解析。

代码示例:

NestedStack:
    Type: AWS::CloudFormation::Stack
    Properties:
      TemplateURL: ./src/nested-stack/infrastructure.yml

编辑:也许您应该将一些端点合并到一个 Lambda 中,并使用 express 之类的东西进行路由。

【讨论】:

我认识罗伯特。我对该链接有很多看法。但我更需要一个与我类似的代码示例,因为我不明白如何使链接内容为我工作。 好的,这将我的 lambda 函数拆分为多个堆栈,对吗?你能用你的代码示例替换我的代码,并告诉我这些 lambda 函数将如何分成不同的堆栈吗?关于您的建议,不,我不想使用 express 或任何只会增加不必要的复杂性的东西。 您需要将我的 sn-p 添加到您的 .yml 文件中,然后创建一个新的 .yml 文件,在其中放置一些 Lambda。我的 sn-p 的 Template Url 需要指向您的第二个 .yml 文件。 啊!我们去吧。让我试试看。 谢谢罗伯特,它成功了。但现在它为它创建的每个堆栈创建不同的 API 网关端点。我已经更新了我的问题,请检查并提出建议。

以上是关于如何在 AWS Cloud Formation 中实施嵌套堆栈?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 Cloud Formation 模板自动扩展 DynamoDB?

以下 AWS Cloud Formation 错误?

我能否在 SAM 模板中使用 AWS Cloud Formation 资源语法,反之亦然?

AWS Cloud Formation 陷入 Review_In_Progress

AWS Cloud Formation !Sub & !Ref AWS::Serverless::Function Policies 中的函数

AWS:Cloud Formation:是不是可以使用多个“DependsOn”?