AWS Lambda サーバーレスアプリケーションによる分散型トレーシング

トレースサーバーレス関数

Datadog では、サーバーレストレースをメトリクスに接続することで、アプリケーションのパフォーマンスに関する豊富な情報を提供します。これにより、サーバーレスアプリケーションの性質である分散型の環境でもパフォーマンスの問題を的確にトラブルシューティングできます。

Datadog Python、Node.js、Ruby、Go、Java、.NET トレーシングライブラリは、AWS Lambda の分散型トレーシングをサポートしています。

サーバーレスアプリケーションからトレースを送信する

Datadog で AWS Lambda をトレースするためのアーキテクチャ図

Datadog Python、Node.js、Ruby、Go、Java、.NET トレーシングライブラリは、AWS Lambda の分散型トレーシングをサポートしています。トレーサーは、インストール手順を使用してインストールできます。すでに拡張機能をインストールしている場合は、環境変数 DD_ENABLE_TRACINGtrue に設定されていることを確認してください。

ランタイムの推奨事項

Python
Node.js
Ruby
Java
Go
.NET

Python と Node.js

Python および Node.js をサポートする Datadog Lambda ライブラリとトレースライブラリ:

  • Lambda ログおよびトレースとトレース ID およびタグ挿入との自動相関。
  • Serverless Framework、AWS SAM、AWS CDK インテグレーションを使用したコード変更なしのインストール。
  • ダウンストリームの Lambda 関数またはコンテナを呼び出す HTTP リクエストをトレース。
  • AWS SDK で実行された連続 Lambda 呼び出しのトレース。
  • コールドスタートトレーシング
  • AWS Managed Services を利用して非同期 Lambda 呼び出しをトレースする
    • API Gateway
    • SQS
    • SNS
    • SNS と SQS の直接インテグレーション
    • Kinesis
    • EventBridge
  • すぐに使用できる数十の追加の Python および Node.js ライブラリをトレース。

Python や Node.js のサーバーレスアプリケーションでは、Datadog は Datadog のトレーシングライブラリをインストールすることを推奨しています。

上記にリストされていないサーバーレスリソースのトレースをご希望の場合は、機能リクエストを開いてください

Ruby

Ruby サポート用の Datadog Lambda ライブラリとトレースライブラリ:

  • Lambda ログおよびトレースとトレース ID およびタグ挿入との自動相関。
  • ダウンストリームの Lambda 関数またはコンテナを呼び出す HTTP リクエストをトレース。
  • すぐに使用できる数十の追加の Ruby ライブラリをトレース。

Datadog のトレーシングライブラリを使って、Datadog でサーバーレストレーシングの関数をトレースすることができます。

上記にリストされていないサーバーレスリソースのトレースをご希望の場合は、機能リクエストを開いてください

Go

Go サポート用の Datadog Lambda ライブラリとトレースライブラリ:

  • Lambda ログおよびトレースとトレース ID およびタグ挿入との手動相関。
  • ダウンストリームの Lambda 関数またはコンテナを呼び出す HTTP リクエストをトレース。
  • すぐに使用できる数十の追加 Go ライブラリをトレース。

Go のサーバーレスアプリケーションでは、Datadog は Datadog のトレーシングライブラリをインストールすることを推奨しています。

上記にリストされていないサーバーレスリソースのトレースをご希望の場合は、機能リクエストを開いてください

Java

Java サポート用の Datadog Lambda ライブラリとトレースライブラリ:

  • Lambda ログおよびトレースとトレース ID およびタグ挿入との相関。詳細は、Java ログとトレースの接続を参照してください。
  • ダウンストリームの Lambda 関数またはコンテナを呼び出す HTTP リクエストをトレース。
  • すぐに使用できる数十の追加 Java ライブラリをトレース。

Java のサーバーレスアプリケーションでは、Datadog は Datadog のトレーシングライブラリをインストールすることを推奨しています。

Java Lambda 関数用の Datadog のトレースライブラリに関してフィードバックがございましたら、Datadog Slack コミュニティ#serverless チャネルで行われているディスカッションをご確認ください。

.NET

.NET 用のトレースライブラリは以下に対応しています。

  • ダウンストリームの Lambda 関数またはコンテナを呼び出す HTTP リクエストをトレース。
  • すぐに使用できる数十の追加 .NET ライブラリをトレース。

.NET のサーバーレスアプリケーションでは、Datadog は Datadog のトレーシングライブラリをインストールすることを推奨しています。

.NET Azure サーバーレスアプリケーションを介したトレースの詳細をご覧ください。

ハイブリッド環境

Datadog のトレーシングライブラリ (dd-trace) を Lambda 関数とホストの両方にインストールした場合は、AWS Lambda、コンテナ、オンプレミスホスト、マネージドサービスなど、インフラストラクチャーの境界を越えてリクエストの全体像がトレースに自動的に表示されます。

Datadog Agent でホストに dd-trace がインストールされていて、サーバーレス関数が AWS X-Ray でトレースされる場合、インフラストラクチャー全体で接続された単一のトレースを表示するには、トレースのマージが必要です。dd-trace と AWS X-Ray からのトレースのマージの詳細については、サーバーレストレースのマージのドキュメントをご覧ください。

Datadog の AWS X-Ray インテグレーションは、Lambda 関数のトレースのみを提供します。コンテナまたはホストベースの環境でのトレースの詳細については、Datadog APM のドキュメントをご覧ください。

Lambda 関数のプロファイリング (公開ベータ版)

ベータ期間中は、プロファイリングを追加料金なしで利用できます。

Datadog の Continuous Profiler は、Python ではバージョン 4.62.0、Layer ではバージョン 62 以上のベータ版で利用できます。このオプション機能は、環境変数 DD_PROFILING_ENABLEDtrue に設定することで有効になります。

Continuous Profiler は、定期的に起動するスレッドを生成し、実行中のすべての Python コードの CPU とヒープのスナップショットを取得することで動作します。これにはプロファイラー自体も含まれることがあります。プロファイラー自身を無視したい場合は、DD_PROFILING_IGNORE_PROFILERtrue に設定します。

トレースマージ

ユースケース

Datadog は Datadog APM トレースライブラリ (dd-trace) のみの使用を推奨していますが、高度な状況ではトレースマージを使って Datadog トレースと AWS X-Ray を組み合わせて使用することもできます。トレースマージは、Node.js と Python の AWS Lambda 関数で利用可能です。どのトレーシングライブラリを使用するかわからない場合は、トレーシングライブラリの選択をお読みください。

dd-trace と AWS X-Ray トレーシングライブラリの両方をインスツルメントするのは、主に 2 つの理由からです。

  • AWS サーバーレス環境では、dd-trace ですでに Lambda 関数をトレースしているため、AppSync や Step Functions など、AWS マネージドサービスの AWS X-Ray アクティブトレースを要求して dd-trace および AWS X-Ray スパンを単一トレースで視覚化します。
  • Lambda 関数とホストの両方を使用するハイブリッド環境では、dd-trace がホストをインスツルメントし、AWS X-Ray が Lambda 関数をインスツルメントするため、Lambda 関数およびホスト全体のトランザクションの接続済みトレースを視覚化します。

注: この場合、使用料が高額になる可能性があります。X-Ray スパンは、トレースのマージ後 2~5 分間は引き続き使用可能です。Datadog では、通常は、単一のトレーシングライブラリの使用をおすすめしています。トレーシングライブラリの選択方法についてご覧ください。

上記のユースケースをセットアップする手順は以下のとおりです。

AWS サーバーレス環境におけるトレースのマージ

AWS X-Ray は、バックエンド AWS サービス (AWS X-Ray アクティブトレース) とクライアントライブラリ一式の両方を提供します。Lambda コンソールでバックエンド AWS サービスのみを有効にすると、AWS Lambda 関数に InitializationInvocation スパンが与えられます。API Gateway および Step Function コンソールから、AWS X-Ray アクティブトレースを有効にすることも可能です。

AWS X-Ray SDK および Datadog APM クライアントライブラリ (dd-trace) は、いずれも関数に直接アクセスしてダウンストリームのコールのメタデータとスパンを追加します。dd-trace を使用してハンドラーレベルでトレースする場合は、以下のようなセットアップになります。

  1. AWS Lambda コンソールおよび Datadog 内の AWS X-Ray インテグレーションで、Lambda 関数の AWS X-Ray アクティブトレースを有効にしてあります。
  2. 使用している Lambda ランタイム用のインストール手順に従い、Datadog APM (dd-trace) を使用して Lambda 関数をインスツルメントしてあります。
  3. dd-trace により、サードパーティライブラリにパッチが自動的に適用されているため、AWS X-Ray クライアントライブラリをインストールする必要はありません。
  4. Lambda 関数で DD_MERGE_XRAY_TRACES 環境を true に設定し、X-Ray と dd-trace トレースをマージします (Ruby では DD_MERGE_DATADOG_XRAY_TRACES)。

AWS Lambda とホスト全体のトレース

Datadog のトレーシングライブラリ (dd-trace) を Lambda 関数とホストの両方にインストールした場合は、AWS Lambda、コンテナ、オンプレミスホスト、マネージドサービスなど、インフラストラクチャーの境界を越えてリクエストの全体像がトレースに自動的に表示されます。

Datadog Agent でホストに dd-trace がインストールされていて、Node.js または Python サーバーレス関数が AWS X-Ray でトレースされる場合、セットアップは以下のようになります。

  1. Lambda 関数のトレース用に AWS X-Ray インテグレーションがインストールされていて、AWS X-Ray アクティブトレースと X-Ray クライアントライブラリのインストールが可能です。
  2. Lambda ランタイム用の Datadog Lambda ライブラリがインストール済みで、DD_TRACE_ENABLED 環境変数が false に設定されています。
  3. ホストおよびコンテナベースのインフラストラクチャーで Datadog APM が構成されています。

そして、X-Ray と Datadog APM トレースを同じフレームグラフに表示するには、すべてのサービスに同じ env タグが必要です。

: 分散型トレーシングはホストまたはコンテナベースアプリケーションにおけるすべてのランタイムでサポートされています。ホストおよび Lambda 関数が同じランタイムにある必要はありません。

ホストから Lambda 関数へのリクエストのトレース

トレース伝搬

サーバーレスの分散型非 HTTP トレース

必須セットアップ

Lambda 関数を非同期でトリガーする Node や Python のサーバーレスアプリケーションで、1 つのつながったトレースを見るには、追加のインスツルメンテーションが必要になることがあります。Datadog でサーバーレスアプリケーションの監視を始めたばかりであれば、こちらの主なインストール手順に従いトレースライブラリの選択に関するこのページをお読みくださいDatadog Lambda ライブラリを使って Lambda 関数から Datadog にトレースを送るようになったら、以下のようなケースではこれらの手順で 2 つの Lambda 関数間のトレースを繋ぐと良いかもしれません。

  • Step Functions で Lambda 関数をトリガーする
  • MQTT など HTTP 以外のプロトコルで Lambda 関数を呼び出す

多くの AWS Managed サービス (こちらを参照) のトレースは、最初からサポートされており、このページで説明されている手順を実行する必要はありません。

トレースを送信するリソース間でトレースコンテキストを正常に接続するために、以下のことが必要です。

  • Datadog のトレースコンテキストを発信イベントに含める。発信イベントは、dd-trace をインストールしたホストや Lambda 関数から発生させることができます。
  • コンシューマー Lambda 関数内のトレースコンテキストを抽出する。

トレースコンテキストの受け渡し

以下のサンプルコードでは、HTTP ヘッダーをサポートしないサービスや、Datadog が Node や Python でネイティブに対応していないマネージドサービスに対して、発信ペイロードでトレースコンテキストを渡す方法について説明しています。

Python では、get_dd_trace_context ヘルパー関数を使用して、Lambda 関数内の発信イベントにトレースコンテキストを渡すことができます。

import json
import boto3
import os

from datadog_lambda.tracing import get_dd_trace_context  # Datadog トレースヘルパー関数

def handler(event, context):
    my_custom_client.sendRequest(
        {
          'myCustom': 'data',
          '_datadog': {
              'DataType': 'String',
              'StringValue': json.dumps(get_dd_trace_context()) # 発信ペイロードにトレースコンテキストを含めます。
          },
        },
    )

Node では、getTraceHeaders ヘルパー関数を使用して、Lambda 関数内の発信イベントにトレースコンテキストを渡すことができます。

const { getTraceHeaders } = require("datadog-lambda-js"); // Datadog トレースヘルパー関数

module.exports.handler = async event => {
  const _datadog = getTraceHeaders(); // 現在の Datadog のトレースコンテキストをキャプチャします。

  var payload = JSON.stringify({ data: 'sns', _datadog });
  await myCustomClient.sendRequest(payload)

ホストから

Lambda 関数からトレースコンテキストを渡していない場合、getTraceHeadersget_dd_trace_context ヘルパー関数の代わりに以下のコードテンプレートを使用すると、現在のスパンコンテキストを取得することができます。すべてのランタイムでこれを行う方法については、ここで説明しています。

const tracer = require("dd-trace");

exports.handler = async event => {
  const span = tracer.scope().active();
  const _datadog = {}
  tracer.inject(span, 'text_map', _datadog)

  // ...

トレースコンテキストの抽出

上記のトレースコンテキストをコンシューマー Lambda 関数から抽出するには、Lambda 関数ハンドラーの実行前にトレースコンテキストをキャプチャするエクストラクター関数を定義する必要があります。これを行うには、エクストラクター関数の場所を指すように DD_TRACE_EXTRACTOR 環境変数を構成してください。フォーマットは <FILE NAME>.<FUNCTION NAME> です。例えば、json エクストラクターが extractors.js ファイルにある場合は、extractors.json となります。Datadog は、エクストラクターを複数の Lambda 関数で再利用できるように、エクストラクターメソッドを 1 つのファイルにまとめて配置することを推奨しています。これらのエクストラクターは、どんなユースケースにも合うように完全にカスタマイズ可能です。

:

  • TypeScript や webpack のようなバンドラーを使用している場合、エクストラクターが定義されている Node.js モジュールを import または require する必要があります。これにより、モジュールがコンパイルされ、Lambda のデプロイメントパッケージにバンドルされるようになります。
  • Node.js の Lambda 関数が arm64 上で動作する場合、環境変数 DD_TRACE_EXTRACTOR を使用する代わりに、関数コード内でエクストラクターを定義する必要があります。

サンプルエクストラクター

以下のコードサンプルでは、サードパーティシステムや標準的な HTTP ヘッダーをサポートしない API にトレースコンテキストを伝達するために使用するエクストラクターのサンプルについて説明します。

def extractor(payload):
    trace_headers = json.loads(payload["_datadog"]);
    trace_id = trace_headers["x-datadog-trace-id"];
    parent_id = trace_headers["x-datadog-parent-id"];
    sampling_priority = trace_headers["x-datadog-sampling-priority"];
    return trace_id, parent_id, sampling_priority
exports.json = (payload) => {
    const traceData = payload._datadog
    const traceID = traceData["x-datadog-trace-id"];
    const parentID = traceData["x-datadog-parent-id"];
    const sampledHeader = traceData["x-datadog-sampling-priority"];
    const sampleMode = parseInt(sampledHeader, 10);

    return {
      parentID,
      sampleMode,
      source: 'event',
      traceID,
    };
};
var exampleSQSExtractor = func(ctx context.Context, ev json.RawMessage) map[string]string {
    eh := events.SQSEvent{}

    headers := map[string]string{}

    if err := json.Unmarshal(ev, &eh); err != nil {
        return headers
    }

    // SQS を batchSize=1 のトリガーとして使用すると、1 つの SQS メッセージが
  // ハンドラーの実行を駆動するため、これをチェックすることが重要です。
    if len(eh.Records) != 1 {
        return headers
    }

    record := eh.Records[0]

    lowercaseHeaders := map[string]string{}
    for k, v := range record.MessageAttributes {
        if v.StringValue != nil {
            lowercaseHeaders[strings.ToLower(k)] = *v.StringValue
        }
    }

    return lowercaseHeaders
}

cfg := &ddlambda.Config{
    TraceContextExtractor: exampleSQSExtractor,
}
ddlambda.WrapFunction(handler, cfg)

X-Ray インテグレーションで Datadog にトレースを送信する

すでに X-Ray でサーバーレスアプリケーションをトレースしていて、X-Ray を引き続き使用したい場合は、AWS X-Ray インテグレーションをインストールして X-Ray から Datadog にトレースを送信できます。

その他の参考資料