Amazon Web Services ブログ

AWS Lambda Powertools for TypeScriptでサーバーレスのベストプラクティスを簡素化する

この記事は Simplifying serverless best practices with AWS Lambda Powertools for TypeScript (記事公開日: 2022 年 7 月 15 日) を翻訳したものです。

開発チームは、ビジネス価値を迅速かつ確実に提供するために、自分たちが所有するワークロードとその期待される動作について、共通の理解を持つ必要があります。AWS Well-Architected Framework とその Serverless Lens は、AWSクラウドで信頼性、安全性、効率性、コスト効果の高いシステムを設計、運用するためのアーキテクチャーのベストプラクティスを提供します。

開発者は、ワークロードの内部状態と現在のステータスに関する情報を発信するように、ワークロードを設計し、設定する必要があります。これにより、エンジニアリングチームは、システムの健全性について、いつでも任意の質問をすることができます。例えば、有用なコンテキスト情報を持つメトリクス、ログ、トレースを出力することで、状況認識が可能になり、開発者は必要なものだけをフィルタリングして選択できるようになります。

このようなプラクティスに従うことで、バグの数を減らし、修正を迅速化し、本番環境へのアプリケーションライフサイクルを短縮することができます。これにより、デプロイリスクの軽減、プロダクションレディかどうかのより正確な評価、システムのデプロイや変更に関するより多くの情報に基づいた意思決定、が可能になります。

AWS Lambda Powertools for TypeScript

AWS Lambda Powertools は、簡単にサーバーレスのベストプラクティスに沿えるように、AWS Lambda 関数用のユーティリティ群を提供します。AWS Hero Yan Cui による DAZN Lambda Powertools の初期実装がこのアイデアのきっかけとなりました。

AWS Lambda Powertools for PythonAWS Lambda Powertools for Java に続き、AWS Lambda Powertools for TypeScript の一般提供を開始することをお知らせします。

AWS Lambda Powertools for TypeScript は、Node.js ランタイムのためのユーティリティ群を提供し、JavaScript と TypeScript の両方のコードベースで使用することができます。このライブラリは、AWS SDK v3 for JavaScript と同様のモジュラーアプローチを採用しています。各ユーティリティは、スタンドアロンの NPM パッケージとしてインストールされます。

現在は、分散トレース (Tracer)、構造化ロギング (Logger)、非同期のビジネスおよびアプリケーションメトリクス (Metrics) という3つの観測機能を備えたライブラリが製品として使用できるようになっています。

Powertools では、以下の3種類の方法でコードを実装することができます:

  • 手動実装 (実装サンプル) : 最もきめ細かい制御が可能です。最も冗長なアプローチであり、依存関係の追加や TypeScript Classes へのリファクタリングが不要という利点もあります。
  • Middy ミドルウェア (実装サンプル) : 既存のコードベースが Middy ミドルウェアエンジンに依存している場合に最適な選択です。Powertools は、この統合をシームレスにするために、互換性のある Middy ミドルウェアを提供します。
  • Method decorator (実装サンプル) : ビジネスロジックを TypeScript Classes で書いている場合は、TypeScript method decorators を使用します。もしクラスを使っていないのであれば、最も大きなリファクタリングが必要になります。

このブログ記事の例では、Middy のアプローチを使用しています。例に従うには、middy がインストールされていることを確認してください:

npm i @middy/core

Logger

Logger  は、JSON として構造化された出力を行う、Lambda に特化したロガーを提供します。
その主な特徴は以下の通りです:

  • Lambda のコンテキスト、コールドスタート、JSON で構造化されたログからキーフィールドをキャプチャします。
  • Lambda の起動イベントをログ出力 (デフォルトでは無効) します。
  • ログサンプリングにより、起動の一定割合のみ全てのログを出力 (デフォルトでは無効) します。
  • 構造化されたログに任意のタイミングで追加のキーを付加します。
  • カスタムログフォーマッター (Bring Your Own Formatter) を提供し、組織の求めるログ標準と互換性のある構造でログを出力します。

インストール:

npm install @aws-lambda-powertools/logger

使用例:

mport { Logger, injectLambdaContext } from '@aws-lambda-powertools/logger';
 import middy from '@middy/core';

 const logger = new Logger({
    logLevel: 'INFO',
    serviceName: 'shopping-cart-api',
});

 const lambdaHandler = async (): Promise<void> => {
     logger.info('This is an INFO log with some context');
 };

 export const handler = middy(lambdaHandler)
     .use(injectLambdaContext(logger));

Amazon CloudWatch では、アプリケーションが出力する構造化ログは以下のようになります;

{
     "cold_start": true,
     "function_arn": "arn:aws:lambda:eu-west-1:123456789012:function:shopping-cart-api-lambda-prod-eu-west-1",
     "function_memory_size": 128,
     "function_request_id": "c6af9ac6-7b61-11e6-9a41-93e812345678",
     "function_name": "shopping-cart-api-lambda-prod-eu-west-1",
     "level": "INFO",
     "message": "This is an INFO log with some context",
     "service": "shopping-cart-api",
     "timestamp": "2021-12-12T21:21:08.921Z",
     "xray_trace_id": "abcdef123456abcdef123456abcdef123456"
 }

Powertools で生成されたログは、JSON をサポートするサードパーティーの SaaS ベンダーでも取り込み、分析することができます。

Tracer

TracerAWS X-Ray SDK for Node.js に特化した軽量ラッパーです。
その主な特徴は以下の通りです:

  • コールドスタートとサービス名をアノテーションとして、レスポンスや例外処理をメタデータとしてキャプチャします。
  • HTTP(S) クライアントを自動的にトレースし、リクエストごとにセグメントを生成します。
  • デコレータ、ミドルウェア、手動実装によるトレース機能をサポートします。
  • AWS X-Ray SDK for Node.js による AWS SDK v2 および v3 のトレースをサポートします。
  • Lambda 環境で実行しない場合、トレースを自動無効化します。

インストール:

npm install @aws-lambda-powertools/tracer

使用例:

import { Tracer, captureLambdaHandler } from '@aws-lambda-powertools/tracer';
import middy from '@middy/core';

const tracer = new Tracer({
serviceName: 'shopping-cart-api'
});

const lambdaHandler = async (): Promise<void> => {
/* ... Something happens ... */
};

export const handler = middy(lambdaHandler)
.use(captureLambdaHandler(tracer));

AWS X-Ray segments and subsegments emitted by Powertools

Powertools によるAWS X-Ray セグメントとサブセグメント出力

Example service map generated with Powertools

Powertools で生成されたサービスマップの例

Metrics

Metrics は、Amazon CloudWatch Embedded Metric Format (EMF) に従って標準出力にメトリクスをロギングすることにより、非同期にカスタムメトリクスを作成します。これらのメトリクスは、CloudWatch ダッシュボードで可視化したり、アラートのトリガーとして使用することができます。

その主な特徴は以下の通りです:

  • 1つの CloudWatch EMF オブジェクト (大きな JSON blob) を使って、最大100のメトリクスを集計します。
  • よくあるメトリクス定義の間違い (例えば、メトリクス単位、値、最大寸法、最大メトリクスなど) に対して、メトリクスを検証します。
  • メトリクスは CloudWatch サービスによって非同期で作成されます。カスタムスタックは不要で、Lambda 関数のレイテンシーへの影響もありません。
  • 異なるディメンションで一回限りのメトリックを作成することができます。

インストール:

npm install @aws-lambda-powertools/metrics

使用例:

import { Metrics, MetricUnits, logMetrics } from '@aws-lambda-powertools/metrics';
 import middy from '@middy/core';

 const metrics = new Metrics({
    namespace: 'serverlessAirline', 
    serviceName: 'orders'
});

 const lambdaHandler = async (): Promise<void> => {
     metrics.addMetric('successfulBooking', MetricUnits.Count, 1);
 };

 export const handler = middy(lambdaHandler)
     .use(logMetrics(metrics));

Amazon CloudWatchでは、アプリケーションが出力するメトリックは以下のようになります:

{
     "successfulBooking": 1.0,
     "_aws": {
     "Timestamp": 1592234975665,
     "CloudWatchMetrics": [
         {
         "Namespace": "serverlessAirline",
         "Dimensions": [
             [
             "service"
             ]
         ],
         "Metrics": [
             {
             "Name": "successfulBooking",
             "Unit": "Count"
             }
         ]
     },
     "service": "orders"
 }

Serverless TypeScript デモアプリケーション

Serverless TypeScript Demo は、Lambda Powertools for TypeScript の使用方法を示しています。このアプリケーションのデプロイとロードテストの方法は、リポジトリに記載されています。

Serverless TypeScript Demo architecture

Serverless TypeScript デモアーキテクチャ

Get Products Lambda 関数のコードは、ユーティリティの使用方法を示しています。この関数は、LoggerMetricsTracer で実装されており、観測可能なデータを出力することができます。

// blob/main/src/api/get-products.ts
import { APIGatewayProxyEvent, APIGatewayProxyResult} from "aws-lambda";
import { DynamoDbStore } from "../store/dynamodb/dynamodb-store";
import { ProductStore } from "../store/product-store";
import { logger, tracer, metrics } from "../powertools/utilities"
import middy from "@middy/core";
import { captureLambdaHandler } from '@aws-lambda-powertools/tracer';
import { injectLambdaContext } from '@aws-lambda-powertools/logger';
import { logMetrics, MetricUnits } from '@aws-lambda-powertools/metrics';

const store: ProductStore = new DynamoDbStore();
const lambdaHandler = async (event: APIGatewayProxyEvent): Promise<APIGatewayProxyResult> => {

  logger.appendKeys({
    resource_path: event.requestContext.resourcePath
  });

  try {
    const result = await store.getProducts();

    logger.info('Products retrieved', { details: { products: result } });
    metrics.addMetric('productsRetrieved', MetricUnits.Count, 1);

    return {
      statusCode: 200,
      headers: { "content-type": "application/json" },
      body: `{"products":${JSON.stringify(result)}}`,
    };
  } catch (error) {
      logger.error('Unexpected error occurred while trying to retrieve products', error as Error);

      return {
        statusCode: 500,
        headers: { "content-type": "application/json" },
        body: JSON.stringify(error),
      };
  }
};

const handler = middy(lambdaHandler)
    .use(captureLambdaHandler(tracer))
    .use(logMetrics(metrics, { captureColdStartMetric: true }))
    .use(injectLambdaContext(logger, { clearState: true, logEvent: true }));

export {
  handler
};

Logger ユーティリティは、アプリケーションログに有用なコンテキストを追加します。ログを JSON として構造化することで、Amazon CloudWatch Logs Insights を使用して構造化されたデータで検索することができます。これにより、不要な情報をフィルタリングすることができます。

例えば、以下のクエリを使用して、serverless-typescript-demo サービスに対するあらゆるエラーを検索します。

fields resource_path, message, timestamp
| filter service = 'serverless-typescript-demo'
| filter level = 'ERROR'
| sort @timestamp desc
| limit 20

CloudWatch Logs Insights showing errors for the serverless-typescript-demo service.

CloudWatch Logs Insights による serverless-typescript-demo サービスのエラー照会

Tracer ユーティリティは、関数の呼び出し中にカスタムされたアノテーションとメタデータを追加し、それを AWS X-Ray に送信します。アノテーションを使用すると、製品 ID やコールドスタートなどのビジネスやアプリケーションのコンテキスト情報によってトレースを検索し、フィルタリングすることができます。

putProduct メソッドの実行時間と、Lambda ハンドラ関数にアタッチされた ColdStartService アノテーションを確認することができます。

Metrics ユーティリティは、複雑なハイカーディナリティアプリケーションデータの作成を簡素化します。構造化されたデータをメトリクスと一緒に含めることで、必要に応じて検索や追加分析を行うことができます。

putProduct trace view

putProduct トレースビュー

この例では、1秒間に何回、製品が作成、削除、または照会されたかを見ることができます。メトリクスに基づいてアラームを構成することもできます。

Metrics view

Metrics ビュー

Code examples

Powertools は、多くの Infrastructure as Code やデプロイツールで使用することができます。このプロジェクトには、AWS Cloud Development Kit (AWS CDK) または AWS Serverless Application Model (AWS SAM) でデプロイできるサーバーレスアプリケーション用のソースコードとサポートファイルが含まれています。

AWS CDK は、TypeScript をはじめとするプログラミング言語の表現力で、信頼性と拡張性の高いアプリケーションをクラウド上で構築できるようにするものです。AWS SAM CLI は、サーバーレスアプリケーションの作成と管理を容易にするものです。

GitHubリポジトリで提供されているサンプルアプリケーションを利用することで、ライブラリの使い方を素早く理解し、ご自身の AWS 環境で実験することができます。

まとめ

AWS Lambda Powertools for TypeScript は、チーム内および組織全体におけるサーバーレスのベストプラクティスの採用を簡素化、加速、拡大するのに役立ちます。

このライブラリは、AWS Well-Architected Framework の一部として推奨されるベストプラクティスを実装しており、多量の独自実装を必要としません。

このライブラリによって、これらの機能を実装するための運用負荷を軽減するため、お客様は最も重要な機能に集中することができ、ソフトウェア開発ライフサイクルを短縮し、Time To Market を短縮することができます。

このライブラリは、個々の開発者とエンジニアリングチームの両方が、組織のベストプラクティスを標準化するのに役立ちます。ユーティリティは、スタートアップからエンタープライズまで、サーバーレス化のどの段階にいるお客様にも段階的に導入できるように設計されています。

AWS Lambda Powertools for TypeScript を使い始めるには、公式ドキュメントをご覧ください。サーバーレスの学習リソースについては、Serverless Land をご覧ください。

翻訳はプロフェッショナルサービスの松岡が担当しました。原文はこちらです。