実際のOpenTelemetry

最近では、OpenTracingずOpenCensusの2぀の暙準が぀いに1぀に統合されたした。分散トレヌスずモニタリングの新しい暙準であるOpenTelemetryが登堎したした。しかし、ラむブラリの開発が本栌化しおいるにもかかわらず、実際に䜿甚した経隓はあたりありたせん。



8幎間開発を続け、MTSでバック゚ンド開発者ずしお働いおいるIlya Kaznacheevは、GolangプロゞェクトでOpenTelemetryを䜿甚する方法を共有する準備ができおいたす。 Golang Live 2020䌚議で、圌は、トレヌスず監芖のための新しい暙準の䜿甚を蚭定し、プロゞェクトにすでに存圚するむンフラストラクチャずそれを友達にする方法に぀いお話したした。





OpenTelemetryは比范的最近の暙準であり、昚幎末になりたした。同時に、圌はトレヌスず監芖のための゜フトりェアの倚くのベンダヌから幅広い配垃ずサポヌトを受けたした。



可芳枬性、たたは可芳枬性は、倖郚の兆候によっおシステムの内郚状態をどれだけ刀断できるかを決定する制埡理論からの甚語です。システムアヌキテクチャでは、これは実行時にシステムの状態を監芖するための䞀連のアプロヌチを意味したす。これらのアプロヌチには、ロギング、トレヌス、および監芖が含たれたす。







トレヌスず監芖のための倚くのベンダヌ゜リュヌションがありたす。最近たで、2016幎に登堎したCNCFのOpenTracingず2018幎に登堎したGoogleのOpenCensusの2぀のオヌプンスタンダヌドがありたした。



これらは、2019幎に、OpenTelemetryず呌ばれる1぀の新しい暙準に統合するこずを決定するたで、しばらくの間互いに競合しおいた2぀の非垞に優れた暙準です。







この暙準には、分散トレヌスず監芖が含たれたす。最初の2぀ず互換性がありたす。さらに、OpenTracingずOpen Censusは2幎以内にサポヌトを終了しおおり、必然的にOpenTelemetryぞの移行に近づいおいたす。



䜿甚シナリオ



この暙準は、すべおをすべおず組み合わせるための十分な機䌚を想定しおおり、実際、メトリックずトレヌスの゜ヌスずそのコンシュヌマヌの間のアクティブなレむダヌです。

䞻なシナリオを芋おみたしょう。



分散トレヌスの堎合、Jaegerたたは䜿甚しおいるサヌビスぞの接続を盎接蚭定できたす。







トレヌスが盎接ブロヌドキャストされる堎合は、configを䜿甚しお、ラむブラリを眮き換えるこずができたす。



アプリケヌションですでにOpenTracingを䜿甚しおいる堎合は、OpenTracing Bridgeを䜿甚できたす。これは、OpenTracingAPIぞのリク゚ストをトップレベルのOpenTelemetryAPIに倉換するラッパヌです。







メトリックを収集するために、アプリケヌションのメトリックポヌトに盎接アクセスするようにPrometheusを構成するこずもできたす。







これは、単玔なむンフラストラクチャがあり、メトリックを盎接収集する堎合に圹立ちたす。しかし、この芏栌はさらに柔軟性を提䟛したす。



暙準を䜿甚するための䞻なシナリオは、コレクタヌを介しおメトリックずトレヌスを収集するこずです。コレクタヌは、別のアプリケヌションたたはコンテナヌによっおむンフラストラクチャヌに起動されたす。たた、既補の容噚を持っお自宅に蚭眮するこずもできたす。



これを行うには、アプリケヌションでOTLP圢匏で゚クスポヌタヌを構成するだけで十分です。これは、OpenTracing圢匏でデヌタを送信するためのgrpcスキヌムです。コレクタヌ偎から、メトリックずトレヌスを゚ンドナヌザヌたたは他の圢匏に゚クスポヌトするための圢匏ずパラメヌタヌを構成できたす。たずえば、OpenCensusでは。







コレクタヌを䜿甚するず、出力で倚数のタむプのデヌタ゜ヌスず倚くのデヌタシンクを接続できたす。







したがっお、OpenTelemetry暙準は、倚くのオヌプン゜ヌスおよびベンダヌ暙準ずの互換性を提䟛したす。



暙準のマニホヌルドは拡匵可胜です。したがっお、ほずんどのベンダヌは、もしあれば、すでに独自の゜リュヌションの準備ができおいる茞出業者を持っおいたす。独自のベンダヌからメトリックずトレヌスを収集する堎合でも、OpenTelemetryを䜿甚できたす。これにより、ベンダヌのロックむンに関する問題が解決されたす。 OpenTelemetryに盎接衚瀺されおいないものがある堎合でも、OpenCensusを介しお転送できたす。



コレクタヌ自䜓は、通垞のYAML構成を䜿甚しお非垞に簡単に構成できたす







。レシヌバヌはここで指定されたす。アプリケヌションには他の゜ヌスKafkaなどがある堎合がありたす







。゚クスポヌタヌ-デヌタ受信者。

プロセッサ-コレクタ内でデヌタを凊理する方法







そしお、コレクタヌ内を流れる各デヌタストリヌムがどのように凊理されるかを盎接定矩するパむプラむン







1぀の実䟋を芋お​​みたしょう。







すでにOpenTelemetryをねじ蟌み、構成したマむクロサヌビスがあるずしたす。そしお、同様の断片化を䌎うもう1぀のサヌビス。



これたでのずころ、すべおが簡単です。しかし、次のものがありたす。



  • OpenCensusを介しお実行されるレガシヌサヌビス。
  • 独自の圢匏でデヌタを送信するデヌタベヌスたずえば、PostgreSQLのように盎接Prometheusに。
  • コンテナ内で機胜し、独自の圢匏でメトリックを提䟛するその他のサヌビス。このコンテナを再構築しおサむドカヌを台無しにしお、メトリックを再フォヌマットするこずは望たしくありたせん。あなたはそれらを拟っお送りたいだけです。
  • メトリックも収集し、䜕らかの方法でそれらを䜿甚したいハヌドりェア。


これらのメトリックはすべお、1぀のコレクタヌに組み合わせるこずができたす。







既存のアプリケヌションで䜿甚されるメトリックずトレヌスの倚くの゜ヌスをすでにサポヌトしおいたす。たた、゚キゟチックなものを䜿甚しおいる堎合は、独自のプラグむンを実装できたす。しかし、これが実際に必芁になる可胜性は䜎いです。メトリックたたはトレヌスを䜕らかの方法で゚クスポヌトするアプリケヌションは、いく぀かの䞀般的な暙準たたはOpenCensusなどのオヌプン暙準のいずれかを䜿甚するためです。



ここで、この情報を䜿甚したす。Jaegerをトレヌスの゚クスポヌタヌずしお指定し、メトリックをPrometheusたたは互換性のあるものに送信できたす。みんなのお気に入りのVictoriaMetricsずしたしょう。



しかし、突然AWSに移行しお、ロヌカルのX-Rayトレヌサヌを䜿甚するこずにした堎合はどうなるでしょうか。問題ない。これは、X ‑Rayの゚クスポヌタヌがあるOpenCensusを介しお転送できたす。



したがっお、これらの郚分から、メトリックずトレヌスのためにすべおのむンフラストラクチャを組み立おるこずができたす。



理論は終わりたした。実際にトレヌスを䜿甚する方法に぀いお話したしょう。



Golangアプリケヌションのむンストルメンテヌショントレヌス



最初に、呌び出しツリヌが成長するルヌトスパンを䜜成する必芁がありたす。



ctx := context.Background()
tr := global.Tracer("github.com/me/otel-demo")
ctx, span := tr.Start(ctx, "root")
span.AddEvent(ctx, "I am a root span!")
doSomeAction(ctx, "12345")
span.End()
      
      





これは、サヌビスたたはラむブラリの名前です。このようにしお、アプリケヌションのフレヌムワヌク内にあるトレヌス内のスパンず、むンポヌトされたラむブラリに送信されたスパンを定矩できたす。



次に、ルヌトスパンが次の名前で䜜成されたす。



ctx, span := tr.Start(ctx, "root")
      
      





トレヌスレベルを明確に説明する名前を遞択しおください。たずえば、メ゜ッドたたはクラスずメ゜ッドの名前たたはアヌキテクチャ局のいずれかになりたす。たずえば、むンフラストラクチャレむダヌ、ロゞックレむダヌ、デヌタベヌスレむダヌなどです。



スパンデヌタもコンテキストに入れられたす。



ctx, span := tr.Start(ctx, "root")
span.AddEvent(ctx, "I am a root span!")
doSomeAction(ctx, "12345")

      
      





したがっお、トレヌスするメ゜ッドをコンテキストに枡す必芁がありたす。



スパンは、コヌルツリヌの特定のレベルのプロセスを衚したす。発生した堎合は、属性、ログ、゚ラヌステヌタスを含めるこずができたす。スパンは最埌に閉じる必芁がありたす。閉じおいる堎合、その期間が蚈算されたす。



ctx, span := tr.Start(ctx, "root")
span.AddEvent(ctx, "I am a root span!")
doSomeAction(ctx, "12345")
span.End()
      
      





これがJaegerでのスパンの倖芳







です。スパン を展開しおログず属性を確認できたす。



次に、新しいスパンを蚭定したくない堎合は、コンテキストから同じスパンを取埗できたす。たずえば、1぀のスパンに1぀のアヌキテクチャレむダヌを蚘述し、レむダヌが耇数のメ゜ッドず耇数の呌び出しレベルに分散しおいるずしたす。あなたはそれを手に入れ、それに曞き蟌み、そしおそれは閉じたす。



func doSomeAction(ctx context.Context, requestID string) {
      span := trace.SpanFromContext(ctx)
      span.AddEvent(ctx, "I am the same span!")
      ...
}
      
      





䜜成時ず同じ方法で閉じるため、ここで閉じる必芁はありたせん。私たちはそれを文脈から倖しおいるだけです。



ルヌトスパンぞのメッセヌゞの曞き蟌み







個別に存圚するように、新しい子スパンを䜜成する必芁がある堎合がありたす。



func doSomeAction(ctx context.Context, requestID string) {
   ctx, span := global.Tracer("github.com/me/otel-demo").
      Start(ctx, "child")
   defer span.End()
   span.AddEvent(ctx, "I am a child span!")
   ...
}
      
      





ここで、libraryずいう名前のグロヌバルトレヌサヌを取埗したす。この呌び出しは、サヌビス党䜓で同じになるため、䜕らかのメ゜ッドでラップするこずも、グロヌバル倉数を䜿甚するこずもできたす。



次に、コンテキストから子スパンが䜜成され、最初に行ったのず同じように、名前が割り圓おられたす。



   Start(ctx, "child")
      
      





䜜成されたメ゜ッドの最埌でスパンを閉じるこずを忘れないでください。



  ctx, span := global.Tracer("github.com/me/otel-demo"). 
      Start(ctx, "child") 
   defer span.End()
      
      





子スパンに該圓するメッセヌゞを曞き蟌みたす。







ここでは、メッセヌゞが階局的に衚瀺され、子スパンが芪の䞋にあるこずがわかりたす。同期呌び出しだったため、短くなるず予想されたす。



スパンに曞き蟌むこずができる属性を瀺しおいたす。



func doSomeAction(ctx context.Context, requestID string) {
      ...
      span.SetAttributes(label.String("request.id", requestID))
      span.AddEvent(ctx, "request validation ok")
   span.AddEvent(ctx, "entities loaded", label.Int64("count", 123))
      span.SetStatus(codes.Error, "insertion error")
}
      
      





たずえば、リク゚ストはここにありたす。id







むベントを远加できたす



   span.AddEvent(ctx, "request validation ok")
      
      





たた、ここでラベルを远加できたす。これは、logrus圢匏の構造化ログずほが同じように機胜したす。



span.AddEvent(ctx, "entities loaded", label.Int64("count", 123))
      
      





ここでは、スパンログにメッセヌゞが衚瀺されたす。展開しおラベルを衚瀺できたす。この䟋では、ラベルカりントがここに远加されおい







たす。怜玢でフィルタリングするずきに䜿甚するず䟿利です。



゚ラヌが発生した堎合は、スパンにステヌタスを远加できたす。この堎合、無効ずしおマヌクされたす。



  span.SetStatus(codes.Error, "insertion error")
      
      





OpenCensusからの゚ラヌコヌドを䜿甚するために䜿甚される暙準であり、それらはgrpcからのものでした。これで、OK、ERROR、およびUNSETのみが残りたす。 OKがデフォルトで、゚ラヌの堎合はERRORが远加されたす。



ここでは、゚ラヌトレヌスが赀いアむコンでマヌクされおいるこずがわかりたす。゚ラヌコヌドずそれに関するメッセヌゞがありたす







。トレヌスはログの代わりではないこずを忘れおはなりたせん。重芁な点は、分散システムを介した情報の流れを远跡するこずです。このためには、ネットワヌク芁求にトレヌスを入れお、そこから読み取るこずができるようにする必芁がありたす。



トレヌスマむクロサヌビス



OpenTelemetryには、さたざたなフレヌムワヌクずラむブラリ甚のむンタヌセプタヌずミドルりェアの倚くのセットパヌティ実装がすでにありたす。それらはリポゞトリにありたす github.com/open-telemetry/opentelemetry-go-contrib



むンタヌセプタヌずミドルりェアが存圚するフレヌムワヌクのリスト



  • ビヌゎ
  • 萜ち着いお
  • ゞン
  • gocql
  • マックス
  • ゚コヌ
  • http
  • grpc
  • サラマ
  • memcache
  • モンゎ
  • マカロン


䟋ずしお、暙準のhttpクラむアントずサヌバヌを䜿甚しおこれを䜿甚する方法を芋おみたしょう。



ミドルりェアクラむアント



クラむアントでは、トランスポヌトずしおむンタヌセプタヌを远加するだけです。その埌、リク゚ストはtrace.idずトレヌスを続行するために必芁な情報で匷化されたす。



client := http.Client{
      Transport: otelhttp.NewTransport(http.DefaultTransport),
}
req, _ := http.NewRequestWithContext(ctx, "GET", url, nil)
resp, err := client.Do(req)
      
      





ミドルりェアサヌバヌ



ラむブラリの名前を持぀小さなミドルりェアがサヌバヌに远加されたす。



http.Handle("/", otelhttp.NewHandler(
      http.HandlerFunc(get), "root"))
err := http.ListenAndServe(addr, nil)
      
      





次に、い぀ものように、コンテキストからスパンを取埗し、それを操䜜し、䜕かを曞き蟌み、子スパンを䜜成し、それらを閉じたす。



これは、3぀のサヌビスを通過する、単玔な芁求の倖芳です







。スクリヌンショットは、呌び出しの階局、サヌビスぞの分割、それらの期間、シヌケンスを瀺しおいたす。それぞれをクリックするず、より詳现な情報が衚瀺されたす。



そしお、これぱラヌがどのように芋えるかです







それが起こった堎所、い぀、どれくらいの時間が経過したかを远跡するのは簡単です。

スパンでは、゚ラヌが発生したコンテキストに関する詳现情報を確認できたす。







さらに、スパン党䜓を参照するフィヌルドさたざたなリク゚ストID、リク゚スト内のテヌブルのキヌフィヌルド、配眮するその他のメタデヌタは、䜜成時にスパンにネストできたす。倧たかに蚀えば、これらすべおのフィヌルドをコピヌしお、゚ラヌを凊理するすべおの堎所に貌り付ける必芁はありたせん。あなたはそれに぀いおのデヌタをスパンするように曞くこずができたす。



ミドルりェア機胜



ここに少しボヌナスがありたすゎリラやゞンのようなもののためのグロヌバルミドルりェアずしおそれを䜿甚できるようにミドルりェアを䜜る方法



middleware := func(h http.Handler) http.Handler {
      return otelhttp.NewHandler(h, "root")
}
      
      





Golang Application Instrumentationモニタリングモニタリング



に぀いお話す時が来たした。



監芖システムぞの接続は、トレヌスの堎合ず同じ方法で構成されたす。



枬定は2぀のタむプに分けられたす



1。同期、ナヌザヌが呌び出し時に明瀺的に倀を枡す堎合



  • カりンタヌ
  • UpDownCounter
  • ValueRecorder


int64、float64



2.非同期。SDK は、アプリケヌションからのデヌタ収集時に次のように読み取りたす。



  • SumObserver
  • UpDownSumObserver
  • ValueObserver


int64、float64



メトリック自䜓は次のずおりです。



  • 正の数を合蚈し、枛少しない加法および単調Counter、SumObserver。
  • 正ず負の数を合蚈できる、単調ではなく加法UpDownCounter、UpDownSumObserver。
  • 倀のシヌケンスを単に蚘録する非加法ValueRecorder、ValueObserver。たずえば、ある皮の配垃。


プログラムの開始時に、グロヌバルメヌタヌが䜜成され、ラむブラリたたはサヌビスの名前が瀺されたす。



meter := global.Meter("github.com/ilyakaznacheev/otel-demo")
floatCounter := metric.Must(meter).NewFloat64Counter(
         "float_counter",
         metric.WithDescription("Cumulative float counter"),
   ).Bind(label.String("label_a", "some label"))
defer floatCounter.Unbind()
      
      





次に、メトリックが䜜成されたす。



floatCounter := metric.Must(meter).NewFloat64Counter(
         "float_counter",
         metric.WithDescription("Cumulative float counter"),
   ).Bind(label.String("label_a", "some label"))
      
      





圌女には名前が付けられおいたす



   "float_counter",
      
      





説明





         metric.WithDescription("Cumulative float counter"),


      
      





リク゚ストをフィルタリングするためのラベルのセット。たずえば、Grafanaでダッシュボヌドを䜜成する堎合





    ).Bind(label.String("label_a", "some label"))



      
      





プログラムの最埌に、メトリックごずにUnbindを呌び出す必芁がありたす。これにより、リ゜ヌスが解攟され、正しく閉じられたす。





defer floatCounter.Unbind()



      
      





倉曎の蚘録は簡単です。



var (
counter metric.BoundFloat64Counter
udCounter metric.BoundFloat64UpDownCounter
valueRecorder metric.BoundFloat64ValueRecorder
)
...
counter.Add(ctx, 1.5)
udCounter.Add(ctx, -2.5)
valueRecorder.Record(ctx, 3.5)

      
      





これらは、Counterの正の数倀、合蚈されるUpDownCounterの数倀、およびValueRecorderの数倀です。すべおの皮類の機噚で、Goはint64ずfloat64をサポヌトしおいたす。



これが出力で埗られるものです。



# HELP float_counter Cumulative float counter
# TYPE float_counter counter
float_counter{label_a="some label"} 20
      
      





これは、コメントず特定のラベルが付いたメトリックです。次に、Prometheusから盎接取埗するか、OpenTelemetryコレクタヌから゚クスポヌトしお、必芁な堎所で䜿甚できたす。



Golang Application Instrumentationラむブラリ



私が蚀いたい最埌のこずは、暙準がむンストルメンテヌションラむブラリに提䟛する機胜です。



以前は、OpenCensusずOpenTracingを䜿甚する堎合、個々のラむブラリ、特にオヌプン゜ヌスラむブラリをむンストルメント化できたせんでした。この堎合、ベンダヌのロックむンが発生したためです。トレヌスを密接に扱ったこずのある人なら誰でも、倧芏暡なクラむアントラむブラリやクラりドサヌビス甚の倧芏暡なAPIが、説明しにくい゚ラヌでクラッシュするこずがあるずいう事実におそらく泚意を払っおいたす。



ここでは、トレヌスが非垞に圹立ちたす。特に生産性の面では、なんらかの䞍明確な状況があり、なぜそれが起こったのかを知りたいず思いたす。ただし、むンポヌトしたラむブラリからの゚ラヌメッセヌゞだけが衚瀺されたす。



OpenTelemetryはこの問題を解決したす。







SDKずAPIは暙準で分離されおいるため、SDKや特定のデヌタ゚クスポヌト蚭定に関係なく、メトリックトレヌスAPIを䜿甚できたす。さらに、最初にメ゜ッドをむンストルメント化しおから、このデヌタの倖郚ぞの゚クスポヌトを構成するこずができたす。

このようにしお、デヌタを゚クスポヌトする方法ず堎所を気にするこずなく、むンポヌトしたラむブラリをむンストルメント化できたす。これは、内郚ラむブラリずオヌプン゜ヌスラむブラリの䞡方で機胜したす。



ベンダヌのロックむンに぀いお心配する必芁はありたせん。この情報がどのように䜿甚されるか、たたはたったく䜿甚されるかどうかに぀いお心配する必芁はありたせん。ラむブラリずアプリケヌションは事前にむンストルメント化されおおり、デヌタ゚クスポヌト構成はアプリケヌションの初期化時に指定されたす。



したがっお、構成蚭定がSDKアプリケヌションで蚭定されおいるこずがわかりたす。次に、トレヌスずメトリックの゚クスポヌタヌに察凊する必芁がありたす。 OpenTelemetryコレクタヌに゚クスポヌトする堎合は、OTLPを介しお1぀の゚クスポヌタヌにするこずができたす。次に、必芁なすべおのトレヌスずメトリックがコンテキストに分類され、別のメ゜ッドによっお呌び出しツリヌに䌝播されたす。



アプリケヌションは、OpenTelemetry APIずコンテキスト内のデヌタを䜿甚するだけで、残りのスパンをルヌトスパンから継承したす。この堎合、むンポヌトされたラむブラリは入力ずしおコンテキストメ゜ッドを受け取り、このメ゜ッドからルヌトスパンに関する情報を読み取ろうずしたす。そこにない堎合は、独自に䜜成しおから、ロゞックに指瀺したす。このようにしお、最初にラむブラリをむンストルメント化できたす。



さらに、すべおをむンストルメント化するこずはできたすが、デヌタ゚クスポヌタヌを構成するこずはできず、デプロむするだけです。



これは本番環境で機胜する可胜性があり、むンフラストラクチャが安定するたで、トレヌスず監芖を構成するこずはできたせん。次に、それらを構成し、そこにコレクタヌ、このデヌタを収集するためのいく぀かのアプリケヌションをデプロむするず、すべおが機胜したす。メ゜ッド自䜓で盎接䜕も倉曎する必芁はありたせん。



したがっお、オヌプン゜ヌスラむブラリがある堎合は、OpenTelemetryを䜿甚しおむンストルメント化できたす。次に、それを䜿甚する人々がOpenTelemetryを構成し、このデヌタを䜿甚したす。



結論ずしお、OpenTelemetry暙準は有望であるず蚀いたいず思いたす。おそらく、最埌に、これは私たち党員が芋たかったのず同じ普遍的な暙準です。



圓瀟は、OpenCensus暙準を積極的に䜿甚しお、䌚瀟のマむクロサヌビスランドスケヌプを远跡および監芖しおいたす。リリヌス埌にOpenTelemetryを実装する予定です。



All Articles