Amazon Web Services ブログ

AWS Solutions Constructs – AWS CDK のアーキテクチャパターンのライブラリ

クラウドアプリケーションは、仮想サーバー、コンテナ、サーバーレス機能、ストレージバケット、データベースなどの複数のコンポーネントを使用して構築されます。これらのリソースを安全かつ反復可能な方法でプロビジョニングおよび設定できることは、プロセスを自動化し、実装の固有の部分に集中できるようにするために非常に重要です。

AWS Cloud Development Kit を使用すると、お気に入りのプログラミング言語の表現力を活用してアプリケーションをモデル化できます。コンストラクトと呼ばれる高レベルのコンポーネントを使用して、カスタマイズ可能な「実用的なデフォルト」で事前設定し、新しいアプリケーションをすばやく構築できます。CDKは、Infrastructure as Code を管理することにおけるあらゆるメリットを得るために、AWS CloudFormation を使用してリソースをプロビジョニングします。私が CDK を気に入っている理由の 1 つは、独自のカスタムコンポーネントを上位レベルのコンストラクトとして作成および共有できることです。

複数のお客様にとって役立ち得るパターンが繰り返し発生することは想像に難くありません。そこで、本日、AWS Solutions Constructs をリリースします。これは、独自のソリューションの構築に役立つ Well-Architected なパターンを提供する CDK のオープンソース拡張ライブラリです。CDK コンストラクトは主に単一のサービスをカバーしています。 AWS Solutions Constructs は、2 つ以上の CDK のリソースを組み合わせ、ロギングや暗号化などのベストプラクティスを実装する複数サービスのパターンを提供します。

AWS Solutions Constructs を使用する
パターンベースのアプローチの威力を確認するために、新しいアプリケーションを構築する際にどのように役立つかを見てみましょう。例として、Amazon DynamoDB テーブルにデータを格納する HTTP API を構築します。テーブルのコンテンツを小さく保つために、DynamoDB Time to Live (TTL) を使用して、数日後にアイテムを期限切れにすることができます。TTL の期限が切れると、テーブルからデータが削除され、DynamoDB Streams を介して AWS Lambda 関数に送信され、期限切れのデータを Amazon Simple Storage Service (S3) にアーカイブします。

このアプリケーションを構築するには、いくつかのコンポーネントを使用できます。

  • API の Amazon API Gateway エンドポイント。
  • データを格納する DynamoDB テーブル。
  • API リクエストを処理し、DynamoDB テーブルにデータを格納する Lambda 関数。
  • データの変更をキャプチャする DynamoDB Streams。
  • 期限切れのデータをアーカイブするためにデータ変更を処理する Lambda 関数。

よりシンプルにすることができるでしょうか? AWS Solutions Constructs で利用可能なパターンを見ると、アプリの構築に役立つ 2 つのパターンが見つかりました。

  • aws-apigateway-lambda。Lambda 関数に接続された API Gateway REST API を実装するコンストラクトです。AWS Solutions Constructs で使用される「実用的なデフォルト」の例として、このパターンは API Gateway の CloudWatch ロギングを有効にします。
  • aws-dynamodb-stream-lambda。DynamoDB テーブルを実装するコンストラクトであり、最小権限のアクセス許可で Lambda 関数にデータの変更をストリーミングします。

最終的なアーキテクチャを構築するには、これらの 2 つのコンストラクトを接続するだけです。

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 ソリューションコンストラクトから ApiGatewayToLambda および DynamoDBStreamToLambda の 2 つのパターンを追加します。

ApiGatewayToLambda コンストラクトおよび DynamoDBStreamToLambda コンストラクトの 2 つを宣言した後、ApiGatewayToLambda コンストラクトによって作成された Lambda 関数と DynamoDBStreamToLambda によって作成された DynamoDB テーブルを 2 つの変数に格納します。

スタックの最後に、DynamoDB テーブルで読み取り/書き込みを行うアクセス許可を Lambda 関数に付与することで 2 つのパターンを「接続」し、DynamoDB テーブルの名前を Lambda 関数の環境に追加して、 関数コードで使用して、テーブルにデータを格納できます。

2 つの 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 Stream からのデータキャプチャレコードを処理して、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 を使用しています。最初の 3 つのコマンドは、プレーンな CDK コンストラクトをインストールしています。最後の 2 つのコマンドは、このアプリケーションに使用している 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 が設定されます。1 週間後、アイテムは削除され、DynamoDB Streams を介して processStream.js 関数に送信されます。

テストが完了したら、CDK を再度使用して、このアプリケーション用に作成されたすべてのリソースをすばやく削除します。

cdk destroy

今すぐ利用可能
AWS Solutions Constructs が TypeScript と Python で利用可能になりました。AWS Solutions Builders チームは、CDK で Java および C# を使用する場合にもこれらのコンストラクトを利用できるように取り組んでいます。ご期待ください。AWS Solutions Constructs または CDK を使用しても費用はかかりません。スタックのデプロイ時に作成されたリソースに対してのみ支払います。

この最初のリリースには、25 のパターンが含まれており、さまざまなユースケースをカバーしています。今注目すべき新しいパターンと機能はどれでしょうか? オープンソースのプロジェクトリポジトリでフィードバックをお寄せください。

Danilo