コンテナ、サーバーレスプログラミングの出現により、ソフトウェアの配信とデプロイの効率が大幅に向上しました。 アーキテクチャの進化で次の 2 点が変わりました。
- アプリケーションアーキテクチャは、単一のシステムからマイクロサービスに変わりつつあり、 ビジネスロジックはマイクロサービス間の呼び出しや、リクエストに変化しています。
- リソースに関しては、従来の物理サーバーは、次第に少なくなってきており、目に見えない仮想リソースに変化しています。

前述の 2 つの変化は、柔軟で標準化されたアーキテクチャが柔軟の一方、運用管理 (O&M) と診断の要件がますます複雑化していることを示しています。これらの変化に対応するために、集中ログシステム (ロギング)、集中測定システム (メトリック)、および分散トレースシステム (トレーシング) を含む一連の DevOps 向けの診断および分析システムが登場しました。
ロギング、メトリック、およびトレーシング
ロギング、メトリック、トレーシングの特徴は次のとおりです。
- ロギングにより、あらゆるイベントが記録されます 。
たとえば、問題診断の基礎となるアプリケーションのデバッグ情報またはエラー情報です。
- メトリックにより、集約可能なデータを記録します。
たとえば、キューの現在の深さをメトリックとして定義し、要素がキューに追加またはキューから削除される際に更新します。 HTTP リクエスト数は カウンターとして定義されることができ、新しいリクエストを受信するたびに累積されます。
- トレーシングにより、リクエスト範囲内で情報を記録するために使用されます 。
たとえば、リモートメソッドを1回呼び出しするプロセスと消費時間。これは、システムパフォーマンスを調査するために使用されるツールです。ロギング、メトリック、および、トレーシングは、下図のように重なり合う部分があります。

以上から、既存のシステムを分類することができます。たとえば、Zipkin は Tracing に特化しています。プロメテウスはメトリックに特化し始めており、より多くのトレーシング機能が実装されていく可能性は高いですが、ロギングにはあまり関心がないようです。ELK や Alibaba Cloud Log Service といったシステムはロギングに特化し始めていますが、他の機能も継続的に統合されており、上図の中心に向かっています。
詳細については、メトリック、トレーシング、ロギングをご参照ください。次は、トレーシングシステムの紹介です。
トレーシングの技術的背景
トレーシング技術は 1990 年代から存在していました。Google 記事「Dapper, a Large-Scale Distributed Systems Tracing Infrastructure」により、この分野は主流となりました。また、記事「Uncertainty in Aggregate Estimates from Sampled Distributed Traces」には、サンプリングに関して詳細に分析されています。左記記事の発表により、優れたトレーシングソフトウェアプログラムを開発するグループが多く現れました。
よく利用されている製品
- Dapper (Google): あらゆる Tracer の基盤
- StackDriver Trace (Google)
- Zipkin (Twitter)
- Appdash (golang)
- EagleEye (Taobao)
- Ditecting (Pangu、Alibaba Cloud のプロダクトで使用される Tracing システム)
- Cloud Map (Ant Tracing システム)
- sTrace (Shenma)
- X-ray (AWS)
分散トレーシングシステムは急速に発展しており、種類も数多くあります。 ただし、いずれも共通してコードトラッキング、データストレージ、およびクエリの表示の 3 ステップがあります。


データが収集され保存されると、分散型トレーシングシステムは通常、タイムラインを含むタイミング図としてこの Trace を提示する。 ただし、データ収集プロセスでは、システムがユーザーコードに割り込まなければならない、 その上、異なるシステム間の API 互換性がないため、トレーシングシステムを切り替え流には、大きな変更を加える必要があります。
OpenTracing

- OpenTracing は CNCF に入られており、グローバル分散トレースシステムの統一コンセプトとデータ標準を提供しています。
- OpenTracing は、プラットフォームやベンダーに依存しない API を提供しているため、開発者はトレーシングシステムを容易に実装 (または変更) できます。
OpenTracing のトレーシング (呼び出しチェーン) はこの呼び出しチェーン内の Span によって暗黙的に定義されます。具体的には、Trace (呼び出しチェーン) は 複数の Span で構成される有向非巡回グラフ (DAG) と見なすことができます。Span と Span との関係は References と呼ばれます。
たとえば、次の trace には 8 つの Span で構成されています。
単一の trace の Span 間における因果関係
[Span A] ←←←(the root span)
|
+------+------+
| |
[Span B] [Span C] ←←←(Span C は Span A の子ノード、ChildOf)
| |
[Span D] +---+-------+
| |
[Span E] [Span F] >>> [Span G] >>> [Span H]
↑
↑
↑
(Span G は Span F の後に呼び出されます、 FollowsFrom)
次の例に示すように、タイムラインに基づくタイミング図が、Trace (呼び出しチェーン) をより適切に表示できることがあります。
単一の trace の Span 間における時間的関係。
––|–––––––|–––––––|–––––––|–––––––|–––––––|–––––––|–––––––|–> time
[Span A···················································]
[Span B··············································]
[Span D··········································]
[Span C········································]
[Span E·······] [Span F··] [Span G··] [Span H··]
各 Span には以下が示されます。
- 操作名
- 開始時間
- 終了時間
- Span タグ。キーと値のペアで構成された Span タグのコレクション。キーは文字列でなければなりません。値は文字列、ブール値、または数値にすることができます。
- Span log。Span ログのコレクション。各ログ操作には、キー、値のペア、及びタイムスタンプが 1 つずつ含まれます。キーは文字列でなければなりません。値は任意のデータ型にすることができます。 ただし、OpenTracing をサポートするすべての Tracer が すべてのデータ型をサポートする必要があるわけではないことにご注意ください。
- SpanContext (Span 内容)
- References (Span 間の関係)。 関連しているゼロまたは複数の Span (Span の間では、SpanContext を通じて関係を確立します)。
各 SpanContext には、次のステータスが含まれます。
- いかなる OpenTracing 実装も、一意の Span に基づいてプロセスの境界を越えて現行の呼び出しチェーンのステータス (Trace ID と Span ID など) を送信する必要があります。
- Baggage items (Trace に付属するデータ)。Trace に保存されているキーと値のペアのコレクションであり、プロセスの境界を越えて送信する必要があります。
OpenTracing データモデルの詳細については、OpenTracing のセマンティック標準をご参照ください。
関数の実装
サポートされている Tracer の実装にはすべての OpenTracing の実装が一覧表示されています。Jaeger と Zipkin の実装が最も一般的です。
Jaeger
Jaeger は、Uber によりリリースされたオープンソースの分散トレースシステムです。OpenTracing API と互換性があります。
アーキテクチャ

上図に示すように、Jaeger は次のコンポーネントで構成されています。
- Jaeger client: 異なる言語の OpenTracing 標準に準拠した SDK を実装します。 アプリケーションは API を使用してデータを書き込みます。 Client Library は、 アプリケーションで指定されたサンプリングポリシーに従って Trace 情報を jaeger-agent に送信します。
- Agent: UDP ポートが受信した Span データをモニタリングし、そのデータをまとめて collector に送信するネットワークデーモン。 Agent は基本コンポーネントとして設計されており、すべてのホストにデプロイされています。 Agent は Client Library と collector を分離し、 Client Library を ルータ、および Collector 詳細の検出から遮断します。
- Collector: jaeger-agent によって送信されたデータを受信してから、そのデータをバックエンドストレージに書き込みます。 Collector はステートレスコンポーネントとして設計されています。 そのため、任意の数の jaeger-collectors を同時に 実行できます。
- Data store: バックエンドストレージは、Cassandra と Elasticsearch へのデータ書き込みをサポートするプラグイン可能なコンポーネントとして設計されています。
- Query: クエリリクエストを受信し、バックエンドストレージシステムから Trace 情報を取得して UI に表示します。 Query はステートレスで、複数のインスタンスを起動できます。 インスタンスを Nginx などの ロードバランサの後にデプロイできます。
Jaeger on Alibaba Cloud Log Service

利点
- ネイティブ Jaeger は Cassandra と Elasticsearch へのデータの永久保存のみをサポートします。ユーザーはバックエンドストレージシステムの安定性を保守し、ストレージ容量を調整する必要があります。 Jaeger on Alibaba Cloud Log Service は、 Log Service の大量データ処理機能を利用しているため、ユーザーはバックエンドストレージシステムの問題を意識することなく、分散トレース機能の利便性を享受できます。
- Jaeger UI は、クエリおよび Trace 表示機能のみを提供し、問題分析およびトラブルシューティングを十分に行うことができません。Jaeger on Alibaba Cloud Log Service を使用することで、Log Service の強力なクエリおよび分析機能を利用して、システム内の問題を迅速に分析することができます。
- バックエンドストレージに Elasticsearch を使用する Jaeger と比較し、Log Service は従量課金に対応しているため、そのコストは Elasticsearch のコストのわずか 13% となります。詳細については、「自己構築 ELK vs Log Service (SLS)」をご参照ください。
手順
詳細については、 GitHub をご参照ください。
設定例
HotROD は、複数のマイクロサービスで構成されたアプリケーションで、OpenTracing API を使用して Trace 情報を記録します。
Alibaba Cloud Log Service で Jaeger を使用して HotROD の問題を診断するには、次の手順に従ってください。 ビデオには次の内容が含まれます。
- Log Service の設定
- Jaeger を起動 (docker-compose コマンドを実行)
- HotROD の実行
- Jaeger UI で Trace 情報の取得
- Jaeger UI での詳細な Trace 情報表示
- Jaeger UI でのアプリケーションパフォーマンスのボトルネック特定
- Log Service コンソールでのアプリケーションパフォーマンスのボトルネック特定
- アプリケーションの OpenTracing API 呼び出し
構成チュートリアル
http://cloud.video.taobao.com//play/u/2143829456/p/1/e/6/t/1/50081772711.mp4
例では、次のクエリステートメントを使用します。
- 1 分ごとにフロントエンドサービスの HTTP GET / dispatch 操作の平均待ち時間およびリクエスト数をカウント
process.serviceName: "frontend" and operationName: "HTTP GET /dispatch" | select from_unixtime( __time__ - __time__ % 60) as time, truncate(avg(duration)/1000/1000) as avg_duration_ms, count(1) as count group by __time__ - __time__ % 60 order by time desc limit 60
- 2 つの Trace 操作の所要時間を比較
traceID: "trace1" or traceID: "trace2" | select operationName, (max(duration)-min(duration))/1000/1000 as duration_diff_ms group by operationName order by duration_diff_ms desc
- 待ち時間が 1.5秒 を超える Trace の IP アドレスをカウント
process.serviceName: "frontend" and operationName: "HTTP GET /dispatch" and duration > 1500000000 | select "process.tags.ip" as IP, truncate(avg(duration)/1000/1000) as avg_duration_ms, count(1) as count group by "process.tags.ip"