分散トレーシング、OpenTracingとElastic APM

マイクロサービスの世界

マイクロサービスアーキテクチャを採用する企業はますます増えています。それらの企業では毎日、マイクロサービスを開発し、デプロイしています。そのようなサービスは多くの場合、異なるプログラミング言語で開発され、個別のランタイムコンテナーにデプロイされ、さまざまなチームおよび組織によって管理されています。Twitterのような大企業では何万ものマイクロサービスを持ち、それらのすべてを活用してビジネス目標の達成に取り組むことができます。Twitterが同社のブログ投稿で述べているとおり、多様なサービストポロジーの健全性とパフォーマンスに対する可視性は、同社が問題の根本原因を迅速に判断し、Twitterの全体的な信頼性と効率性を向上させるためにきわめて重要です。

そのために役立つのが分散トレーシングです。分散トレーシングはマイクロサービスが直面する次の2つの根本的な課題に役立ちます。

  1. レイテンシの追跡
    1人のユーザーのリクエストまたはトランザクションは、異なるランタイム環境のさまざまなサービスを経由します。特定のリクエストに対する各サービスのレイテンシを把握することは、システム全体としての総合的なパフォーマンス特性を理解し、改善の可能性に関する貴重なインサイトを得るために不可欠です。
  2. 根本原因の分析
    根本原因の分析は、マイクロサービスの大規模なエコシステム上に構築されているアプリケーションにとって、さらに大きな課題です。どのような問題がどのサービスで、どのようなタイミングで発生するか分かりません。分散トレーシングは、そのようなシステムにおける問題をデバッグする際にきわめて重要です。

客観的に見た場合、トレーシング可観測性の3本の柱の1つに過ぎません。3本の柱とは、ロギング、メトリック、トレーシングです。後で手短に説明しますが、Elastic Stackは可観測性の3つの柱すべてに対応する統合プラットフォームです。ログ、メトリック、およびAPMデータが同じリポジトリに保存され、分析され、相関付けられていれば、ビジネスアプリケーションおよびシステムに関する最もコンテキストが豊富なインサイトを獲得することができます。このブログでは、トレーシングのみに焦点を当てます。

Elastic APMでの分散トレーシング

Elastic APMは、Elastic Stack上に構築されたアプリケーションパフォーマンス監視システムであり、ソフトウェアサービスとアプリケーションのリアルタイムでの監視、受信リクエストへの応答時間に関する詳細なパフォーマンス情報の収集、データベースに対するクエリ、キャッシュの呼び出し、外部HTTPへのリクエストなどが可能です。Elastic APMのエージェントが、すぐに使える機能豊富な自動インストルメンテーション機能(タイミングデータベースクエリなど)を、サポートされているフレームワークおよびテクノロジーに提供します。また、独自の目的のためにカスタムインストルメンテーションを使用することもできます。これによって簡単にパフォーマンスの問題を迅速かつ正確に特定し、修正できます。

Elastic APMは分散トレーシングをサポートし、OpenTracingに準拠しており、単一のビューでマイクロサービスアーキテクチャ全体のパフォーマンスを分析できます。Elastic APMは、すべてのリクエストを最初のWebリクエストからフロントエンドサービスまで、そしてバックエンドサービスに実行されるクエリまで追跡することで、それを達成します。これにより、さらに簡単かつ迅速に、アプリケーション全体におけるボトルネックの可能性を見つけることが可能になります。APM UIでのタイムラインのビジュアライゼーションでは、トレースに接続されている各サービスからのすべてのトランザクションのウォーターフォールビューが表示されます。

また、Elastic Stackはログの集約とメトリック分析にも適したプラットフォームです。ログ、メトリック、およびAPMトレースのすべてをElasticsearch内に保存およびインデックスすれば非常に強力です。インフラストラクチャメトリック、ログ、トレースなどのデータソースを素早く相関付けできれば、より迅速に根本原因をデバッグできます。APM UIでトレースを表示すると、[Actions]メニューをクリックして素早くホストまたはコンテナーメトリックおよびログにアクセスできます(メトリックおよびログも収集されている場合)。

誰もがElastic APMを使用してアプリケーションとサービスのインストルメンテーションを実行していれば素晴らしいのですが、Elastic APMは現在利用できる唯一の分散トレーシングソリューションではありません。ZipkinやJaegerなど、他にも人気のあるオープンソーストレーサーがあります。マイクロサービスの世界では、ポリグロット(多言語)プログラミングやポリグロットパーシステンスのような概念がよく知られており、広く受け入れられています。それと同様に、「ポリグロットトレーシング」がより一般的になっていくでしょう。独立、分離といったマイクロサービスの性質により、異なるサービスの責任者は異なるトレーシングシステムを使用する可能性があります。

開発者にとっての課題

さまざまなトレーシングシステムが利用できるため、開発者はまさに課題に直面しています。結局のところ、トレーサーはアプリケーションコード内に存在します。一般的な課題には次のようなものがあります。

  1. どのトレーシングシステムを使いますか?
  2. トレーサーを変更したい場合はどうなるのでしょうか?ソースコード全体を変更したくはありません。
  3. 異なるトレーサーを使用している可能性のある共有ライブラリについてはどうすればよいですか?
  4. サードパーティのサービスが異なるトレーサーを使用している場合はどうなるのでしょうか?

当然のことながら、これらの懸念事項に対処するには標準化が必要です。現在の私たちが標準化のどの段階にいるかを考える前に、少し前に戻り、アーキテクチャ面から包括的に分散トレーシングについて見てみましょう。そして、分散トレーシングの「最高の状態」を達成するために何が必要かについて理解します。

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

最新のソフトウェアシステムは、いくつかの高レベルのコンポーネントに分解されます。それらは通常、異なる組織によって設計および開発され、次のような異なるランタイム環境で実行されます。

  • 独自のアプリケーションコードとサービス
  • 共有されたライブラリとサービス
  • 外部サービス

そのようなシステムを分散トレーシングで包括的かつ統合的に監視するためには、4つのアーキテクチャコンポーネントが必要です。

  1. 標準化された分散トレーシングAPI。ベンダー非依存型の標準化されたトレーシングAPIにより、開発者は標準化された方法でコードのインストルメンテーションを実行できます。実行時にどのトレーサーの使用を選択しても問題はありません。これがすべての最初のステップになります。
  2. トレーシングコンテキストの標準化された定義と伝達。トレースが1つのラインタイムから別のランタイムに移る場合、その両者がトレーシングコンテキストを理解する必要があり、そのコンテキストを伝達する標準化された方法が必要です。コンテキストには少なくとも、トレースIDが含まれます。
  3. トレーシングデータの標準化された定義。1つのトレーサーからのトレースデータを別のトレーサーが理解し、使用する場合には、トレースデータに標準化された拡張可能なフォーマットが必要です。
  4. 相互運用可能なトレーサー。最後に、100%のランタイム機能を達成するには、さまざまなトレーサーが、別のトレーサーからトレースデータをオープンな方法でエクスポートおよびインポートするメカニズムを提供する必要があります。理想としては、Jaegerなどのトレーサーでインストルメンテーションされた共有のライブラリまたはサービスがそのトレーシングデータを、設定変更を通じてJaegerエージェント経由で直接Elastic APMまたはその他のトレーサーに送信できることです。

では、OpenTracingに進みましょう。

OpenTracingの仕様

OpenTracingの仕様の定義は、分散トレーシング向けのオープンなベンダー非依存型APIです。そのためユーザーは、OpenTracingの実装をいつでも変更することができ、ベンダーへのロックインを回避できます。また、フレームワークおよび共有ライブラリの開発者は、標準的な方法ですぐに使えるトレーシング機能を提供することができます。これにより、フレームワークおよびライブラリに対するより優れたインサイトを獲得できるようになります。UberやYelpなどのWebスケールの企業は、OpenTracingを使用して、会社の高度に分散した動的なアプリケーションに対する詳細な可視性を得ています。

OpenTracingデータモデル

OpenTracingの基本的な概念と基盤となるデータモデルはGoogleのDapperです。主要な概念はトレースとスパンです。

  1. トレースは、分散システムを通過するトランザクションを表します。スパンの有向非巡回グラフと考えることができます。
  2. スパンは、作業の論理的ユニットです。名前、開始時間、継続時間を持ちます。関係をモデル化するために、スパンはネストされ、順序付けられます。スパンは、キー/値タグと、特定のスパンインスタンスに付加されタイムスタンプが付いた細かい構造ログも受け入れます。
  3. トレースコンテキストは、分散トランザクションを伴ったトレース情報です。その情報には、ネットワークまたはメッセージバスでサービスからサービスに渡された時間が含まれています。コンテキストには、トレース識別子、スパン識別子、およびトレーシングシステムがダウンストリームのサービスに伝達する必要があるその他のデータが含まれます。

それらの全体像

さまざまな組織が開発し、稼働させているカスタムアプリケーションコード、共有ライブラリ、共有サービスからのトレーシング情報は、標準化によって、交換可能でランタイム互換性があることが理想的です。それらの各コンポーネントがどのトレーサーの使用を選択するかは問題ではなくなります。

しかしOpenTracingは、前述の4つのアーキテクチャコンポーネントのうちの最初のコンポーネントにしか対応しません。そこで、他のコンポーネントに関して現在の私たちはどのような状態であり、今後はどうなるのでしょうか。

現在の状態

前述のとおり、OpenTracingは、異なるトレーサー向けのトレーシングAPIの標準セットを定義し、実装します。これは優れた開始点であり、役立つものです。しかし、トレーシングコンテキスト、およびトレーシングデータに互換性があり、交換可能なものになるには、やはりそれらの標準化が必要です。

  1. OpenTracing APIはAPIの標準セットを提供する。これが現在の私たちにとってほぼ唯一の標準化です。仕様にも制約があります。たとえば、すべてのプログラミング言語をカバーしていません。とは言え、これは素晴らしい取り組みであり、さらに勢いを増しています。
  2. トレーシングコンテキストの標準化された定義はまだないW3C Distributed Tracingワーキンググループでは、現在、トレーシングコンテキストの定義の標準化に取り組んでいます( W3C Trace Context仕様)。この仕様は、分散システム内でのコンテキストおよびイベント相関付けに対する統合アプローチを定義しており、異なる監視ツール間に渡って、分散アプリケーション内でエンドツーエンドのトランザクショントレーシングが可能となるものです。Elastic APMは、分散トレーシングのためのHTTPヘッダーフォーマットの標準化に向けたW3C Trace Contextワーキンググループの取り組みをサポートしています。当社のエージェント実装はTrace Contextドラフトの仕様にほぼ従っており、最終的に決定した仕様を完全にサポートする予定です。

    現在のトレーシングコンテキストの非互換性の例として、Elastic APMとJaegerがトレースIDとして使用しているHTTPヘッダーを以下に示します。ご覧のとおり、IDの名前とエンコーディングの両方が異なっています。異なるトレーシングヘッダーが使用されていると、各トレーシングツールの境界を越えたときにトレースは破損します。

    Jaeger:
    uber-trace-id:118c6c15301b9b3b3:56e66177e6e55a91:18c6c15301b9b3b3:1

    Elastic APM:
    elastic-apm-traceparent:00-f109f092a7d869fb4615784bacefcfd7-5bf936f4fcde3af0-01

    定義自体以外にも課題はあります。たとえば、HTTPヘッダーのすべてがサービスインフラやルーターなどによって自動的に転送されるわけではないことです。ヘッダーがドロップすると常にトレースは破損します。
  3. トレーシングデータの標準化された定義はまだない。W3C Distributed Tracingワーキンググループによると、トレースの相互運用性に関する2つ目の問題は、「トレース全体またはトレースの断片などのトレースデータを、さらなる解釈のためにさまざまなツールに共有できる標準化された拡張性のあるフォーマット」です。ご想像のとおり、多数のオープンソースおよび営利企業が関与し、標準のフォーマットに合意することは簡単なことではありません。近いうちに実現することを願っています。
  4. トレーサーはランタイム互換性がない。前述の理由のすべてと、システムをオープンにし、世界との互換性を持つようにすることにさまざまな動機があることによって、トレーサーは現在、実行時にお互いの互換性がありません。自信を持って言えることは、これは近い将来においても変わらずそのままだろうということです。

Elastic APMによる現在の他のトレーサーとの連携方法

現在、トレーサー間の完全な互換性の実現が近づいているとは言えませんが、落胆する必要はありません。 そのような現状でも、Elastic Stackはいくつかの異なる方法で他のトレーサーと連携できます。

  1. Elasticsearchを他のトレーサー向けのスケーラブルなバックエンドデータストアとして使用する。

    驚くことではありませんが、ElasticsearchはZipkinやJaegerなど、他のトレーサー向けのバックエンドデータストアとして使用されています。Elasticsearchにはきわめて高いスケーラビリティと豊富な分析機能があるからです。ZipkinまたはJaegerのトレーシングデータをElasticsearchに送信することは、両方とも簡単な設定で済みます。トレーシングデータをElasticsearchに送信すれば、Kibanaの強力な分析およびビジュアライゼーション機能を使用してトレーシング情報を分析し、アプリケーションパフォーマンスに対する詳細なインサイトを提供する魅力的なビジュアライゼーションを作成できます。
  2. Elastic OpenTracingブリッジ

    Elastic APM OpenTracingブリッジにより、OpenTracing APIを使用してElastic APMトランザクションおよびスパンを作成できます。つまり、OpneTracing APIの呼び出しをElastic APMに変換し、それによって既存のインストルメンテーションを再利用することが可能になります。たとえば、Jaegerによって実行された既存のインストルメンテーションを、コードのいくつかの行を変更することでElastic APMに単純に置き換えることができます。

    Jaegerによる最初のインストルメンテーション:

    import io.opentracing.Scope;
    import io.opentracing.Tracer;
    import io.jaegertracing.Configuration;
    import io.jaegertracing.internal.JaegerTracer;
    ...
    private void sayHello(String helloTo) {
        Configuration config = ...
        Tracer tracer = config.getTracer();
        try (Scope scope = tracer.buildSpan("say-hello").startActive(true)) {
            scope.span().setTag("hello-to", helloTo);
        }
        ...
    }
        
    JaegerをElastic OpneTracingブリッジに置き換える:

    import io.opentracing.Scope;
    import io.opentracing.Tracer;
    import co.elastic.apm.opentracing.ElasticApmTracer;
    ...
    private void sayHello(String helloTo) {
        Tracer tracer = new ElasticApmTracer();
        try (Scope scope = tracer.buildSpan("say-hello").startActive(true)) {
            scope.span().setTag("hello-to", helloTo);
        }
        ...
    }
        


    このシンプルな変更によって、その他のトレーシングコードを変更することなく、トレーシングデータを正常にElastic APMに送信できます。これこそがOpenTracingの力です。

Elastic APMリアルユーザー監視

トレーシングおよびコンテキストの伝達などを語る際には、バックエンドサービスに注目することがほとんどですが、クライアント側でトレースをブラウザで開始することには大きな価値があります。これを実行すると、ユーザーがブラウザで何かをクリックした瞬間に、トレース情報を得ることができます。このトレース情報は、パフォーマンスの側面からアプリケーションの「リアルユーザーエクスペリエンス」を表していることになります。ここでも残念なことに、現在ではその情報を転送する標準化された方法はありません。もちろんW3Cグループは今後、トレースコンテキストをブラウザまで拡張する予定です。

Elastic APMリアルユーザー監視(RUM)は、まさにその機能を現在提供します。RUM JSエージェントはクライアント側アプリケーション内のリアルユーザーエクスペリエンスを監視します。TTFB(Time to First Byte)、domInteractive、およびdomCompleteなどのメトリックを計測できるため、クライアントアプリケーション内のパフォーマンス問題、およびサーバー側アプリケーションのレイテンシに関連する問題を見つけるのに役立ちます。弊社のRUM JSエージェントはフレームワークに依存しません。つまり、JavaScriptベースの任意のフロントエンドアプリケーションで使用できます。

<p?</p?

まとめ

このブログが、分散トレーシングの現状の理解に少しでも役立ち、また、OpenTracingの現状に関するいくつかの誤解についても明確化できていることを願っています。最後に、このブログの内容をまとめます。

  1. 分散トレーシングは、マイクロサービスに関するきわめて貴重なパフォーマンスインサイトを提供します。
  2. OpenTracingは、業界における分散トレーシングの標準化に向けた最初の一歩です。完全な互換性を達成するまでには、まだ長い道のりが残されています。
  3. Elastic APMはOpenTracingに準拠しています。
  4. Elastic OpenTracingブリッジにより、インストルメンテーションの再利用が可能です。
  5. Elastic Stackは、現在完全なランタイム互換性はないものの、ZipkinやJaegerなどの他のトレーサーにとってスケーラブルかつ長期対応の優れたストレージです。
  6. Elasticは、Elasticのトレーシングデータかどうかに関わらず、豊富な分析機能を提供します。ZipkinまたはJaegerのトレーシングデータは簡単な設定でElasticsearchに送信できます。
  7. Elastic APMリアルユーザー監視(RUM)は、クライアント側アプリケーション内のリアルユーザーエクスペリエンスを監視します。
  8. 全体としてElasticは、可観測性の3つの柱(ロギング、メトリック、トレーシング)に対応するきわめてスケーラブルで機能豊富な統合分析プラットフォームです。

ディスカッションを開始する場合またはご質問がある場合は、Elastic APMフォーラムをご利用ください。ご希望通りのトレーシングが実行できることを願っています。