AWS Lambda ベースのサーバーレスオブザーバビリティ
分散システムとサーバーレスコンピューティングの世界では、オブザーバビリティの実現がアプリケーションの信頼性とパフォーマンスを確保する鍵となります。 これは従来のモニタリング以上のものを含んでいます。 Amazon CloudWatch や AWS X-Ray などの AWS オブザーバビリティツールを活用することで、サーバーレスア プリケーションの洞察を得て、問題のトラブルシューティングを行い、アプリケーションのパフォーマンスを最適化できます。 このガイドでは、Lambda ベースのサーバーレスアプリケーションのオブザーバビリティを実装するための重要な概念、ツール、ベストプラクティスについて学びます。
インフラストラクチャやアプリケーションのオブザーバビリティを実装する前の最初のステップは、主要な目標を決定することです。 それは、ユーザーエクスペリエンスの向上、開発者の生産性の向上、Service Level Objective (SLO) の達成、ビジネス収益の増加、またはアプリケーションの種類に応じた他の特定の目標かもしれません。 したがって、これらの主要な目標を明確に定義し、それらをどのように測定するかを確立します。 そこから逆算してオブザーバビリティ戦略を設計します。 詳細については、「重要なものをモニタリングする」を参照してください。
オブザーバビリティの柱
オブザーバビリティには主に 3 つの柱があります:
- ログ:アプリケーションやシステム内で発生した個別のイベント(障害、エラー、状態変化など)の時刻付き記録
- メトリクス:様々な時間間隔で測定された数値データ(時系列データ)。SLI(リクエスト率、エラー率 、所要時間、CPU 使用率など)
- トレース:複数のアプリケーションやシステム(通常はマイクロサービス)にまたがる単一ユーザーの行動を表現したもの
AWS は、AWS Lambda アプリケーションの実用的な洞察を得るために、ログ記録、メトリクスのモニタリング、トレーシングを容易にするネイティブおよびオープンソースのツールを提供しています。
ログ
オブザーバビリティのベストプラクティスガイドのこのセクションでは、以下のトピックについて詳しく説明します:
- 非構造化ログと構造化ログ
- CloudWatch Logs Insights
- ログの相関 ID
- Lambda Powertools を使用したコードサンプル
- CloudWatch ダッシュボードを使用したログの可視化
- CloudWatch Logs の保持期間
ログは、アプリケーション内で発生した個別のイベントです。 これには、失敗、エラー、実行パス、またはその他のイベントが含まれる場合があります。 ログは、非構造化、半構造化、または構造化された形式で記録できます。
非構造化ログと構造化ログ
開発者がアプリケーション内で print
や console.log
ステートメントを使用して単純なログメッセージを始めることがよくあります。
これらは、特に多くのログメッセージを異なるロググループにわたって生成する可能性のある AWS Lambda ベースのアプリケーションでは、大規模にプログラムで解析し分析するのが困難です。
その結果、CloudWatch でこれらのログを統合することは難しく、分析が困難になります。
ログ内の関連情報を見つけるには、テキストマッチや正規表現を使用する必要があります。
以下は非構造化ログの例です:
[2023-07-19T19:59:07Z] INFO Request started
[2023-07-19T19:59:07Z] INFO AccessDenied: Could not access resource
[2023-07-19T19:59:08Z] INFO Request finished
ご覧のように、ログメッセージには一貫した構造がないため、有用な洞察を得るのが難しくなっています。 また、コンテキスト情報を追加するのも困難です。
一方、構造化ログは、一貫したフォーマット(多くの場合 JSON)で情報をログに記録する方法です。 これにより、ログをテキストではなくデータとして扱うことができ、クエリとフィルタリングが簡単になります。 開発者はログをプログラムで効率的に保存、取得、分析することができます。 また、デバッグも容易になります。 構造化ログは、ログレベルを通じて異なる環境でのログの詳細度を簡単に変更する方法を提供します。 ログレベルに注意を払ってください。 過度にログを取ることはコストを増加させ、アプリケーションのスループットを低下させます。 ログを記録する前に、個人を特定できる情報が編集されていることを確認してください。 以下は構造化ログの例です:
{
"correlationId": "9ac54d82-75e0-4f0d-ae3c-e84ca400b3bd",
"requestId": "58d9c96e-ae9f-43db-a353-c48e7a70bfa8",
"level": "INFO",
"message": "AccessDenied",
"function-name": "demo-observability-function",
"cold-start": true
}
アプリケーションからトランザクション、異なるコンポーネント間の相関識別子、ビジネス結果に関する運用情報を出力するために、構造化され、CloudWatch logs に集中化されたログを優先してください
。
CloudWatch Logs Insights
CloudWatch Logs Insights を使用すると、JSON 形式のログのフィールドを自動的に検出できます。さらに、JSON ログを拡 張して、アプリケーション固有のカスタムメタデータをログに記録することができます。これにより、ログの検索、フィルタリング、集計が可能になります。
相関 ID のロギング
例えば、API Gateway から入ってくる HTTP リクエストの場合、相関 ID は requestContext.requestId
パスに設定されます。これは Lambda Powertools を使用して、下流の Lambda 関数で簡単に抽出してログに記録できます。分散システムでは、多くの場合、複数のサービスやコンポーネントが連携してリクエストを処理します。そのため、相関 ID をログに記録し、下流のシステムに渡すことが、エンドツーエンドのトレースとデバッグに不可欠となります。相関 ID は、リクエストの最初の段階で割り当てられる一意の識別子です。リクエストが異なるサービスを通過する際、相関 ID がログに含まれることで、リクエストの全経路をトレースすることができます。AWS Lambda のログに相関 ID を手動で挿入するか、AWS Lambda Powertools のようなツールを使用して、API Gateway から相関 ID を簡単に取得し、アプリケーションログと共に記録することができます。例えば、HTTP リクエストの相関 ID は、API Gateway で開始され、Lambda 関数などのバックエンドサービスに渡されるリクエ スト ID とすることができます。
Lambda Powertools を使用したコードサンプル
ベストプラクティスとして、リクエストのライフサイクルの早い段階で、できるだけ早く相関 ID を生成します。理想的には、API Gateway やアプリケーションロードバランサーなど、サーバーレスアプリケーションのエントリーポイントで生成します。UUID やリクエスト ID、または分散システム全体でリクエストを追跡するために使用できるその他のユニークな属性を使用してください。カスタムヘッダー、ボディ、またはメタデータの一部として、各リクエストと共に相関 ID を渡します。ダウンストリームサービスのすべてのログエントリーとトレースに相関 ID が含まれていることを確認してください。
Lambda 関数のログの一部として相関 ID を手動でキャプチャして含めるか、AWS Lambda Powertools のようなツールを使用することができます。Lambda Powertools を使用すると、サポートされている上流サービスの事前定義されたリクエストパスマッピングから相関 ID を簡単に取得し、アプリケーションログと共に自動的に追加することができます。また、障害が発生した場合に簡単にデバッグし、根本原因を特定して元のリクエストに結び付けられるように、すべてのエラーメッセージに相関 ID が追加されていることを確認してください。
以下のサーバーレスアーキテクチャにおける、相関 ID を含む構造化ログの例と、CloudWatch での表示方法を示すコードサンプルを見てみましょう:
// Logger の初期化
Logger log = LogManager.getLogger();
// Lambda Powertools の @Logger アノテーションを使用します。オプションのパラメータ correlationIdPath を使用して、API Gateway ヘッダーから相関 ID を抽出し、構造化された形式で Lambda 関数のログに correlation_id を挿入します。
@Logging(correlationIdPath = "/headers/path-to-correlation-id")
public APIGatewayProxyResponseEvent handleRequest(final APIGatewayProxyRequestEvent input, final Context context) {
...
// 以下のログステートメントには、追加の correlation_id も含まれます
log.info("Success")
...
}
この例では、Java ベースの Lambda 関数が Lambda Powertools ライブラリを使用して、API Gateway リクエストから来る correlation_id
をログに記録しています。
コードサンプルの CloudWatch ログの例:
{
"level": "INFO",
"message": "Success",
"function-name": "demo-observability-function",
"cold-start": true,
"lambda_request_id": "52fdfc07-2182-154f-163f-5f0f9a621d72",
"correlation_id": "<correlation_id_value>"
}_