亚马逊AWS官方博客

AWS Solutions Constructs – 适用于 AWS CDK 的架构模式库

云应用程序的构建需要使用多种组件,例如虚拟服务器、容器、无服务器函数、存储桶和数据库等。能够以安全、可重复的方式预置和配置这些资源,对于操作过程的自动化无比重要,能让您专注于实施的独有部分。

借助 AWS Cloud Development Kit,您可以利用自己喜欢的编程语言的强大功能构建应用程序的模型。您可以使用一种叫做 constructs(构件)的高级别组件来快速构建新的应用程序,这种构件已经预配置了您可以自定义的“合理默认值”。CDK 会使用 AWS CloudFormation 来预置资源,以发挥将您的基础设施作为代码来管理的各种优势。我喜欢使用 CDK 的原因之一就是,您可以构建自定义的组件并将其作为高级别构件进行共享。

正如您可以想象,许多常用的模式不只是对一个客户有用。因此,今天我们宣布推出 AWS Solutions Constructs,这是一个适用于 CDK 的开源扩展库,它提供 well-architected(架构完善的)模式来帮助您构建独特的解决方案。CDK 构件主要针对单项服务, 而 AWS Solutions Constructs 提供多服务模式,将两个或更多的 CDK 资源组合起来并实施日志记录和加密等最佳实践。

AWS Solutions Constructs 的使用
要了解基于模式的方法的强大之处,首先需要了解这种方法在构建新应用程序时的工作原理。例如,假设我要构建一个 HTTP API 以将数据存储到某个 Amazon DynamoDB 表中。为确保表的内容较小,我可以使用 DynamoDB 生存时间 (TTL) 让表中的项目在几天后过期。在 TTL 到期后,系统将从表中删除相关数据并通过 DynamoDB Streams 发送到某个 AWS Lambda 函数,以在 Amazon Simple Storage Service (S3) 中存档过期的数据。

为构建此应用程序,我可以下面的使用几个组件:

  • 一个用于 API 的 Amazon API Gateway 终端节点。
  • 一个用于存储数据的 DynamoDB 表。
  • 一个用于处理 API 请求并将数据存储到 DynamoDB 表中的 Lambda 函数。
  • 用于捕获数据更改的 DynamoDB 流。
  • 一个用于处理数据更改以存档过期数据的 Lambda 函数。

能不能再简单点? 看看 AWS Solutions Constructs 中的可用模式,我找到了两个可帮助我构建应用程序的构件:

  • aws-apigateway-lambda,此构件将实施一个连接到一个 Lambda 函数的 API Gateway REST API。按照 AWS Solutions Constructs 使用的“合理默认值”,此模式为 API Gateway 启用了 CloudWatch 日志记录功能。
  • aws-dynamodb-stream-lambda,此构件将实施一个 DynamoDB 表,将数据更改流式传输到某个具有最低权限 Lambda 函数。

为构建最终架构,我直接将两个构件连接起来:

我使用 TypeScript 来定义 CDK 堆栈,使用 Node.js 来构建 Lambda 函数。我们首先来定义 CDK 堆栈:

 

import * as cdk from '@aws-cdk/core';
import * as lambda from '@aws-cdk/aws-lambda';
import * as apigw from '@aws-cdk/aws-apigateway';
import * as dynamodb from '@aws-cdk/aws-dynamodb';
import { ApiGatewayToLambda } from '@aws-solutions-constructs/aws-apigateway-lambda';
import { DynamoDBStreamToLambda } from '@aws-solutions-constructs/aws-dynamodb-stream-lambda';

export class DemoConstructsStack extends cdk.Stack {
  constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    const apiGatewayToLambda = new ApiGatewayToLambda(this, 'ApiGatewayToLambda', {
      deployLambda: true,
      lambdaFunctionProps: {
        code: lambda.Code.fromAsset('lambda'),
        runtime: lambda.Runtime.NODEJS_12_X,
        handler: 'restApi.handler'
      },
      apiGatewayProps: {
        defaultMethodOptions: {
          authorizationType: apigw.AuthorizationType.NONE
        }
      }
    });

    const dynamoDBStreamToLambda = new DynamoDBStreamToLambda(this, 'DynamoDBStreamToLambda', {
      deployLambda: true,
      lambdaFunctionProps: {
        code: lambda.Code.fromAsset('lambda'),
        runtime: lambda.Runtime.NODEJS_12_X,
        handler: 'processStream.handler'
      },
      dynamoTableProps: {
        tableName: 'my-table',
        partitionKey: { name: 'id', type: dynamodb.AttributeType.STRING },
        timeToLiveAttribute: 'ttl'
      }
    });

    const apiFunction = apiGatewayToLambda.lambdaFunction;
    const dynamoTable = dynamoDBStreamToLambda.dynamoTable;

    dynamoTable.grantReadWriteData(apiFunction);
    apiFunction.addEnvironment('TABLE_NAME', dynamoTable.tableName);
  }
}

在此堆栈中,我首先为 Lambda 函数、API Gateway 终端节点和 DynamoDB 表导入了标准的 CDK 构件。 然后我添加了来自 AWS Solutions Constructs 的两个模式:ApiGatewayToLambdaDynamoDBStreamToLambda

在声明 ApiGatewayToLambdaDynamoDBStreamToLambda 这两个构件后,我将 ApiGatewayToLambda 构件创建的 Lambda 函数和 DynamoDBStreamToLambda 构件创建的 DynamoDB 表存储在两个变量中。

在此堆栈的最后,我通过向该 Lambda 授予对该 DynamoDB 表的读写权限,并将该 DynamoDB 表的名称添加到该 Lambda 函数的环境中,以便在函数代码中使用它将数据存储到表中,从而将这两个模式“连接”起来。

这两个 Lambda 函数的代码位于 CDK 应用程序的 lambda 文件夹中。我使用的 Node.js 12 运行时

restApi.js 会实施该 API 并将数据写入 DynamoDB 表。 URL 路径作为分区键使用,URL 中的所有查询字符串参数都存储为属性。项目的 TTL 按当前时间加 7 天计算。

const { DynamoDB } = require("aws-sdk");

const docClient = new DynamoDB.DocumentClient();

const TABLE_NAME = process.env.TABLE_NAME;
const TTL_WINDOW = 7 * 24 * 60 * 60; // 7 days expressed in seconds

exports.handler = async function (event) {

  const item = event.queryStringParameters;
  item.id = event.pathParameters.proxy;

  const now = new Date(); 
  item.ttl = Math.round(now.getTime() / 1000) + TTL_WINDOW;

  const response = await docClient.put({
    TableName: TABLE_NAME,
    Item: item
  }).promise();

  let statusCode = 204;
  
  if (response.err != null) {
    console.error('request: ', JSON.stringify(event, undefined, 2));
    console.error('error: ', response.err);
    statusCode = 500
  }

  return {
    statusCode: statusCode
  };
};

processStream.js 函数负责处理来自 DynamoDB 流的数据捕获记录,寻找被 TTL 删除的项目。此示例代码未实施存档函数。

exports.handler = async function (event) {
  event.Records.forEach((record) => {
    console.log('Stream record: ', JSON.stringify(record, null, 2));
    if (record.userIdentity.type == "Service" &&
      record.userIdentity.principalId == "dynamodb.amazonaws.com") {

      // Record deleted by DynamoDB Time to Live (TTL)
      
      // I can archive the record to S3, for example using Kinesis Data Firehose.
    }
  }
};

下面我们来看看它是否有效! 首先,我需要安装所有依赖项。为简化依赖项,每个版本的 AWS Solutions Constructs 都与对应的 CDK 版本关联。在此例中,我使用的 CDK 和 AWS Solutions Constructs 模式都为 1.46.0 版。前三个命令会安装简单的 CDK 构件。最后两个命令将安装我将用于此应用程序的 AWS Solutions Constructs 模式。

npm install @aws-cdk/aws-lambda@1.46.0
npm install @aws-cdk/aws-apigateway@1.46.0
npm install @aws-cdk/aws-dynamodb@1.46.0
npm install @aws-solutions-constructs/aws-apigateway-lambda@1.46.0
npm install @aws-solutions-constructs/aws-dynamodb-stream-lambda@1.46.0

现在我将构建应用程序并使用 CDK 来部署应用程序。

npm run build
cdk deploy

cdk deploy 命令的输出结束时,一个绿灯将告诉我堆栈部署已经完成。然后在 Outputs 中,我找到了 API Gateway 的终端节点。

 ✅  DemoConstructsStack

Outputs:
DemoConstructsStack.ApiGatewayToLambdaLambdaRestApiEndpoint9800D4B5 = https://1a2c3c4d.execute-api.eu-west-1.amazonaws.com/prod/

现在,我可以使用 curl 对 API 进行测试:

curl "https://1a2c3c4d.execute-api.eu-west-1.amazonaws.com/prod/danilop?name=Danilo&company=AWS"

下面来看 DynamoDB 表:

该项目已存储,TTL 也已设置。一周后,该项目将被删除并通过 DynamoDB Streams 发送到 processStream.js 函数。

在我完成测试后,我再次使用 CDK 快速删除了我为此应用程序创建的所有资源:

cdk destroy

现已推出
适用于 TypeScript 和 Python 的 AWS Solutions Constructs 现已推出。AWS 解决方案构建器团队正在努力确保使用 CDK 进行 Java 和 C# 编程时也能使用这些构件,请随时关注。使用 AWS Solutions Constructs 和 CDK 不会产生任何费用,您只需为您部署堆栈时创建的资源付费。

此第一版包含 25 个模式,涵盖多种使用案例。现在我们应该关注哪些新模式和功能? 请通过开源项目存储库提供您的反馈!

Danilo