Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Distributed Tracing@OpenShift Meetup Tokyo20191018

16yuki0702
October 18, 2019

Distributed Tracing@OpenShift Meetup Tokyo20191018

16yuki0702

October 18, 2019
Tweet

More Decks by 16yuki0702

Other Decks in Programming

Transcript

  1. • 分散トレーシングの歴史 ◦ OpenTracing, OpenCensus, OpenTelemetry • 分散トレーシングの概念 ◦ なぜ必要か

    ◦ Trace, Span等々の仕様の話 • 実際にサンプルを作って動かす • まとめ 今日話すこと 2
  2. • マイクロサービスは小さなサービスの集まり • サービスがスケールすればするほどサービス間の関係は複雑さを増す • サービス間のリクエストで問題が発生した場合どうする?? ◦ 特定の条件でサービスが極端に遅くなる ◦ サービス単体では問題なさそう

    ◦ どこかのサービス間、かつ特定のタイミングのみ遅くなる ▪ ログとメトリック収集だけではこの事象を素早く特定するのは非常に困難 なぜ分散トレーシングが重要なのか?? 6
  3. • 分散トレーシングではサービス間の リクエストの詳細な状態を記録する ◦ リクエストパスの追跡 ◦ サービス間(ホップ)のレイテンシ ◦ サービス間のエラー状況 •

    複雑なマイクロサービス間の関係をビュー として確認することができる ◦ トラブルシューティングが容易 ▪ どこのサービス間でエラーが起きて いるか、遅延が起きているか なぜ分散トレーシングが重要なのか?? 7
  4. • Trace ◦ 分散システム間のリクエストの記録 ◦ Spanのツリー、またはDAG(有効非巡回グラフ - git commit treeと一緒)

    で表 現される • Span ◦ Trace内の連続した作業セグメント ◦ 名前と時間を持つ ◦ 他のSpanと親子関係等を持ち関連しあう ◦ フォーマットはOpenTracingとOpenCensusで異なる ▪ OpenTelemetryは両方と互換性がある 分散トレーシングの概念 8
  5. • Root Span ◦ Traceの最初のSpan ◦ Root Spanの時間はTrace全体の時間を表すか、非常に近くなる • Context

    Propagation ◦ Tracingはコンテキストの伝搬で成り立つ ▪ GolangのContextパッケージを考えると想像しやすそう ◦ 複数サービスを横断するリクエストが辿るパス内で、一意にどのリクエストか識 別可能な情報 分散トレーシングの概念 9
  6. • Operation name ◦ 大抵はspanを開始したAPIの参照 • Start/finish timestamp ◦ spanがいつ開始/終了したか

    • Tags ◦ クエリ/フィルタ用にユーザで定義するannotation • Logs ◦ span追跡用のkey/valueペア • SpanContext ◦ Span境界を超えて伝搬されるメタデータ Spanのフォーマット (OpenTracing) 12
  7. • Name ◦ Spanが何かを定義する文字列 • SpanID ◦ SpanのID • TraceID

    ◦ Spanが所属するTraceのID • ParantSpanID ◦ Spanの親ID • StartTime/EndTime ◦ Spanがいつ開始/終了したか Spanのフォーマット (OpenCensus) 13
  8. • Status ◦ ある時点のスパンの状態を表現 • Time event ◦ ある時点で発生したイベントを定義 •

    Link ◦ Trace内のspan間の関係を定義 • SpanKind ◦ Span間の関係の定義 • TraceOptions ◦ Spanがサンプリングされるかどうか。バイトで定義 • TraceState ◦ Spanの位置/順序に関する詳細 Spanのフォーマット 2 (OpenCensus) 14
  9. • OpenTracing実装の一つ • Twitterが開発 • Envoyで使われている (対応している) • 2012年くらいから存在し結構歴史は古い •

    バックエンドはJavaで実装され、UIも提供 • C#, Go, Java, JavaScript, Ruby, Scala, PHP等色々な言語でクライアント提供 Tracing実装 - Zipkin 16
  10. • OpenTracing実装の一つ • Uberが開発 (2017) • OpenTelemetryのバックエンドとして機能できる • Client LibraryはOpenTelemetry

    APIを当面実装して対応 ◦ 将来的には凍結し、独自機能はOpenTelemetryに組み込む予定 • agent/collector のモデルで動作する (OpenCensusと非常に似ている) ◦ OpenTracing実装でありつつ、OpenCensusと同じようなアーキテクチャ • OpenShift Service Mesh (Istio) 内でバックエンドコンポーネントとして使用される • OpenTelemetryと少しずつ統合していくと思われる Tracing実装 - Jaeger 18
  11. • OpenTelemetry ◦ 複数言語にAPI, SDKを提供し、テレメトリデータをバックエンドにエクスポートで きる機構を提供 • Jaeger ◦ agent/collectorで動作するが、主な目的はテレメトリデータを受信し、データの

    処理、集約、マイニング、可視化することが目的 ▪ 主にトレースバックエンドとして動作する ◦ クライアントライブラリを持つが、大部分がOpenTelemetryと重複する為将来的 に独自機能をOpenTelemetryに組み込み凍結予定 • OpenTelemetryはJaegerをバックエンドとしてネイティブにサポート • 主とする領域がそれぞれある JaegerとOpenTelemetryを整理 20
  12. • OpenShift Service Meshを使う ◦ https://docs.openshift.com/container-platform/4.1/service_mesh/service _mesh_day_two/ossm-example-bookinfo.html ◦ Operatorで簡単にインストール可能 •

    annotations: sidecar.istio.io/inject: "true" をDeployment Configに設定すると sidecarが立ち上がる • クライアントライブラリはOpenTracingを使用 • バックエンドはJaegerを使用 分散トレーシングを試してみる 21
  13. • Envoy(Sidecar Proxy)がトレーシング情報を直接Tracing Backend(Jaeger)に送信 • Envoyが以下行う ◦ リクエスト通過時にRequestID, トレースヘッダー(X-B3-TraceID)を生成 ◦

    リクエスト、レスポンスのメタデータ(レスポンスタイム等)に基づき各リクエストの Trace Spanを生成 ◦ 生成されたTrace Spanをトレースバックエンド(Jaeger)に送信 ◦ Traceヘッダーをproxyされるアプリケーションに送信 • Istio/EnvoyではLightStep, Zipkinをサポート。Zipkin API互換バックエンド(Jaeger)を サポート Envoy-Based Tracing 23
  14. Key : uber-trace-id Value : {trace-id}:{span-id}:{parent-span-id}:{flags} Propagation Format - Jaeger

    24 • Jaegerはデフォルトだと上記フォーマットでHttp headerにPropagation情報を転送し てくる • Istio/Envoyはこのフォーマットは対応していない • Zipkinのヘッダーフォーマットであるb3-propagationを使う必要がある ◦ もしくはLightStep
  15. x-b3-traceid x-b3-parentspanid x-b3-spanid x-b3-sampled Propagation Format - b3(Zipkin) 25 •

    リクエストを中継するサービスは上記ヘッダーをExtractし て次のサービスにPropagationさせる • Twitterではシステム名を鳥にちなんで名付けることが多 く、Zipkinは元々Big Brother Bird 略してB3と呼ばれてい たらしい。 ◦ なのでheaderのprefixにもb3
  16. 実装 (クライアント初期化処理) 28 Import ( ... opentracing "github.com/opentracing/opentracing-go" jaeger "github.com/uber/jaeger-client-go"

    "github.com/uber/jaeger-client-go/zipkin" ) func InitTracing(serviceName string) (opentracing.Tracer, io.Closer) { zipkinPropagator := zipkin.NewZipkinB3HTTPHeaderPropagator() injector := jaeger.TracerOptions.Injector(opentracing.HTTPHeaders, zipkinPropagator) extractor := jaeger.TracerOptions.Extractor(opentracing.HTTPHeaders, zipkinPropagator) tracer, closer := jaeger.NewTracer( serviceName, jaeger.NewConstSampler(true), jaeger.NewNullReporter(), injector, extractor, ) opentracing.SetGlobalTracer(tracer) ... • 必要なヘッダー(x-b3-*)をextract, injectする為にzipkinPropagatrorを 使用 • JaegerはOpenTracingの実装なの でOpenTracing.Tracer Interfaceを 満たす • Tracerをアプリ内でシングルトンにす る為SetGlobalTracerする • Sampling Rateは固定 • Reporterもテスト用にNullReporter ◦ 詳しい説明はこちら
  17. 実装 (Propagation) 29 func Propagate(w http.ResponseWriter, r *http.Request, url string)

    { tracer := opentracing.GlobalTracer() spanCtx, err := tracer.Extract(opentracing.HTTPHeaders, opentracing.HTTPHeadersCarrier(r.Header)) if err != nil { panic(err) } req, err := http.NewRequest("GET", url, nil) if err != nil { panic(err.Error()) } tracer.Inject( spanCtx, opentracing.HTTPHeaders, opentracing.HTTPHeadersCarrier(req.Header), ) ... • opentracing.GlobalTracer()でtracer取得 (シングルトン) • Propagateするアプリケーションはistio proxyが生成したTrace, Spanを取得する為 にリクエストからSpanCtxをextract • リクエストを作成してSpanCtxから必要な ヘッダーをinject
  18. 実装2 (Propagation) 30 func Trace(w http.ResponseWriter, r *http.Request, traceName, url

    string) { ... incomingHeaders := []string{ "x-request-id", "user-agent", } for _, header := range incomingHeaders { req.Header.Set(header, r.Header.Get(header)) } resp, err := do(req) if err != nil { panic(err.Error()) } w.Write([]byte(string(resp))) } • X-request-id等、自分で抽出し ないといけないheaderだけ抽出 して設定 • 伝搬先にリクエストを送信して、 リクエスト元にレスポンスを返却
  19. 実装 (Destination) 31 func handler(w http.ResponseWriter, r *http.Request) { serviceName

    := os.Getenv("SERVICE_NAME") resptStr := fmt.Sprintf("Hello from %s!!", serviceName) w.Write([]byte(resptStr)) } • リクエストを伝搬しないアプリケーショ ンはサービス名だけレスポンスに設 定して返却
  20. 図を交えてもう一度整理 32 Pilot-agent (Envoy) dest3
 app Pilot-agent (Envoy) istio-ingressgateway
 Pilot-agent

    (Envoy) gateway
 app Pilot-agent (Envoy) dest1
 app Pilot-agent (Envoy) dest2
 app Request Zipkin (k8s Service) Jaeger (Collector) Jaeger UI

  21. 図を交えてもう一度整理 33 Pilot-agent (Envoy) dest3
 app Pilot-agent (Envoy) istio-ingressgateway
 Pilot-agent

    (Envoy) gateway
 app Pilot-agent (Envoy) dest1
 app Pilot-agent (Envoy) dest2
 app Request Zipkin (k8s Service) Jaeger (Collector) Jaeger UI
 • EnvoyがInbound, Outbound時にSpan生成、proxyされたアプリケーションに渡す。 TracerにはOpenTracing(Zipkin)を使用 • Envoyが直接istio-system内のZipkin Serviceにテレメトリ送信 ◦ Zipkinの後ろに控えているのはJaeger Collector (ややこしい!)
  22. 図を交えてもう一度整理 34 Pilot-agent (Envoy) dest3
 app Pilot-agent (Envoy) istio-ingressgateway
 Pilot-agent

    (Envoy) gateway
 app Pilot-agent (Envoy) dest1
 app Pilot-agent (Envoy) dest2
 app Request Zipkin (k8s Service) Jaeger (Collector) Jaeger UI
 • ProxyされたアプリケーションはOpenTracing(Jaeger)を使用して、Proxyから渡って きたContextをProgateする ◦ ただしヘッダーの形式はIstio/Envoyと合わせてZipkin(B3)(ややこしい!!)
  23. 図を交えてもう一度整理 35 Pilot-agent (Envoy) dest3
 app Pilot-agent (Envoy) istio-ingressgateway
 Pilot-agent

    (Envoy) gateway
 app Pilot-agent (Envoy) dest1
 app Pilot-agent (Envoy) dest2
 app Request Zipkin (k8s Service) Jaeger (Collector) Jaeger UI
 • Envoy(Zipkin)とアプリケーション(Jaeger)で異なるライブラリを使用していても、 OpenTracing実装なので正しくContextをPropagateできる ◦ OpenTracingの恩恵 ◦ OpenTelemetryが成熟すればもっとわかりやすくなるはず!
  24. Headerを見てみる (gateway) 38 X-Request-Id, [5465a62d-e194-99a0-9230-d44a52231e60] X-B3-Traceid, [a8cfcf72af6f01b2548c9f9605ac2b1f] X-B3-Spanid, [845a979942ca2aa4] X-B3-Parentspanid,

    [548c9f9605ac2b1f] X-B3-Sampled, [1] X-Forwarded-Host: [istio-ingressgateway-istio-system.apps.opensh...] • X-Forwarded-Hostをみるとistio-ingressgatewayからリクエストからきていること がわかる • isito-ingressgatewayがX-Request-ID, X-B3-Traceidを生成 • X-B3-Parentspanidはisito-ingressgatewayが生成したSpanを指す • X-B3-Spanidはistio-sidecarがリクエストを受け取った時に生成(inbound)
  25. Headerを見てみる (dest1) 39 X-Request-Id, [5465a62d-e194-99a0-9230-d44a52231e60] X-B3-Traceid, [a8cfcf72af6f01b2548c9f9605ac2b1f] X-B3-Spanid, [02b0374724e15993] X-B3-Parentspanid,

    [60a1097e8035b5e2] X-B3-Sampled, [1] • X-B3-Parentspanidはgateway のOutbound Spanを指す • X-B3-Spanidはistio-sidecarがリクエストを受け取った時に生成(inbound)
  26. Headerを見てみる (dest2) 40 X-Request-Id, [5465a62d-e194-99a0-9230-d44a52231e60] X-B3-Traceid, [a8cfcf72af6f01b2548c9f9605ac2b1f] X-B3-Spanid, [8959f06229087845] X-B3-Parentspanid,

    [f96978dbdc3effb1] X-B3-Sampled, [1] • X-B3-Parentspanidはdest1 のOutbound Spanを指す • X-B3-Spanidはistio-sidecarがリクエストを受け取った時に生成(inbound)
  27. Sample置き場 42 • • 試したいだけなら アプリより軽量かつシンプル ◦ なぜならリクエストの転送しかしていないから • Bookinfoはpython(productpage),

    java(reviews), node.js(ratings), ruby(details) でそれぞれ実装 ◦ Goのサンプルがないのでよろしければどうぞ ▪ ほぼRequestへのextract, injectしかしてないけど。。。
  28. まとめ 43 • 分散トレーシングに関しては今後OpenTelemetryに要注目 • バックエンド(Collector)に関してはJeagerが有力? ◦ Clientライブラリ, agent/collector全てOpenTelemetryと密に関わっているし ▪

    作者も一緒だし • OpenShift Service Meshで分散トレーシングするなら、アプリ側でちょっと実装入れ るだけで割と簡単に試せるはず ◦ ただしSideCarに対するInbound/Outboundだけでなく、もっと細かくSpan取りた いなら自分でStartSpanとかする必要あり ▪ 例えば特定の関数だけ細かく取りたいとか
  29. linkedin.com/company/red-hat youtube.com/user/RedHatVideos facebook.com/redhatinc twitter.com/RedHat 44 Red Hat is the world’s

    leading provider of enterprise open source software solutions. Award-winning support, training, and consulting services make Red Hat a trusted adviser to the Fortune 500. 
 Thank you OPTIONAL SECTION MARKER OR TITLE