🚜

分散トレーシングについて ~ 前提知識編 ~

2023/10/07に公開

概要

分散トレーシングについての前提となる知識をまとめてみました。

分散トレーシングとは

分散トレーシングとは、主にマイクロサービスのような分散アーキテクチャ環境において利用されるソリューションです。
分散アーキテクチャ環境では、 1 つのリクエストを返却するまでに、裏側で複数のマイクロサービスを呼び出す必要があり、障害時などに原因究明が困難になったり、システム全体の分析が難しくなるなどの問題があります。
そこで分散トレーシングを導入、 することで1 つのリクエストに対して一意のトレース ID を付与することによりそのリクエストがどのマイクロサービスと通信し、どこで処理に時間がかかっているのかなどの情報 ( トレース ) を可視化することが出来ます。

分散トレーシングのアーキテクチャ

分散トレーシングを実現するためには、それぞれのマイクロサービスがログを送信し、そのログを可視化する仕組み ( = 分散トレーシングプロバイダー ) が必要になります。
これを実現するためには、以下のコンポーネントが必要になります。
厳密には分散トレーシングプロバイダーによって構成が変わることもあるため、各プロバイダーの公式サイトをご覧ください。

  • ログを送信するレポーター
    • レポーターは分散トレーシングプロバイダーによっては各言語の SDK として提供してある場合もあります
    • Anthos 環境下では istio-proxy がここ担当しています
  • ログを保存するストレージ
    • Cloud Trace の場合は Cloud Logging が利用されています
  • トレースを可視化するインターフェース ( GUI )
    • Cloud Trace の場合は Cloud Trace のコンソール画面を指します

以下のような流れでレポーターからストレージに対してログを送信し、可視化インターフェースでトレース情報を可視化します。

分散トレーシングプロバイダー

分散トレーシングを実現するためのプロバイダーとしては Zipkin, Jaeger, Cloud Trace, Grafana Tempo などがあります。

https://landscape.cncf.io/card-mode?category=tracing&grouping=category

分散トレーシングにおけるトレース情報の構成要素

分散トレーシングにおける 1 つのトレースがどのような情報で構成されているかを記載します。

トレース

分散トレーシングでは 1 つのリクエスト全体をトレースと定義しています。
1 つのリクエストに対して 1 つのトレースが生成されます。

スパン

1 つのリクエストに対して内部で呼ばれている個々のリクエストをスパンと定義しています。
1 つのログが 1 つのスパンに紐付いており、各アプリケーションのレポーターから収集されます。

具体的な例を以下に示します。
以下のサンプルアプリケーションでは polaris を Gateway とし、 polaris から aldebaran, aquarius, http://google.com を直列で呼び出しています。
aldebaran は内部で http://google.com を呼んでいます。また、 aquarius は背後の sadalmelik を呼んでいます。
この例では、最上部の polaris.polaris.svc.cluster.local:8080/* ( Ingress 経由でユーザがアクセスするエンドポイント ) に対するリクエストが 1 つのトレースとなっており、 1 回のリクエストと紐付いています。
polaris から呼ばれている裏側のマイクロサービスごとのログがスパンとなっています。

※ スパンは一部省略しています。

分散トレーシングにおけるトレース ID の付与

分散トレーシングでは各マイクロサービスからのアクセスログがどのリクエストに紐付いているかを管理するためにそれぞれのログにトレース ID が紐付いている必要があります。そのため、各分散トレーシングプロバイダーのレポーターはトレース ID の発行を行う必要があります。
以下、 Anthos Service Mesh においてレポーターである istio-proxy でのトレース ID の付与に関してです。
istio-proxy がリクエストを受け取った時点でトレース ID が入っていない場合はトレース ID を付与します。トレース ID が入っている場合は、そのままそのトレース ID を利用します。トレース ID を付与した後に istio-proxy はストレージにログを送信します。

分散トレーシングプロバイダーとアプリケーションの責務について

分散トレーシングプロバイダーの責務

分散トレーシングプロバイダーでは、新規トレース ID の付与、レポーターから吐き出されたログやトレース情報を保存、可視化を責務としています。
トレース ID を元に特定のリクエストに紐付いたトレースの可視化を行います。
このとき用いられるトレース ID はプロバイダーによって異なるため、分散トレーシングを行いたいプロバイダーによってトレース ID の識別子を使い分ける必要があります。
レポーターが付与するトレース ID の識別子 ( 例: x-trace-id ) は分散トレーシングプロバイダーによって異なるため、利用する分散トレーシングプロバイダーのドキュメントをご覧ください。
Cloud Trace の場合、 x-cloud-trace-context を識別子として用いています。 [1]

アプリケーションの責務

レポーターでは、そのアプリケーションに対する接続リクエストと、そのアプリケーションから発行されるリクエストの対応付けを行うことが出来ません。
アプリケーションで他のアプリケーションを呼び出す場合は、入ってきたリクエストに付与されているトレース ID を伝播させるために、呼び出しリクエストのヘッダーにトレース ID を載せ替える必要があります。この載せ替えを行わないと、呼び出し先のアプリケーションで新規にトレース ID が発行されてしまい、別のトレースとしてデータが登録されてしまいます。
このときに実際に載せ替えるヘッダーの識別子は分散トレーシングプロバイダーによって異なるため、実装時には分散トレーシングプロバイダーが使用しているヘッダーを載せ替える必要があります。

その他

サンプリングアルゴリズムについて

分散トレーシングソリューションの多くは、リクエストの中から特定の割合のみをトレースとして可視化するためのサンプリング機能が備わっています。
istio-proxy ( Envoy ) の場合、 Head-based sampling を行っています。
Head-based sampling とは、 Envoy がアクセスを受け取った時点で既にトレース情報が付与されている場合、サンプリング対象には含めず、呼び出し元 ( Head ) の意思を尊重しトレースを行うというアルゴリズムです。
これにより、呼び出し元でトレースされているのに、呼び出し先のサンプリングによりトレース情報が途中で消えてしまうことを防ぐことが出来ます。

Grafana Tempo を分散トレーシングプロバイダーとして利用する方法

Grafana Tempo は Zipkin として istio-proxy の trace オプションを指定することで Anthos の分散トレーシングプロバイダーとして利用することが出来ます。

https://grafana.com/blog/2021/08/31/how-istio-tempo-and-loki-speed-up-debugging-for-microservices/

※ Cloud Trace と Grafana Tempo では分散トレーシングプロバイダーが利用しているトレース ID の識別子が異なるため、アプリケーション側で実装している Middleware の切り替えも必要になります。

※ 同時に複数の分散トレーシングプロバイダーを利用することは出来ません。

参考リンク

脚注
  1. https://istio.io/latest/docs/tasks/observability/distributed-tracing/overview/#trace-context-propagation ↩︎

Discussion