Kubernetesやクラウドネイティブをより便利に利用する技術やツールの概要、使い方を凝縮して紹介する連載。今回は、ObservabilityとOpenTelemetryについて、概要や使い方を簡単に紹介する。
この記事は会員限定です。会員登録(無料)すると全てご覧いただけます。
Kubernetesやクラウドネイティブをより便利に利用する技術やツールの概要、使い方を凝縮して紹介する本連載「Cloud Nativeチートシート」。今回は、ObservabilityとOpenTelemetryについて、概要や使い方を簡単に紹介します。
劇的な勝利を収めたFIFAワールドカップカタール2022 日本対スペイン戦。肉眼では到底捉え切ることができなかった、ビデオアシスタントレフェリー(VAR)による勝利を裏付ける1枚の写真が全世界で注目されたことは記憶に新しいですね。
クラウドネイティブなシステムも動的に変化を続けています。プラットフォームには回復力や管理力、自動化といった特徴が備えられているからです。刻一刻と変わり続ける環境において「Observability」(可観測性:オブザーバビリティ)は重要な概念です。
OpenTelemetryは、Observabilityには欠くことができない、システムの状態を示すさまざまなシグナル(ログやCPU、メモリなどのメトリクス)の収集や送信を標準化する、Cloud Native Computing Foundation(CNCF)プロジェクトです。
Observabilityは、システムの状態を可視化するさまざまなシグナル「テレメトリー」によって実現されます。Observabilityを考える上で、各テレメトリーについて理解することが重要です。
テレメトリーについてはObservabilityの3本柱として「ログ」「トレース」「メトリクス」が有名です。「CNCF TAG Observability」のホワイトペーパーでは現在、「プロファイル」「ダンプ」も含めて「Primary Signals」と呼んでいます。
本稿では、基本となる「ログ」「トレース」「メトリクス」に着目し、テレメトリーやその収集方法について簡単に説明します。
テレメトリーの詳細については、本連載第13回で詳しく解説しているので併せてご覧ください。
これらのテレメトリー情報を収集し、可視化、解析することでシステムの状態を観測できます。
ここからは下図を見ながら、OpenTelemetryを利用しない場合(左:Separate Collection)と、利用した場合(右:OpenTelemetry Collection)を比較しながら、テレメトリー取得の課題とOpenTelemetryの特徴を紹介します。
テレメトリーの取得は、OpenTelemetryを利用しない場合(上記図左:Separate Collectionを参照)、ログはログファイルに出力し、「Grafana Loki」「ElasticSearch」などのログバックエンドに保存します。メトリクスはOSやプロセスのリソース利用量を取得して、「Prometheus」などのメトリクスバックエンドに保存します。トレースについても、「Jaeger」といったツールを使ってトレース情報を作成し、トレースバックエンドに保存するのが典型的な方法です。
そうなると、テレメトリーごとにバラバラに実装しているだけで、各テレメトリーを関連付けることができません。例えば、トレース情報からレイテンシの大きいリクエストを見つけたとしても、そのトレースにひも付くログを確認することは困難になります。
OpenTelemetryを用いると、OpenTelemetryで標準化された方法によって、ログとトレース、メトリクスを関連付けることができます(上記図右: OpenTelemetry Collectionを参照)(※注1)。
※注1:ただし、2023年1月の原稿執筆時点では、OpenTelemetryは完全には実装されておらず、現時点で実装する際のアーキテクチャは、上記の2つの図が混ざったアーキテクチャとなることにご留意ください。次回、OpenTelemetryやオープンソースソフトウェア(OSS)のモニタリングツールを使ったテレメトリーの関連付けについて、詳しく実践的に解説します。
これらテレメトリーを取得可能にすることを「インストルメンテーション」といいます。OpenTelemetryによるSDKやライブラリを用いることで、取得と収集の方法を統一でき、OpenTelemetryでインストルメントされたアプリはさまざまなObservabilityツールで可視化できます。さらに、後述する自動インストルメンテーションによって、アプリのコードの変更なしにテレメトリーを取得できます。
OpenTelemetryは、アプリに組み込むテレメトリーの取得と、「OpenTelemetry Collector」という、テレメトリーを収集、加工するコンポーネントに対する仕様策定、SDKやライブラリの提供を行っています。テレメトリーを保存、可視化するデータストアやダッシュボードなどのバックエンドはOpenTelemetryの対象外なので、OSSツールや商用製品を利用することになります。
OpenTelemetryの特徴をまとめると下記のようになります。
OpenTelemetryのインストルメンテーションは言語ごとにリポジトリが分かれており、各テレメトリーについては下表のようなステータスになっています(2023年1月段階)。最新のステータスは「Status」から確認できます。
「Stable」ステータスは、厳密なテストが済んでおり、長期的な互換性が保証されます。「Experimental」「Alpha」「Beta」ステータスは実験段階を表し、互換性を損なう仕様変更やパフォーマンスの問題が発生する場合があるので、利用には注意が必要です。
言語 | トレース | メトリクス | ログ |
---|---|---|---|
Java | Stable | Stable | Experimental |
Python | Stable | Stable | Experimental |
JavaScript | Stable | Stable | Development |
C#/.NET | Stable | Stable | Mixed |
C++ | Stable | Stable | Experimental |
Go言語 | Stable | Alpha | Frozen |
PHP | Beta | Beta | Not yet implemented |
Ruby | Stable | Not yet implemented | Not yet implemented |
Rust | Stable | Alpha | Not yet implemented |
Swift | Stable | Experimental | In development |
Erlang | Stable | Experimental | Experimental |
※ステータスは各プロジェクトの2023年1月段階の状態を転載したもの |
本稿の後半で、インストルメントしたアプリのソースコードを紹介しますが、手動でのインストルメンテーションは少々骨の折れる作業です。そういった場合、自動インストルメンテーションライブラリが役立ちます。
OpenTelemetryでは、自動インストルメンテーションのツールも開発されています。ここではKubernetesを使っている場合に有効な自動インストルメンテーションツールについて触れます。詳しくは「OpenTelemetry auto-instrumentation injection」をご覧ください。ここで紹介されている自動インストルメンテーションのためのOperatorを用いることで、アプリPodにサイドカーコンテナがインジェクトされ、テレメトリーを自動で取得できます。現状、.NETとJava、Node.js、Pythonがサポートされています。
他にもOpenTelemetry RegistryではOpenTelemetryのエコシステム(ライブラリやプラグイン、その他の便利なツールセット)がたくさんあるので、のぞいてみると新たな発見があるかもしれません。
自動インストルメンテーションに関しては、言語やフレームワークなどが対応していれば便利ですが、お使いの言語もしくはフレームワークによっては仕組み上対応できないケースもあるので、そのような場合は、本稿で紹介する方法を参考に手動でインストルメントしてください。
OpenTelemetry Collectorはバックエンドにテレメトリーを送信する際のプロキシのような役割を担い、テレメトリーデータの受信、処理、送信についてベンダー非依存な実装を提供します。OpenTelemetry Collectorをモニタリングサービスの前段に置く(下図)ことで、アプリ開発者はさまざまな恩恵を享受できます。
OpenTelemetry Collectorは、「Receiver」「Processor」「Exporter」といったコンポーネントから構成されています。
※注2:ここで説明するExporterは、後述のサンプルコード内に出てくる「TraceExporter」(アプリからOpenTelemetry Collectorにトレース情報を送信するコンポーネント)とは別のコンポーネントなので、ご注意ください。
OpenTelemetry Collectorは標準ディストリビューションとコントリビューションディストリビューションの2つがあります。上述のコンポーネント一覧リポジトリはコントリビューションのコンポーネントも含まれていて、Amazon Web Services(AWS)やGoogle Cloud Platform(GCP)といったベンダー製のコンポーネントや、OpenTelemetryが開発中のコンポーネント(非商用向け)も含まれています。
標準ディストリビューションのコンポーネント一覧とコントリビューションディストリビューションのコンポーネント一覧を見ると、含まれているコンポーネント数の違いが分かります。プロダクション環境では、イメージサイズの最適化などの理由で、必要なコンポーネントに構成を制限したカスタマイズ(リビルド)が推奨されています。
ここからは、OpenTelemetryを使ってサンプルアプリをインストルメントします。今回は分散トレースに絞りますが、次回でログとメトリクスも織り交ぜて紹介します。
本稿では、次のようなサンプルアプリを利用しながら、OpenTelemetryについて解説します。
Go言語製のサンプルアプリ(マイクロサービス)にOpenTelemetryを使ってインストルメントし、OpenTelemetry Collectorにトレース情報を送信します。OpenTelemetry Collectorはトレーシングバックエンドの「Grafana Tempo」にトレース情報を送信し、ダッシュボード機能を持つ「Grafana」で可視化する構成です。
サンプルアプリの「Service1」にリクエストを送ると、「Service2」と通信します。その処理中、各サービスからOpenTelemetry Collectorにテレメトリー(今回はトレース情報)が送信されます。最終的にGrafanaダッシュボード上でトレーススパン情報(詳細は後述)を確認します。
それぞれのコンポーネントは「Helm」を使ってデプロイしています。検証に用いたそれぞれのChartのバージョンを記しておきます。
Package | Chartのバージョン | Helm Repository |
---|---|---|
opentelemetry-operator | opentelemetry-operator-0.20.4 | https://artifacthub.io/packages/helm/opentelemetry-helm/opentelemetry-operator |
tempo-distributed | tempo-distributed-0.26.8 | https://artifacthub.io/packages/helm/grafana/tempo-distributed |
grafana | grafana-6.48.2 | https://artifacthub.io/packages/helm/grafana/grafana |
cert-manager | cert-manager-v1.9.1 | https://artifacthub.io/packages/search?org=cert-manager |
分散トレーシングでは、サービス間のリクエストの流れや処理速度などを監視します。リクエスト時の一連の処理を表すトレースは、個々の処理を表すスパンから構成されており、その幅は処理時間を表しています。
トレース情報(他のテレメトリーについても同様)は分散システム間でデータを関連付ける仕組み「Context Propagation」(コンテキスト伝搬)によってサービス間を流れていきます。
上記で説明したログ、トレース、メトリクスの他に、OpenTelemetryでは、第4のシグナルとして、「Baggage」が定義されています(OpenTelemetryサイトの「Signals」を参照)。
Baggageは、OpenTelemetryのスパン間で渡されるコンテキスト情報で、スパン間で情報を共有したい場合に便利です。例えば、顧客からクレームが来たときに、その顧客のアカウントIDを元に、顧客が操作したリクエストのログやトレースなどのテレメトリー情報を確認したいことがあるでしょう。また、サービスの特定のリクエストに問題があったときに、影響がある顧客を特定するためにアカウントIDを確認したい場合もあると思います。
このような、アカウントID、ユーザーID、商品IDなどのIDをBaggageに設定することによって、これらと、トレースや他のテレメトリーとの間に意味のあるIDの対応付けができます。OpenTelemetryでは「シグナル」として定義されていますが、テレメトリーに付与されるメタデータのようなものと考えた方が分かりやすいでしょう。
Baggageは便利なテレメトリーですが、HTTPヘッダに格納されてサービス間で受け渡されるので、機密情報の受け渡しに利用しないようにしてください。
本稿では簡単なサンプルを利用しているので、Baggageは利用していませんが、興味のある方は調べてみてください。
サンプルアプリのソースコードは次の通りです。
package main import ( "context" "fmt" "log" "os" "os/signal" "time" "github.com/gin-gonic/gin" "go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin" "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc" "go.opentelemetry.io/otel/propagation" "google.golang.org/grpc" "google.golang.org/grpc/credentials/insecure" "go.opentelemetry.io/otel/sdk/resource" sdktrace "go.opentelemetry.io/otel/sdk/trace" semconv "go.opentelemetry.io/otel/semconv/v1.4.0" ) // トレースをOpenTelemetry Collectorに送信するトレースエクスポーターの初期化と、 // トレーサーオブジェクト(スパンを生成)を作成するトレーサープロバイダーを設定する func initProvider() (func(context.Context) error, error) { ctx := context.Background() // トレーサープロバイダーでトレースに付与する属性情報(リソース)を定義する res, err := resource.New(ctx, resource.WithAttributes( // service1のトレースプロバイダーに設定するために"service1"とサービス名(ServiceName)属性情報を付与する semconv.ServiceNameKey.String("service1"), ), ) if err != nil { return nil, fmt.Errorf("failed to create resource: %w", err) } // トレースの送信先(エクスポート先)の設定 // 今回はKubernetesのクラスタ内通信で、OpenTelemetry CollectorにgRPCでトレースを送信する設定を記述する conn, err := grpc.DialContext(ctx, "sample-collector.observability.svc.cluster.local:4317", grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithBlock()) if err != nil { return nil, fmt.Errorf("failed to create gRPC connection to collector: %w", err) } // トレースエクスポーターの設定 traceExporter, err := otlptracegrpc.New(ctx, otlptracegrpc.WithGRPCConn(conn)) if err != nil { return nil, fmt.Errorf("failed to create trace exporter: %w", err) } // スパンProcessor(スパンの開始/終了時に呼び出される処理)としてバッチProcessorを作成する // バッチProcessorによって、テレメトリーがある程度たまってからまとめてOpenTelemetry Collectorに送信する bsp := sdktrace.NewBatchSpanProcessor(traceExporter) // トレーサープロバイダーの設定 // 設定にはサンプル方式や、上記で設定したリソーススパンProcessorを付与する var tracerProvider *sdktrace.TracerProvider tracerProvider = sdktrace.NewTracerProvider( sdktrace.WithSampler(sdktrace.AlwaysSample()), sdktrace.WithResource(res), sdktrace.WithSpanProcessor(bsp), ) otel.SetTracerProvider(tracerProvider) otel.SetTextMapPropagator(propagation.TraceContext{}) return tracerProvider.Shutdown, nil } var tracer = otel.Tracer("service1") // main 関数 func main() { //--- ここから、opentelemetry-goでトレーシングするための定型コード // シグナルハンドラーを設定する // プロセスの強制終了時にハンドラーを適切に設定しないと、強制終了によってテレメトリーが失われるので、注意 ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt) defer stop() shutdown, err := initProvider() // 上部で定義したOpenTelemetryのトレーシングコンポーネント初期化関数 if err != nil { log.Fatal(err) } // main関数終了時にトレースプロバイダーを終了するdeferを定義する defer func() { if err := shutdown(ctx); err != nil { log.Fatal("failed to shutdown TracerProvider: %w", err) } }() //--- ここまで、opentelemetry-goでトレーシングするための定型コード r := gin.New() r.Use(otelgin.Middleware("service1")) r.GET("/service", service1_sample1) r.Run(":8080") } // ここからがサービスを実行するロジックコード func service1_sample1(c *gin.Context) { // tracer.Startでスパンを生成する _, span := tracer.Start(c.Request.Context(), "service1 の sample1 関数を実行") // service1_sample1関数終了時にスパンを終了する defer span.End() // 他サービスの呼び出し例 // OpenTelemetryのライブラリを用いて他サービスへのHTTPリクエストを実装し、コンテキストの持つトレース情報を呼び出し先のサービスに引き継ぐ rsp, err := otelhttp.Get( c.Request.Context(), "http://service2.observability.svc.cluster.local:8080/service", ) if err != nil { log.Println(err) return } defer rsp.Body.Close() // 他関数の呼び出し例 // 現在のコンテキストを呼び出し先の関数に渡してトレース情報を引き継ぐ service1_sample2(c) } func service1_sample2(c *gin.Context) { // スパンの生成 _, span := tracer.Start(c.Request.Context(), "service1 の sample2 関数を実行") defer span.End() time.Sleep(time.Second * 2) }
プログラム全体のソースコードはこちらのリポジトリを参照してください。また、サービス1とサービス2のコンテナイメージはGitHub Container Registryに置いてあります。ソースコードは「opentelemetry-go」リポジトリの「example」コードを参考にしているので、興味のある方はそちらも併せてご確認ください。
Serviec1から呼び出されるService2については、Service1とほぼ同じです。Service1から呼び出される「service2_sample1」関数は、下記のようになります。上記のservice1_sample2と比べると、同じようにコンテキストを引数にして、スパンを作成、終了していることが分かります。
func service2_sample1(c *gin.Context) { _, span := tracer.Start(c.Request.Context(), "service2 の sample1 関数を実行") defer span.End() time.Sleep(time.Second * 4) }
これでサンプルアプリの実装は終わりです。このように手動でトレースを設定する場合、必要な箇所にスパンを定義する設定が適宜必要です。いささか苦労するので、先述したような自動インストルメンテーションが重要になります。
サンプルアプリを実行し、生成したトレーススパンをGrafanaで確認します。
本サンプルは、Kubernetesクラスタならば特にベンダーに依存せずに動作しますが、本稿では、Kubernetesは1.22を「Amazon Elastic Kubernetes Service」(EKS)で使用しています。
Kubernetes環境を用意したら、「Helm Chart」を用いて各種コンポーネントをデプロイします。
・作業用namespaceの作成
$ kubectl create ns observability
・cert-managerのデプロイ
「cert-manager」はKubernetesでSSL/TLS証明書を扱うパッケージです。詳細は省きますが、次にデプロイするOpenTelemetry Operatorを使うにはv1以上のcert-managerをデプロイする必要があります。
# cert-managerのデプロイ $ helm repo add jetstack https://charts.jetstack.io $ helm repo update $ helm install \ cert-manager jetstack/cert-manager \ --namespace observability \ --version v1.9.1 \ --set installCRDs=true \ --wait
・OpenTelemetry Collectorデプロイ
OpenTelemetry Collectorをデプロイします。cert-managerが起動していることを確認したら、まずはOpenTelemetry Operatorをデプロイします。OpenTelemetry Collectorは「OpenTelemetryCollector」というカスタムリソースとして定義されます。
# cert-managerの確認 $ kubectl get po | grep cert-manager …… # OpenTelemetry Operatorデプロイ $ helm repo add open-telemetry https://open-telemetry.github.io/opentelemetry-helm-charts $ helm repo update $ helm install \ otel-operator open-telemetry/opentelemetry-operator \ --namespace observability \ --version 0.20.4 \ --set installCRDs=true \ --wait # OpenTelemetry Collectorデプロイ $ cat <<EOF | kubectl apply -f - apiVersion: opentelemetry.io/v1alpha1 kind: OpenTelemetryCollector metadata: name: sample namespace: observability spec: mode: deployment config: | receivers: otlp: protocols: grpc: endpoint: "0.0.0.0:4317" processors: exporters: otlp: endpoint: tempo-distributed-distributor.observability.svc.cluster.local:4317 tls: insecure: true service: pipelines: traces: receivers: [otlp] processors: [] exporters: [otlp] EOF
OpenTelemetry Collectorの設定(25行目の「config: |」以下)を見ると、OpenTelemetry Collectorの内部では下表のようなコンポーネントでトレース情報の処理パイプラインが形成されていることが分かります。
コンポーネント | 名前 | 設定 |
---|---|---|
Receiver | otlp | 0.0.0.0:4317 |
Processor | --- | 今回は設定なし |
Exporter | otlp | tempo-distributed-distributor.observability.svc.cluster.local:4317 |
・Grafana Tempo/Grafanaデプロイ
トレーシングバックエンドのGrafana Tempoと、ダッシュボードツールのGrafanaをデプロイします。
Grafana Tempoのデプロイ時に「traces.otlp.grpc.enabled」を有効化してOTLP(OpenTelemetry Protocol)ポートを開いています(デフォルトでは無効)。
「search.enabled」はトレース情報のサーチ機能です。取得したトレースを一覧表示できて便利なので、有効化してトレース確認時に使用します。
# tempo-distributedのデプロイ $ helm repo add grafana https://grafana.github.io/helm-charts $ helm repo update $ helm install \ tempo-distributed grafana/tempo-distributed \ -n observability \ --version 0.26.8 \ --set traces.otlp.grpc.enabled=true \ --set search.enabled=true \ --wait # Grafanaのデプロイ $ helm install \ grafana grafana/grafana \ -n observability \ --version 6.48.2 \ --wait
最後に、今回作成したサンプルアプリをデプロイすれば、全てのコンポーネントの準備が完了です。下記のように、Pod一覧が「Running」となっていればOKです。
# デプロイ $ kubectl -n observability apply -f https://raw.githubusercontent.com/cloudnativecheetsheet/opentelemetry/main/01/service1/deployments/manifest.yaml,https://raw.githubusercontent.com/cloudnativecheetsheet/opentelemetry/main/01/service2/deployments/manifest.yaml $ kubectl get po -nobservability NAME READY STATUS RESTARTS AGE cert-manager-7b8d75c477-45rsc 1/1 Running 0 11m cert-manager-cainjector-6cd8d7f84b-pkjpj 1/1 Running 0 11m cert-manager-webhook-66fc85bd57-dc65n 1/1 Running 0 11m grafana-6f66688c89-fhnnc 1/1 Running 0 119s opentelemetry-operator-controller-manager-7944958b76-ppr26 2/2 Running 0 10m sample-collector-749dcc597b-x6m46 1/1 Running 0 8m34s service1 1/1 Running 0 15m service2 1/1 Running 0 14m tempo-distributed-compactor-7d7b475d8f-pz546 1/1 Running 0 3m7s tempo-distributed-distributor-7c9bcc855c-gv7jr 1/1 Running 0 3m7s tempo-distributed-ingester-0 1/1 Running 0 3m6s tempo-distributed-ingester-1 1/1 Running 0 3m6s tempo-distributed-ingester-2 1/1 Running 0 3m6s tempo-distributed-memcached-0 1/1 Running 0 3m6s tempo-distributed-querier-6599c84b5d-bpvbm 1/1 Running 0 3m7s tempo-distributed-query-frontend-56b5f7fbc8-hp677 1/1 Running 0 3m7s
service1のサービスにポートフォワード(※注3)して、サンプルアプリにリクエストを送ってみます。
# service1 サービスにポートフォワード $ kubectl port-forward svc/service1 8080:8080 -nobservability # service1 サービスにリクエストを送信 $ curl localhost:8080/service
※注3:ここで、「AWS CloudShell」などを使っている場合は、ポートがCloudShell上にフォワードされるので、使用端末のブラウザからフォワードされたポートにアクセスできません。使用端末に「kubectl」をインストールし、端末からKubernetesクラスタに接続できるようにし、端末上から実行してください(もしくは、Serviceの「LoadBalancer」を利用して、パブリックIPから接続できるようにしてください)。
Grafanaのダッシュボードを確認します。Grafanaダッシュボードにはサンプルアプリ同様にポートフォワードしてアクセスします。
# Grafana サービスにポートフォワード $ kubectl port-forward svc/grafana 8090:80 -nobservability
ブラウザで「http://localhost:8090」にアクセスすると、Grafanaのログイン画面が表示されます。
デフォルトのユーザー名は「admin」です。パスワードはKubernetesのSecretリソースとしてデプロイされており、パスワードの内容は次のコマンドで確認できます。
# Grafanaダッシュボードのログインパスワードを表示 $ kubectl get secret --namespace observability grafana -o jsonpath="{.data.admin-password}" | base64 --decode sHEqjnUjNUlt1TmVVuHMTU9P8KUklZNCzxjLbJcW
ログインすると下記の画面が表示されます。
Grafanaにログインできたら、まずはGrafana Tempoを「data source」として追加します。サブメニューの歯車アイコン「Configuration」→「Add data source」を押すと、追加可能なdata sourceの一覧が表示されます。
「Tempo」をクリックします。
HTTPフィールドのURLにGrafana Tempoのエンドポイント「http://tempo-distributed-query-frontend:3100」を指定します。ページ下部の「Save & Test」を押して「Data source is working」が表示されれば、data sourceの追加はOKです。
「Explore」を開いてサンプルアプリのトレースを検索してみましょう。
サンプルアプリで設定したリソース情報(下図の「1.Resource情報」を参照)や、サービス2の処理では4秒間要している(下図の「2.Span情報」を参照)ことも確認できます。
分散トレーシングの見方をもう少し詳しく解説します。下図をご覧ください。
左側にサービス/関数(メソッド)の呼び出し関係が階層構造として表示されています。図の例では、一番上にservice1へのアクセス(エンドポイントは「/service」で、サンプルコード中の「r.GET("/service", service1_sample1)」が該当)がルートのサービスとして表示されており、次にサービスの中で呼び出されるservice1の関数や、さらにはservice1から呼び出されたservice2へのアクセス(/service)や関数の呼び出しも同様に表示されているのが分かります。
また、右側に、各サービス/関数の実行時間がグラフと数値で表示されています。最下部のservice1のスパンは、service2の後に来ているので、一番下のservice1の前にservice2が実行されたことが分かります。
このようにOpenTelemetryを利用して分散トレーシングを取得することで、「サービスの呼び出し階層の中の、どの部分で時間がかかっているか」を簡単に確認することができ、システムのボトルネックや障害の発見に役立ちます。
今回はOpenTelemetryの簡単な概要を紹介し、OpenTelemetryとGrafana Tempoを用いてマイクロサービスの分散トレーシングを試してみました。
アプリのコンテナ化が進み、複数のサービスを組み合わせてマイクロサービス的にアプリを開発することが増えた場合、OpenTelemetryを利用すると、アプリ内の処理だけでなく、サービス間のトレースも簡単に実現できます。
しかし、トレースのみ取得できているだけでは不十分な状況もあります。現場の運用では、「エラーログや異常なメトリクス値を検出して、関連するトレースを解析しに行く」といった流れもよくあるパターンだと思います。このようにObservabilityを見るとき、一つ一つのテレメトリーがバラバラと個別に獲得できているだけでなく、上述したようにテレメトリー間が関連付けられていることも重要だと思います。
次回は、トレース情報をログやメトリクスにひも付けることで、問題があるログや異常なメトリクスを起点に、トレースを使いながら詳しくアプリ/システムを分析する方法を、より実践的に紹介するので、お楽しみに。
Copyright © ITmedia, Inc. All Rights Reserved.