マイクロサービスのトレーサビリティを確保するための分散トレーシングとJaeger
こんにちは、shoito(しょいと)です。
今回はCloud Native Computing Foundation(CNCF)のプロジェクトリストを見ていて、気になった分散トレーシング関連プロジェクトのOpenTracingとJaegerについて紹介します。
分散トレーシングとOpenTracing
マイクロサービスのように、アプリケーションが複数のサービスで構成され複雑になると、1つのリクエスト(例えばREST API)が内部的に複数のサービスを跨いで処理されることになります。
そのため、リクエスト全体の流れを分析することが難しくなり、そのままではトレーサビリティの低下を招くことになります。
分散トレーシングシステムは、そのような分散したシステムのリクエストを分析するためのものであり、OpenTracingは分散トレーシングのためのベンダ非依存のAPI仕様とライブラリを提供しています。
分散トレーシングシステムのマネージド・サービスとしてはAWSが提供するAWS X-Ray、Google Cloudが提供するStackdriver Traceなどがあります。
Jaegerとは
Jaegerは、Uber TechnologiesがDapperやOpenZipkinにインスパイアされ開発・OSSした分散トレーシングシステムです。OpenTracing互換のデータモデルとInstrumentationライブラリを提供します。
JaegerはAgentとCollector, Queryのコンポーネントから構成され、以下を提供します。
- トレースする仕組み
- トレース結果をモニタリングする仕組み
Jaeger公式サイトのArchitecture解説より
技術仕様としては、以下のようになっています。
- バックエンドはGoによる実装
- ReactによるWeb UI実装
- サポートストレージバックエンド
- Cassandra 3.4+
- Elasticsearch 5.x, 6.x
- メモリストレージ
Jaegerを試してみた
動作確認環境
- OS: macOS Mojave(10.14.1)
- Docker: Docker for Mac Community Edition Version 18.06.1-ce-mac73 (26764)
JaegerバックエンドとJaeger UIを立ち上げる
まずはJaeger公式のGetting startedに従って、ローカルテスト用のAll-in-oneのDockerイメージを使って起動します。
あとで使うデモアプリケーションで不要なポートの分は-pオプションから省いています。
$ docker run -d --name jaeger \ -p 6831:6831/udp \ -p 16686:16686 \ jaegertracing/all-in-one:1.7 Unable to find image 'jaegertracing/all-in-one:1.7' locally 1.7: Pulling from jaegertracing/all-in-one a83feccf3672: Pull complete d518ddfa1466: Pull complete Digest: sha256:4e3779c7c1f39af39f998d3056e020a1e2e6ae5babc4aa41496bec4a73b261f0 Status: Downloaded newer image for jaegertracing/all-in-one:1.7 2c022c4b480267e71402ed50f7d7e7fac45a2e6d0457c420ffc6a6c380037397
コンテナが起動したら、ブラウザで http://localhost:16686
を開いてJaeger UIにアクセスします。
この段階では、データストアにはトレース情報がないため、Jaeger UIで見られるデータはありません。
$ open -a "Google Chrome" http://localhost:16686
上記の docker run
時に指定したポートは以下のような役割になっています。
ポート | プロトコル | コンポーネント | 機能 |
---|---|---|---|
6831 | udp | agent | jaeger.thrift エンドポイント(compact) |
16686 | http | query | Jaeger UIフロントエンドの提供 |
ちょっと気になったので設定情報を返すエンドポイントの 5778
番ポートに以下のようなGETリクエストを送ってみたところ、jaeger-query
サービスのサンプリングレートの設定値が取得できました。
サンプリングしないため、デフォルト値の1となっています。
$ curl "http://localhost:5778/?service=jaeger-query" {"strategyType":0,"probabilisticSampling":{"samplingRate":1}}
トレース対象のデモアプリケーション HotROD
HotRODはOpenTracingを使ったライドシェアアプリケーションです(テキストベースですが)。
画面上のボタンをクリックすると、ドライバーを呼び出し、乗客のところへ配車し、車のナンバープレートと到着までにかかる時間が表示されます。
さらにデバッグ情報として、リクエストIDとバックエンド応答までのレイテンシが表示されます。
HotRODのコードはGitHubのjaegertracing/jaegerリポジトリにあるのですが、試すだけならばDockerイメージ(jaegertracing/example-hotrod
)が簡単です。
$ docker run --rm -it \ --link jaeger \ -p8080-8083:8080-8083 \ jaegertracing/example-hotrod:1.7 \ --jaeger-agent.host-port=jaeger:6831 \ all Unable to find image 'jaegertracing/example-hotrod:1.7' locally 1.7: Pulling from jaegertracing/example-hotrod fbbcd8295019: Pull complete Digest: sha256:1b21aa8d851903c823d38a942aa7e98234d0918ffcc119f866e9f3e23807250d Status: Downloaded newer image for jaegertracing/example-hotrod:1.7 2018-11-08T05:27:24.453Z INFO cmd/root.go:86 Using expvar as metrics backend 2018-11-08T05:27:24.454Z INFO cmd/all.go:25 Starting all services 2018-11-08T05:27:25.590Z INFO log/logger.go:37 Starting {"service": "route", "address": "http://0.0.0.0:8083"} 2018-11-08T05:27:25.592Z INFO log/logger.go:37 Starting {"service": "frontend", "address": "http://0.0.0.0:8080"} 2018-11-08T05:27:25.700Z INFO log/logger.go:37 Starting {"service": "customer", "address": "http://0.0.0.0:8081"} 2018-11-08T05:27:25.702Z INFO log/logger.go:37 TChannel listening {"service": "driver", "hostPort": "[::]:8082"}
起動ログを見ると frontend
, customer
, route
, driver
という4つのマイクロサービスからHotRODは構成されています。
コンテナが起動したら、ブラウザで http://localhost:8080
を開いてHotRODのfrontendサービスにアクセスします。
$ open -a "Google Chrome" http://localhost:8080
画面上のボタンを何度かクリックして、トレース情報をJaegerに送りましょう。
Jaeger UIでトレース情報を確認
トレース情報が貯まったところで、Jaeger UIを確認していきます。
Jaeger UIには、Search
, Compare
, Dependencies
のメニューがあります。
Search - 大量のトレースから目的のトレース情報を探す
Search
画面はトレース情報の検索UIを提供します。
左側の Find Traces
では、
Service
:frontend
,driver
,customer
,route
のようなサービスによる絞り込みOperation
:all
,HTTP GET
,HTTP GET パス
のような操作による絞り込みTags
:error=true
,http.status_code=200
のようなスパン・タグによる絞り込みMin Duration
: レイテンシの最短時間による絞り込み
...のような絞り込み機能を提供しています。
右上部には、各トレース情報の発生時刻とレイテンシがプロットされていて、遅延の発生がひと目で分かるようになっています。
右下部には、絞り込み条件に合ったトレース情報が1件ずつ表示されます。
さらに詳細を知りたいトレース情報を画面から選択すると、詳細ビューに遷移します。
ここでは、トレースの各スパンの詳細情報が表示されますが、HTTPリクエストであれば、URLやレイテンシ、HTTPステータスコードを確認できますし、MySQLのようなDBアクセスであれば、発行されたSQLまでも確認が可能です。
トレースとスパンの関係については、トレースはリクエストからレスポンスまでのトランザクションを構成するスパンの集合であり、スパンは1つのサービス内の処理を表現しています。
Jaeger公式サイトのArchitecture解説より
Compare - 2つのトレース情報を比較して問題の原因箇所を特定する
Compare
画面は2つのトレース情報の比較UIを提供します。
比較した結果、パフォーマンスのボトルネックがどこなのか可視化されます。
Dependencies - サービス間の依存関係を可視化する
Dependencies
画面はサービス間の依存関係を表示するUIを提供します。
スクリーンショットの例だと、HotRODのWeb UIを提供するfrontendサービスからcustomerサービス、driverサービス、routeサービスへ、さらにcustomerサービスからmysqlへ、driverサービスからredisへとRPCリクエストがされていることが、その呼び出し回数も含めて分かります。
Jaegerストレージバックエンドの補足
技術仕様にも記載したように、トレース情報を保存するストレージとして、インメモリストレージ、Cassandra、Elasticsearchがサポートされています。 なお、本記事のDockerイメージを使ったやり方では、インメモリストレージとなるため、Dockerコンテナを停止するとトレース情報は消えてしまいますので、ご注意ください。
さいごに
分散トレーシングとOpenTracing、そして分散トレーシングシステムのJaegerを紹介しました。
OpenTracingに関しては、以前の業務でSpring BootアプリケーションにDatadog APMを導入した際に扱っていたのですが、紹介したようにパフォーマンスボトルネックやサービス/ミドルウェアの依存関係が可視化され、サービス運用の助けになるものでした。
マイクロサービスによりシステムの構成が複雑になると、サービス間の依存関係やログの把握が難しく、問題が発生した場合に原因の調査が困難になります。
そんなシステムでいざ問題が発生した場合、効率的に問題の事象と原因を把握するために、Jaegerのような分散トレーシングシステムが重要になるのではないでしょうか。
最後に、私のいるプロダクトグループでは一緒にプロダクト開発をするメンバーを募集しています。
少しでも興味のある方は以下の記事を是非ご覧ください。
参考
- Jaeger: open source, end-to-end distributed tracing
- jaegertracing/jaeger - GitHub
- AWS X-Ray(製品や分散アプリケーションの分析とデバッグ)
- Stackdriver Trace - Googleの分散トレーシングシステム
- OpenZipkin
- アプリケーションパフォーマンスモニタリング(APM)| Elastic
- Next generation application performance monitoring | Datadog
- Go で書かれたマイクロサービスの分散トレーシング
- Cloud Native Computing Foundationに新プロジェクト「Jaeger」と「Envoy」が追加される