簡體   English   中英

AWS CloudFormation:如何獲取 DynamoDB GlobalTable 副本的 stream ARN?

[英]AWS CloudFormation: How to get stream ARN of DynamoDB GlobalTable replica?

客觀的

我想通過 CloudFormation 在 eu-west-1 區域部署一個 DynamoDB 全局表,並在 ap-northeast-1 中有一個副本。 在每個區域中,我希望有一個 Lambda 來處理來自相應區域副本的事件。

我有興趣避免手動硬編碼 ap-northeast-1 中的 StreamArn 值。

失敗的方法

我想出了以下模板(為清楚起見,僅顯示相關細節):

Conditions:
  IsMainRegionCondition: !Equals [ !Ref AWS::Region, "eu-west-1" ]

Resources:
  MyDynamoDB:
    Condition: IsMainRegionCondition
    Type: AWS::DynamoDB::GlobalTable
    Properties:
      Replicas:
        - Region: eu-west-1
          PointInTimeRecoverySpecification:
            PointInTimeRecoveryEnabled: true
        - Region: ap-northeast-1
          PointInTimeRecoverySpecification:
            PointInTimeRecoveryEnabled: true

  DynamoToESFunction:
    Type: AWS::Serverless::Function
    Properties:
      Events:
        RawDynamoDBEvent:
          Type: DynamoDB
          Properties:
            Stream: !GetAtt MyDynamoDB.StreamArn

這在 eu-west-1 中運行良好,但在 ap-northeast-1 中,我得到: Template format error: Unresolved resource dependencies [MyDynamoDB] in the Resources block of the template

問題

我明白上面的方法不起作用,因為資源 MyDynamoDB 不是在 ap-northeast-1 中創建的。 但是,我想知道在不需要對 ARN 進行硬編碼的情況下,讓這個設置工作的最佳方法是什么。

我目前的想法是將我的 CloudFormation 堆棧拆分為 2:首先使用 DynamoDB 表部署一個堆棧,然后在第二個中引用(沒有硬編碼)副本的 StreamArn。

來自Global DynamoDB CloudFormation 文檔(在 Return values - StreamArn 下): The StreamArn returned is that of the replica in the region the stack is deployed to 因此,就我而言,這意味着它只能訪問 eu-west-1 中的值。

是否可以引用副本區域 (ap-northeast-1) 的 StreamArn 值而無需對其值進行硬編碼?

提前致謝。

在此博客條目的幫助下,我提出了使用自定義資源的以下解決方案。

簡而言之,我將我的堆棧分成 2 個:在一個中,我部署了DynamoDB::GlobalTable以及一個Serverless::Function ,它將負責為我們獲取 ARN。 另一方面,我有 rest 的組件。

第一個堆棧的模板如下所示:

AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31

Parameters:
  DynamoTableName:
    Type: "String"
    Default: TableNameExample
  MainRegion:
    Type: "String"
    Default: "eu-west-1"


Conditions:
  IsMainRegionCondition: !Equals [ !Ref AWS::Region, !Ref MainRegion ]


Resources:
  DynamoTable:
    Condition: IsMainRegionCondition  # Note: Deployed only in one region, replicated in others
    Type: AWS::DynamoDB::GlobalTable
    Properties:
      TableName: !Ref DynamoTableName
      BillingMode: PAY_PER_REQUEST
      KeySchema:
        - AttributeName: entity_id
          KeyType: HASH
        - AttributeName: language
          KeyType: RANGE
      AttributeDefinitions:
        - AttributeName: entity_id
          AttributeType: S
        - AttributeName: language
          AttributeType: S
      StreamSpecification:
        StreamViewType: NEW_AND_OLD_IMAGES
      Replicas:
        - Region: eu-west-1
          PointInTimeRecoverySpecification:
            PointInTimeRecoveryEnabled: true
        - Region: us-east-1
          PointInTimeRecoverySpecification:
            PointInTimeRecoveryEnabled: true

  GetGlobalTableStreamFunction:
    Type: AWS::Serverless::Function
    Properties:
      Environment:
        Variables:
          TABLE_NAME: !Ref DynamoTableName
      MemorySize: 256
      CodeUri: functions/dynamo_db_stream/
      Handler: app.lambda_handler
      Runtime: python3.7
      Policies:
        - Version: '2012-10-17'
          Statement:
            - Effect: Allow
              Action:
                - dynamodb:DescribeTable
              Resource: '*'

Outputs:
  GetGlobalTableStreamFunctionArn:
    Value: !GetAtt GetGlobalTableStreamFunction.Arn
    Export:
      Name: "get-global-table-stream-function-arn"

Lambda 的代碼如下(注意您需要沿 Lambda 打包crhelper依賴項)。

import logging
import os

import boto3
from crhelper import CfnResource

TABLE_NAME = os.environ["TABLE_NAME"]

logger = logging.getLogger(__name__)

helper = CfnResource(
    json_logging=False,
    log_level='DEBUG',
    boto_level='CRITICAL'
)

dynamo_db_client = boto3.client("dynamodb")


def lambda_handler(event, context):
    helper(event, context)


def get_table_stream_arn():
    logger.info(f"Getting stream ARN for table '{TABLE_NAME}'...")
    response = dynamo_db_client.describe_table(
        TableName=TABLE_NAME
    )

    logger.debug(f"Describe table response: {response}")
    stream_arn = response["Table"]["LatestStreamArn"]
    logger.info(f"ARN for table {TABLE_NAME}: {stream_arn}")
    return stream_arn


@helper.create
def create(event, context):
    logger.info("Received a 'Create' event")

    stream_arn = get_table_stream_arn()
    # This will make the stream ARN accessible via Cfn
    # `!GetAtt DynamoTableStreamArnGetter.StreamArn`
    helper.Data.update({"StreamArn": stream_arn})
    return stream_arn


@helper.update
def update(event, context):
    logger.info("Received an 'Update' event, doing nothing")


@helper.delete
def delete(event, context):
    logger.info("Received a 'Delete' event, doing nothing")

然后,在第二個堆棧中,我們需要創建自定義資源。

Resources:
  
  [...]

  DynamoTableStreamArnGetter:
    Type: 'Custom::DynamoTableStreamArnFunction'
    Version: '1.0'
    Properties:
      ServiceToken: !ImportValue "get-global-table-stream-function-arn"

最后,您可以通過以下方式獲取/引用副本區域(在第二個堆棧模板中)中的 stream ARN:

!GetAtt DynamoTableStreamArnGetter.StreamArn

關於此解決方案的一些說明:

  1. 我不確定對於這種特殊情況,我們是否需要在 Lambda 的create(...) function 中return stream_arn
  2. 在第一個模板中,我不喜歡授予描述所有表的權限( Resource: '*' )。 但是,您無法在此處引用DynamoTable資源,因為它不會存在於副本區域中。 如果有人知道將其限制在該表中的更好方法,請告訴我。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM