Datadog APMで実現するサーバーレスアーキテクチャの分散トレーシング

OGP

はじめに

こんにちは。ブランドソリューション開発部 プロダクト開発チームの杉田です。Fulfillment by ZOZO(以下、FBZ)が提供するAPIシステムの開発・運用を担当しています。

本記事では、サーバーレスアーキテクチャを採用しているFBZのAPIを例に、Datadog APMを使った分散トレーシングの導入手順と運用する際のポイントを紹介します。

「サーバーレスアーキテクチャを採用しているけど分散トレーシングを導入していない」という方や、「既にDatadogは活用しているけどAPMの機能は使っていない」という方に読んでいただけると幸いです。

FBZにおけるサービス監視

FBZでは、CloudWatchメトリクスやAWS Lambda、API Gatewayのログを解析し、PagerDutyやDatadogなどの外部サービスに連携して監視をしています。最近では、Lambda Destinationsの活用や頻度ベースによるアラート通知を実施したことで、サービス監視に要していた運用工数の削減を実現しました。

運用改善のためにアラート最適化を行った事例を過去記事で紹介しているので、併せて御覧ください。 techblog.zozo.com

サーバーレス監視の課題

アラート通知の最適化により、サービス監視を効率的に行える環境を構築できました。その結果、APIにリクエストが送られてからレスポンスを返すまで、以下のような処理の流れの構成になっています。

処理フローの概略図

しかし、この処理の流れを起因とする課題も残っていました。サーバーレスアーキテクチャでは、複数のサービスを組み合わせて構築するため、1サービスだけのログや個々のメトリクスだけではボトルネックの特定は困難です。特に、FBZ APIでレスポンスの遅延やタイムアウトが発生した場合、AWS LambdaやAPI Gatewayなど複数ログをまたがって確認する必要があり、調査しづらいという課題がありました。

分散トレーシングの導入

前述の課題を解決するために、Datadog社が提供しているDatadog APMというサービスを導入しました。分散トレーシングを実現するためのツールはいくつかありましたが、以下の理由でDatadog APMの採用に至りました。

  • Serverless Frameworkのインテグレーションが存在した
  • 複数のAWSアカウントによる運用をしていたため、各アカウントの情報を一元管理してシームレスにサービス監視できる環境を作りたかった
  • 既にログ監視でDatadogを使っており、社内にDatadogに関するノウハウがあった

Datadog APMのセットアップ

本章では、Datadog APMのセットアップ手順を紹介します。

Datadogのマニュアルに従ってセットアップを行います。

AWSインテグレーションのインストール

AWSインテグレーションを設定することで、DatadogがCloudWatchからLambdaメトリクスを取り込むことができるようになります。

インテグレーションのインストールを行う過程で、Datadog Forwarderと呼ばれるLambdaが作成されます。このDatadog ForwarderがAWSの各種サービスの情報をDatadogに対して送信します。

なお、こちらのページからもDatadog Forwarderのインストールを行えますが、AWSインテグレーションのインストールで既に作成されている場合はスキップしてください。

Datadog Serverless Pluginの設定

FBZのAPIはServerless Frameworkを使って開発しています。DatadogがServerless Framework向けのPluginであるDatadog Serverless Pluginを提供しているので、それを利用します。このプラグインはメトリクス、トレース、ログをDatadogに送信するLambdaレイヤーを作成します。

Datadog Serverless Pluginのセットアップは、こちらを参考にして実施します。

下記3つの手順によって、Datadog Serverless Pluginのインストールと設定が可能です。

# Datadog Serverless Pluginのインストール
$ yarn add --dev serverless-plugin-datadog
# serverless.yml
# プラグインを追加
plugins:
    - serverless-plugin-datadog
# serverless.yml
# セクションを追加
custom:
    datadog:
        addExtension: true
        apiKey: # Your Datadog API Key goes here.

その他、プラグインに関する詳細はDatadogのGitHubリポジトリにまとめられています。

以下に示す設定例は、上記リポジトリに記載されているものです。ログレベルやタグの付与に関する設定が可能です。

# serverless.yml
# パラメータの例
custom:
    datadog:
        flushMetricsToLogs: true
        apiKey: "{Datadog_API_Key}"
        apiKMSKey: "{Encrypted_Datadog_API_Key}"
        addLayers: true
        logLevel: "info"
        enableXrayTracing: false
        enableDDTracing: true
        forwarderArn: arn:aws:lambda:us-east-1:000000000000:function:datadog-forwarder
        enableTags: true
        injectLogContext: true
        exclude:
            - dd-excluded-function

以上の設定で、分散トレーシングができるようになります。

セットアップ時の注意点

実際にDatadog APMをセットアップしていく中で、いくつか注意すべき点を発見しました。本章では、その注意点を説明します。

リージョンの統一

AWSインテグレーションのインストール時には、CloudFormationを使ってスタックを作成します。その際、リージョンがデフォルトでは us-east-1 となっているため、必要に応じて監視対象のLambda関数と同じリージョンに変更する必要があります。この設定を間違えると、DatadogとAWSの連携ができなくなるので、再度セットアップをやり直すことになります。

リージョンの設定

ログとトレースの接続

アプリケーションから出力されるログとトレースした情報を接続するには、以下の2つの作業が必要となります。

  • アプリケーション側
    • ログにトレースIDを挿入
  • Datadog側
    • パイプラインの設定

トレースIDの挿入

Datadog Serverless Pluginのオプションで injectLogContext: true とすると、ログにトレースIDやスパンIDが自動で挿入されます。しかし、CloudWatchに出力しているログフォーマットを独自に設定している場合、それらの設定が上書かれて異なるログフォーマットとなってしまいます。

FBZのAPIではログの可読性向上を目的として、LambdaのログフォーマットをLTSV形式の独自フォーマットに変更しています。そのため、こちらを参考にし、手動で分散トレースに必要なログを設定しました。

# FBZで利用しているLTSV形式のログフォーマット
FORMAT = (
    # 省略
    '\tdatadog:[dd.trace_id=%(dd.trace_id)s dd.span_id=%(dd.span_id)s]' # 接続のために追加
    # 省略
    )

パイプラインの設定

前述のように、ログの設定をしただけではトレースとの接続はできません。手動で設定したログを、Datadogの共通形式にするために、ログのデータ構造を変換する必要があります。こちらを参考に設定しました。

AWSインテグレーションをインストールした際に自動作成されたパイプラインは編集できないので、クローンして新たなパイプラインを作成します。

デフォルトのプロセッサをクローン

作成したパイプラインの中から、Grok Parser: Parsing Lambda logsという名前のプロセッサーを選択して編集します。

パイプライン内のプロセッサ一覧

Log samplesの5つの項目のいずれかにログのサンプルをセットし、マッチするかを確認しながらパースのルールを書いていきます。この際にヘルパールールを用いることで目的ごとに名付けができます。

# LTSV形式をパースするルール例
# Sample Rule
sample_rule %{datadog_trace}

# Helper Rules
datadog_trace datadog:(\[dd.trace_id=%{word:dd.trace_id} dd.span_id=%{word:dd.span_id}\])

この設定が完了すると、ログとトレースの接続が実現されます。

ログとトレースの接続

運用上でのポイント

次に、実際に運用する中で得られたポイントとなる点を紹介します。

タグの活用

Datadogでは env service versionの3つのタグが予約済みタグとして利用されています。公式ドキュメントでは、タグを使うことで以下の3つが可能となると書かれています。

バージョンでフィルタリングされたトレースおよびコンテナメトリクスでデプロイへの影響を特定する
一貫性のあるタグを使用して、トレース、メトリクス、ログ間をシームレスに移動する
Datadogアプリ内で統一された方法で環境またはバージョンに基づいてサービスデータを表示する

公式ドキュメント より引用

FBZでも、タグを使ってフィルタすることで検索性が向上したり、APIやBatchといった処理系ごとの処理時間の傾向などを確認できるようになりました。

タグの自動付与

Datadog Serverless Pluginをインストールすると、serverless.ymlに定義している値からLambdaに対して自動的にタグ付けをしてくれます。プラグインの機能を使うと各Lambdaで重複するタグを書かなくてもよくなるので、定義ファイルへの記述を減らすことができます。

# serverless.yml
service: service-name
provider:
    name: aws
    stage: prod # Lambdaのenvタグに「prod」が付与される

plugins:
    - serverless-plugin-datadog

functions:
    hello:
        handler: handler.hello
        # この関数は、プラグインによって上記で構成されたサービスレベルのタグを継承する
        # tags:
            #env: provider.stageの値が自動付与される
            #service: serviceの値が自動付与される
    world:
        # この関数は、タグを上書きする
        handler: handler.users
        tags:
            env: "<ENV>"
            service: "<SERVICE>"

タグに関する詳細は公式ドキュメントを御覧ください。

まとめ

Datadog APMのServerless Frameworkへの導入方法から運用する際のポイントを紹介しました。

サーバーレスアーキテクチャで運用している場合、内部でいくつものサービスを経由して処理が進みます。そのため、サービス間の依存関係やパフォーマンス面で問題が発生した場合、調査が困難になることが多いです。その課題の解決のために、Datadog APMを導入することで既存のコードへの修正は最小限に抑えながら、分散トレーシングを実現できました。

サービス毎のレイテンシ可視化

分散トレーシングを実現できたことで、上図のようにタイムアウトが発生した際のボトルネックの箇所の特定が容易になり、各サービスのレイテンシを可視化できるようになりました。

さいごに

ZOZOテクノロジーズでは、サーバーレスアーキテクチャやAWSのマネージドサービスを活用しサービスを成長させていきたい仲間を募集中です。ご興味のある方は、以下のリンクからぜひご応募ください!

tech.zozo.com

カテゴリー