使用 AWS SAM 自動部署巢狀應用程式 - AWS Prescriptive Guidance

本文為英文版的機器翻譯版本,如內容有任何歧義或不一致之處,概以英文版為準。

使用 AWS SAM 自動部署巢狀應用程式

由拉胡爾沙拉德蓋克瓦德 (AWS)、德米特里古林 (AWS) 和塔比沃德 (AWS) 所建立

環境:PoC 或試驗

技術:現代化;無伺服器;DevOps

工作負載:所有其他工作負載

AWS 服務:AWS Serverless Application Repository

Summary

您可以使用 AWS 無伺服器應用程式模型 (AWS SAM),在 Amazon Web Services (AWS SAM) 雲端上建置無伺服器應用程式。AWS SAM 是一種開放原始碼架構,提供簡寫語法來表達函數、API、資料庫和事件來源對應。使用幾行配置,您可以定義您想要的應用程序並對其進行模型。

此模式使用 AWS SAM 範本來自動部署巢狀應用程式。巢狀應用程式是另一個應用程式中的應用程式。父應用程式呼叫其子應用程式。這些是無伺服器架構的鬆散耦合元件。 

使用巢狀應用程式,您可以重複使用獨立撰寫和維護但使用 AWS SAM 和無伺服器應用程式存放庫組成的服務或元件,快速建置高度複雜的無伺服器架構。巢狀應用程式可協助您建置更強大的應用程式、避免重複工作,並確保團隊和組織間的一致性和最佳作法。為了示範巢狀應用程式,模式會部署AWS 無伺服器購物車應用程式範例

先決條件和限制

先決條件

  • 作用中的 AWS 帳戶

  • 現有的虛擬私有雲端 (VPC) 和子網路

  • 整合式開發環境,例如 AWS Cloud9 或 Visual Studio 程式碼 (如需詳細資訊,請參閱開發套件和工具開發套件和工具建置)

  • 使用 pip install wheel(如果尚未安裝)

限制

  • 可嵌套在無伺服器應用程式中的應用程式數量上限為 200。

  • 巢狀應用程式的參數量上限為 60。

產品版本

  • 此解決方案建置在 AWS SAM 命令列界面 (AWS SAM CLI) 1.21.1 版上,但是此架構應該適用於較新版本的 AWS SAM CLI 版本。

Architecture

目標技術堆疊

  • Amazon API Gateway

  • AWS SAM

  • Amazon Cognito

  • Amazon DynamoDB

  • AWS Lambda

  • Amazon Simple Queue Service (Amazon SQS) 佇列

目標架構

下圖顯示的架構AWS 無伺服器購物車應用程式範例

在此解決方案設定中,AWS SAM CLI 可做為 AWS 雲端格式堆疊的介面。AWS SAM 範本會自動部署巢狀應用程式。父系 SAM 範本會呼叫子範本,而父系 CloudFormation 堆疊會部署子系堆疊。每個子堆疊都會建立 AWS SAM CloudFormation 範本中定義的 AWS 資源。

Tools

工具

  • Amazon API Gateway— Amazon API Gateway 是一種 AWS 服務,可讓您建立、發佈、維護、監控和保護任何規模的 REST、HTTP 和 WebSocket API。

  • AWS CloudFormation— AWS CloudFormation 是一個能幫助您模型化與設定 AWS 資源的服務,讓您能花較少的時間管理這些資源,並且有更多時間專注在 AWS 上執行的應用程式上。您可以建立一個範本,描述所有您想要的 AWS 資源,就會為您佈 CloudFormation 和設定那些資源。

  • Amazon Cognito— Amazon Cognito 為您的 Web 和行動應用程式提供身分驗證、授權和使用者管理。

  • Amazon DynamoDB— Amazon DynamoDB 是一項完全受管的 NoSQL 資料庫服務,可提供快速且可預期的效能和無縫的可擴展性。藉助 DynamoDB,您就可以建立資料庫資料表,來存放和擷取任意數量的資料,並為任何層級的請求流量提供服務。

  • AWS Lambda— AWS Lambda 是一項運算服務,可支援執行程式碼,無需佈建或管理伺服器。Lambda 只有在需要時才會執行程式碼,可自動從每天數項請求擴展成每秒數千項請求。只需為使用的運算時間支付費用,一旦未執行程式碼,就會停止計費。

  • AWS SAM— AWS Serverless Application Model (AWS SAM) 是一種開放原始碼架構,您可以用來在 AWS 上建置無伺服器應用程式。無伺服器應用程式結合 Lambda 函數、事件來源及其他資源,以共同執行任務。請注意,無伺服器應用程式不單只是 Lambda 函數,還可包含其他資源,例如 API、資料庫和事件來源對應。

  • Amazon SQS— Amazon Simple Queue Service (Amazon SQS) 提供安全、耐用且可用的託管佇列。您可以使用 Amazon SQS 整合並讓分散式軟體系統和元件脫鉤。

Code

此模式的代碼已附加。

Epics

任務描述所需技能
安裝 AWS SAM CLI。

若要安裝 AWS SAM CLI,請參閱AWS SAM 文件

DevOps
設定 AWS 登入資料。

要設定 AWS 登入資料,讓 AWS SAM CLI 能代您呼叫 AWS 服務,請執行aws configure命令並依照提示操作:

$aws configure AWS Access Key ID [None]: <your_access_key_id> AWS Secret Access Key [None]: your_secret_access_key Default region name [None]: Default output format [None]:

DevOps
任務描述所需技能
部署範本以初始化專案。

若要初始化專案,請執行SAM init命令。當系統提示您選擇範本來源時,請選擇Custom Template Location

DevOps
任務描述所需技能
建立 AWS SAM 應用程式範本。

建立巢狀應用程式的範本。在此範例中,巢狀的應用程式範本是auth.yamlproduct-mock.yaml,以及shoppingcart-service.yaml。如需詳細資訊,請參閲 。其他資訊區段中。

DevOps
建立父系樣板。

建立將叫用巢狀應用程式範本的範本。在這個例子中,父模板是template.yml

DevOps
編譯和建置 AWS SAM 範本程式碼。

使用 AWS SAM CLI,執行以下命令。

sam build

DevOps
任務描述所需技能
部署應用程式。

若要啟動建立巢狀應用程式 CloudFormation 堆疊並在 AWS 環境中部署程式碼的 SAM 範本程式碼,請執行下列命令。

sam deploy --guided --stack-name shopping-cart-nested-stack --capabilities CAPABILITY_IAM CAPABILITY_AUTO_EXPAND

如需詳細資訊,請參閲 。其他資訊區段中。

DevOps
任務描述所需技能
驗證堆疊。

若要檢閱和驗證 AWS CloudFormation 堆疊和 AWS SAM 範本中定義的所有 AWS 資源,請登入 AWS 管理主控台。如需詳細資訊,請參閲 。其他資訊區段中。

DevOps

References

教學和影片

其他資訊

應用程式的目錄結構

在完成所有程式碼之後,範例具有下列目錄結構:

  • 圖層— Layer 是一個檔案封存檔,其中包含程式庫、自訂執行時間或其他依存項目。藉助 Layer,您就可以在函式中使用程式庫,而無須將程式庫納入部署套件。

  • 產品模擬服務— 此資料夾包含所有與產品相關的 Lambda 函數和檔案。

  • 購物車服務— 此資料夾包含所有與購物相關的 Lambda 功能和檔案。

巢狀應用程式範本

對於獨立的應用程式,此範例包含下列三個 .yaml 檔案。

認證

此範本會設定與驗證相關的資源,例如 Amazon Cognito 和 AWS Systems Manager Parameter Store。

AWSTemplateFormatVersion: '2010-09-09' Transform: AWS::Serverless-2016-10-31 Description: >   auth-resources   SAM Template for auth resources Globals:   Function:     Timeout: 3 Resources:   CognitoUserPool:     Type: AWS::Cognito::UserPool     Properties:       UserPoolName: !Sub ${AWS::StackName}-UserPool       AutoVerifiedAttributes:          - email   UserPoolClient:     Type: AWS::Cognito::UserPoolClient     Properties:       ClientName: my-app       GenerateSecret: false       UserPoolId: !Ref CognitoUserPool       ExplicitAuthFlows:         - ADMIN_NO_SRP_AUTH   UserPoolSSM:     Type: AWS::SSM::Parameter     Properties:       Type: String       Name: /serverless-shopping-cart-demo/auth/user-pool-id       Value: !Ref CognitoUserPool   UserPoolARNSSM:     Type: AWS::SSM::Parameter     Properties:       Type: String       Name: /serverless-shopping-cart-demo/auth/user-pool-arn       Value: !GetAtt CognitoUserPool.Arn   UserPoolAppClientSSM:     Type: AWS::SSM::Parameter     Properties:       Type: String       Name: /serverless-shopping-cart-demo/auth/user-pool-client-id       Value: !Ref UserPoolClient Outputs:   CognitoUserPoolId:     Description: "Cognito User Pool ID"     Value: !Ref CognitoUserPool   CognitoAppClientId:     Description: "Cognito App Client ID"     Value: !Ref UserPoolClient      UserPoolARNSSM:     Description: "UserPool ID" Value: !Ref UserPoolARNSSM

產品模擬 .yaml

此範本可部署與產品相關的資源,例如 Lambda 函數和 Amazon API Gateway。

AWSTemplateFormatVersion: '2010-09-09' Transform: AWS::Serverless-2016-10-31 Description: >  product-service  SAM Template for mock product-service Parameters:  AllowedOrigin:    Type: 'String' Globals:  Function:    Timeout: 5    Tracing: Active    AutoPublishAlias: live    Runtime: python3.8    MemorySize: 256    Environment:      Variables:        LOG_LEVEL: "DEBUG"        ALLOWED_ORIGIN: !Ref AllowedOrigin        POWERTOOLS_SERVICE_NAME: product-mock        POWERTOOLS_METRICS_NAMESPACE: ecommerce-app  Api:    EndpointConfiguration: REGIONAL    TracingEnabled: true    OpenApiVersion: '2.0'    Cors:      AllowMethods: "'OPTIONS,POST,GET'"      AllowHeaders: "'Content-Type'"      AllowOrigin: !Sub "'${AllowedOrigin}'" Resources:  GetProductFunction:    Type: AWS::Serverless::Function    Properties:      CodeUri: product-mock-service/      Handler: get_product.lambda_handler      Events:        ListCart:          Type: Api          Properties:            Path: /product/{product_id}            Method: get  GetProductsFunction:    Type: AWS::Serverless::Function    Properties:      CodeUri: product-mock-service/      Handler: get_products.lambda_handler      Events:        ListCart:          Type: Api          Properties:            Path: /product            Method: get  GetProductApiUrl:    Type: AWS::SSM::Parameter    Properties:      Type: String      Name: /serverless-shopping-cart-demo/products/products-api-url      Value: !Sub "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod Outputs:  ProductApi:    Description: "API Gateway endpoint URL for Prod stage for Product Mock Service"    Value: !Sub "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod"

購物車服務 .yaml

此範本會設定購物車相關資源,例如 AWS Identity and Access Management (IAM)、DynamoDB 表格和 Lambda 函數。

AWSTemplateFormatVersion: '2010-09-09' Transform: AWS::Serverless-2016-10-31 Description: >  shoppingcart-service  SAM Template for shoppingcart-service Parameters:  UserPoolArn:    Type: 'AWS::SSM::Parameter::Value<String>'    Default: '/serverless-shopping-cart-demo/auth/user-pool-arn'  UserPoolId:    Type: 'AWS::SSM::Parameter::Value<String>'    Default: '/serverless-shopping-cart-demo/auth/user-pool-id'  ProductServiceUrl:    Type: 'AWS::SSM::Parameter::Value<String>'    Default: '/serverless-shopping-cart-demo/products/products-api-url'  AllowedOrigin:    Type: 'String' Globals:  Function:    Timeout: 5    MemorySize: 512    Tracing: Active    AutoPublishAlias: live    Runtime: python3.8    Environment:      Variables:        TABLE_NAME: !Ref DynamoDBShoppingCartTable        LOG_LEVEL: "INFO"        ALLOWED_ORIGIN: !Ref AllowedOrigin        POWERTOOLS_SERVICE_NAME: shopping-cart        POWERTOOLS_METRICS_NAMESPACE: ecommerce-app  Api:    EndpointConfiguration: REGIONAL    TracingEnabled: true    OpenApiVersion: '2.0'    Cors:      AllowMethods: "'OPTIONS,POST,GET,PUT'"      AllowHeaders: "'Content-Type,Authorization'"      AllowCredentials: true      AllowOrigin: !Sub "'${AllowedOrigin}'" Resources:  UtilsLayer:    Type: AWS::Serverless::LayerVersion    Properties:      ContentUri: ./layers/      CompatibleRuntimes:        - python3.8    Metadata:      BuildMethod: python3.8  CartApi:    Type: AWS::Serverless::Api    DependsOn:      - ApiGWAccount    Properties:      StageName: Prod      MethodSettings:        - DataTraceEnabled: True          MetricsEnabled: True          ResourcePath: "/*"          HttpMethod: "*"          LoggingLevel: INFO      Auth:        Authorizers:          CognitoAuthorizer:            UserPoolArn: !Ref UserPoolArn            Identity: # OPTIONAL              Header: Authorization # OPTIONAL; Default: 'Authorization'  ListCartRole:    Type: AWS::IAM::Role    Properties:      AssumeRolePolicyDocument:        Version: "2012-10-17"        Statement:          - Action:            - "sts:AssumeRole"            Effect: "Allow"            Principal:              Service:                - "lambda.amazonaws.com"  AddToCartRole:    Type: AWS::IAM::Role    Properties:      AssumeRolePolicyDocument:        Version: "2012-10-17"        Statement:          - Action:            - "sts:AssumeRole"            Effect: "Allow"            Principal:              Service:                - "lambda.amazonaws.com"  LambdaLoggingPolicy:    Type: "AWS::IAM::Policy"    Properties:      PolicyName: LambdaXRayPolicy      PolicyDocument:        Version: "2012-10-17"        Statement:          -            Effect: "Allow"            Action: [              "xray:PutTraceSegments",              "xray:PutTelemetryRecords",              "logs:CreateLogGroup",              "logs:CreateLogStream",              "logs:PutLogEvents"              ]            Resource: "*"      Roles:        - !Ref ListCartRole        - !Ref AddToCartRole  DynamoDBReadPolicy:    Type: "AWS::IAM::Policy"    Properties:      PolicyName: DynamoDBReadPolicy      PolicyDocument:        Version: "2012-10-17"        Statement:          -            Effect: "Allow"            Action: [              "dynamodb:GetItem",              "dynamodb:Scan",              "dynamodb:Query",              "dynamodb:BatchGetItem",              "dynamodb:DescribeTable"              ]            Resource:              - !GetAtt DynamoDBShoppingCartTable.Arn      Roles:        - !Ref ListCartRole        - !Ref AddToCartRole  DynamoDBWritePolicy:    Type: "AWS::IAM::Policy"    Properties:      PolicyName: DynamoDBWritePolicy      PolicyDocument:        Version: "2012-10-17"        Statement:          -            Effect: "Allow"            Action: [              "dynamodb:PutItem",              "dynamodb:UpdateItem",              "dynamodb:ConditionCheckItem",              "dynamodb:DeleteItem",              "dynamodb:BatchWriteItem"            ]            Resource: !GetAtt DynamoDBShoppingCartTable.Arn      Roles:        - !Ref AddToCartRole  SQSSendMessagePolicy:    Type: "AWS::IAM::Policy"    Properties:      PolicyName: SQSSendMessagePolicy      PolicyDocument:        Version: "2012-10-17"        Statement:          -            Effect: "Allow"            Action: [              "sqs:SendMessage*"            ]            Resource: !GetAtt CartDeleteSQSQueue.Arn      Roles:        - !Ref AddToCartRole  ListCartFunction:    Type: AWS::Serverless::Function    DependsOn:      - LambdaLoggingPolicy    Properties:      CodeUri: shopping-cart-service/      Handler: list_cart.lambda_handler      Role: !GetAtt ListCartRole.Arn      Layers:        - !Ref UtilsLayer      Environment:        Variables:          USERPOOL_ID: !Ref UserPoolId      Events:        ListCart:          Type: Api          Properties:            RestApiId: !Ref CartApi            Path: /cart            Method: get  AddToCartFunction:    Type: AWS::Serverless::Function    DependsOn:      - LambdaLoggingPolicy    Properties:      CodeUri: shopping-cart-service/      Handler: add_to_cart.lambda_handler      Role: !GetAtt AddToCartRole.Arn      Layers:        - !Ref UtilsLayer      Environment:        Variables:          PRODUCT_SERVICE_URL: !Ref ProductServiceUrl          USERPOOL_ID: !Ref UserPoolId      Events:        AddToCart:          Type: Api          Properties:            RestApiId: !Ref CartApi            Path: /cart            Method: post  UpdateCartFunction:    Type: AWS::Serverless::Function    DependsOn:      - LambdaLoggingPolicy    Properties:      CodeUri: shopping-cart-service/      Handler: update_cart.lambda_handler      Role: !GetAtt AddToCartRole.Arn      Layers:        - !Ref UtilsLayer      Environment:        Variables:          PRODUCT_SERVICE_URL: !Ref ProductServiceUrl          USERPOOL_ID: !Ref UserPoolId      Events:        AddToCart:          Type: Api          Properties:            RestApiId: !Ref CartApi            Path: /cart/{product_id}            Method: put  MigrateCartFunction:    Type: AWS::Serverless::Function    DependsOn:      - LambdaLoggingPolicy    Properties:      CodeUri: shopping-cart-service/      Handler: migrate_cart.lambda_handler      Timeout: 30      Layers:        - !Ref UtilsLayer      Environment:        Variables:          PRODUCT_SERVICE_URL: !Ref ProductServiceUrl          USERPOOL_ID: !Ref UserPoolId          DELETE_FROM_CART_SQS_QUEUE: !Ref CartDeleteSQSQueue      Role: !GetAtt AddToCartRole.Arn      Events:        AddToCart:          Type: Api          Properties:            RestApiId: !Ref CartApi            Path: /cart/migrate            Method: post            Auth:              Authorizer: CognitoAuthorizer  CheckoutCartFunction:    Type: AWS::Serverless::Function    DependsOn:      - LambdaLoggingPolicy    Properties:      CodeUri: shopping-cart-service/      Handler: checkout_cart.lambda_handler      Timeout: 10      Layers:        - !Ref UtilsLayer      Environment:        Variables:          PRODUCT_SERVICE_URL: !Ref ProductServiceUrl          USERPOOL_ID: !Ref UserPoolId      Role: !GetAtt AddToCartRole.Arn      Events:        AddToCart:          Type: Api          Properties:            RestApiId: !Ref CartApi            Path: /cart/checkout            Method: post            Auth:              Authorizer: CognitoAuthorizer  GetCartTotalFunction:    Type: AWS::Serverless::Function    DependsOn:      - LambdaLoggingPolicy    Properties:      CodeUri: shopping-cart-service/      Handler: get_cart_total.lambda_handler      Timeout: 10      Layers:        - !Ref UtilsLayer      Role: !GetAtt ListCartRole.Arn      Events:        GetCartTotal:          Type: Api          Properties:            RestApiId: !Ref CartApi            Path: /cart/{product_id}/total            Method: get  DeleteFromCartFunction:    Type: AWS::Serverless::Function    DependsOn:      - LambdaLoggingPolicy    Properties:      CodeUri: shopping-cart-service/      Handler: delete_from_cart.lambda_handler      ReservedConcurrentExecutions: 25  # Keep the ddb spikes down in case of many deletes at once      Policies:        - SQSPollerPolicy:            QueueName:              !GetAtt CartDeleteSQSQueue.QueueName        - Statement:            - Effect: Allow              Action:                - "dynamodb:DeleteItem"                - "dynamodb:BatchWriteItem"              Resource:                - !GetAtt DynamoDBShoppingCartTable.Arn      Layers:        - !Ref UtilsLayer      Environment:        Variables:          USERPOOL_ID: !Ref UserPoolId      Events:        RetrieveFromSQS:          Type: SQS          Properties:            Queue: !GetAtt CartDeleteSQSQueue.Arn            BatchSize: 5  CartDBStreamHandler:    Type: AWS::Serverless::Function    DependsOn:      - LambdaLoggingPolicy    Properties:      CodeUri: shopping-cart-service/      Handler: db_stream_handler.lambda_handler      Layers:        - !Ref UtilsLayer      Policies:        - AWSLambdaDynamoDBExecutionRole        - Statement:            - Effect: Allow              Action:                - "dynamodb:UpdateItem"              Resource:                - !GetAtt DynamoDBShoppingCartTable.Arn      Events:        Stream:          Type: DynamoDB          Properties:            Stream: !GetAtt DynamoDBShoppingCartTable.StreamArn            BatchSize: 100            MaximumBatchingWindowInSeconds: 60            StartingPosition: LATEST  DynamoDBShoppingCartTable:    Type: AWS::DynamoDB::Table    Properties:      AttributeDefinitions:        - AttributeName: pk          AttributeType: S        - AttributeName: sk          AttributeType: S      KeySchema:        - AttributeName: pk          KeyType: HASH        - AttributeName: sk          KeyType: RANGE      BillingMode: PAY_PER_REQUEST      StreamSpecification:        StreamViewType: 'NEW_AND_OLD_IMAGES'      TimeToLiveSpecification:        AttributeName: expirationTime        Enabled: True  APIGWCloudWatchRole:    Type: 'AWS::IAM::Role'    Properties:      AssumeRolePolicyDocument:        Version: 2012-10-17        Statement:          - Effect: Allow            Principal:              Service:                - apigateway.amazonaws.com            Action: 'sts:AssumeRole'      Path: /      ManagedPolicyArns:        - >-          arn:aws:iam::aws:policy/service-role/AmazonAPIGatewayPushToCloudWatchLogs  ApiGWAccount:    Type: 'AWS::ApiGateway::Account'    Properties:      CloudWatchRoleArn: !GetAtt APIGWCloudWatchRole.Arn  CartDeleteSQSQueue:    Type: AWS::SQS::Queue    Properties:      VisibilityTimeout: 20      RedrivePolicy:        deadLetterTargetArn:          !GetAtt CartDeleteSQSDLQ.Arn        maxReceiveCount: 5  CartDeleteSQSDLQ:    Type: AWS::SQS::Queue  CartApiUrl:    Type: AWS::SSM::Parameter    Properties:      Type: String      Name: /serverless-shopping-cart-demo/shopping-cart/cart-api-url      Value: !Sub "https://${CartApi}.execute-api.${AWS::Region}.amazonaws.com/Prod" Outputs:  CartApi:    Description: "API Gateway endpoint URL for Prod stage for Cart Service"    Value: !Sub "https://${CartApi}.execute-api.${AWS::Region}.amazonaws.com/Prod"

父範本

所以此template.yaml文件調用所有獨立的子堆棧。所有單獨的應用程序將作為嵌套應用程序在單個父 SAM 模板工作。

AWSTemplateFormatVersion: '2010-09-09' Transform: AWS::Serverless-2016-10-31 Description: >   SAM Template for Nested application resources Resources:     Auth:       Type: AWS::Serverless::Application       Properties:         Location: auth.yaml     Product:       Type: AWS::Serverless::Application       Properties:         Location: product-mock.yaml         Parameters:           AllowedOrigin: 'http://localhost:8080'       DependsOn: Auth     Shopping:       Type: AWS::Serverless::Application       Properties:         Location: shoppingcart-service.yaml         Parameters:           AllowedOrigin: 'http://localhost:8080' DependsOn: Product

運行 sam 部署命令

執行命令時sam deploy --guided --stack-name shopping-cart-nested-stack --capabilities CAPABILITY_IAM CAPABILITY_AUTO_EXPAND,它會提示幾個問題。回答所有問題y,然後您就會看到以下畫面輸出。

在主控台上堆疊

登入主控台並選擇 CloudFormation 服務以查看父堆疊和子堆疊。在此範例中,sam-shopping-cart是調用嵌套身份驗證,產品和購物堆棧的父堆棧。

產品堆疊會提供產品 API Gateway URL 連結做為輸出。

下圖顯示產品 API Gateway 的輸出/GET方法。

Attachments

attachment.zip