Rails で OpenTelemetry の Tracing を試す

OpenTelemetry の Ruby 実装である opentelemetry-ruby も開発が進んでおり、Tracing に関しては Stable となっていたので試しに Rails アプリケーションに導入して触ってみた。

データの送り先として Jaeger を準備する

まずは Tracing データの送り先として Jaeger を起動しておく。Getting Started を読みながら、All in One のイメージを実行する。

docker run --rm --name jaeger \
  -p 14268:14268 \
  -p 16686:16686 \
  jaegertracing/all-in-one:1.53

公開するポートとしては、データの投げ込み先として jaeger.thrift の 14268、 WebUI を表示するための 16686 を指定しておいた。

Rails に opentelemetry-ruby を組み込む

こちらも Getting Started を読みながら、Rails アプリケーションに opentelemetry-ruby を仕込んでいく。

まずは必要な gem を Gemfile に追記し、 bundle install する。 今回は Jaeger に飛ばしたいので、opentelemetry-exporter-jaegerも忘れずに指定しておく。

gem 'opentelemetry-sdk'
gem 'opentelemetry-instrumentation-all'
gem 'opentelemetry-exporter-jaeger'

次に config/initializers に設定ファイルを配置する。

# config/initializers/otel.rb

OpenTelemetry::SDK.configure do |c|
  c.service_name = 'rails_test'
  c.service_version = '0.1.0'

  c.resource = OpenTelemetry::SDK::Resources::Resource.create(
    OpenTelemetry::SemanticConventions::Resource::DEPLOYMENT_ENVIRONMENT => Rails.env.to_s,
    OpenTelemetry::SemanticConventions::Resource::HOST_NAME => "localhost",
  )

  c.add_span_processor(
    OpenTelemetry::SDK::Trace::Export::BatchSpanProcessor.new(
      OpenTelemetry::Exporter::Jaeger::CollectorExporter.new(endpoint: 'http://localhost:14268/api/traces')
    )
  )

  c.use_all
end

resource にはホスト名や環境名をセットしておいた。独自に resource 定義していくよりはなるべく Semantic Conventions として定義されているものを使うと良いのだろう。opentelemetry-ruby でも定数が用意されていたのでそれを使った。

exporter の設定には先程起動しておいた Jaeger のエンドポイントを指定しておく。

最後に c.use_all を設定してあるが、これで利用可能な instrumentation を自動でセットアップしてくれるらしい。

Rails アプリケーションを起動して送信されたデータを確認してみる

Rails アプリケーションを起動して適当なエンドポイントを数回叩いたあと、Jaeger が動いている http://localhost:16686/search にアクセスして確認してみる。 設定した service 名で無事データが送信されており、エンドポイントごとに検索できるようになっていた。

結果を選択して詳細を見てみると、データベースの SELECT にかかった時間なども確認できるようになっていた。

use_all するとどのような instrumentation が動くのか

見よう見まねで use_all を指定していたが、この設定でどのような instrumentation が動くのか気になるところである。 まず use_all した状態でサーバを起動すると、ログに大量のメッセージが出力されていることに気付く。

WARN -- : Instrumentation: OpenTelemetry::Instrumentation::Gruf failed to install
WARN -- : Instrumentation: OpenTelemetry::Instrumentation::Trilogy failed to install
INFO -- : Instrumentation: OpenTelemetry::Instrumentation::ActiveSupport was successfully installed with the following options {}
--- これが延々と続くので以下略 ---

基本的にはインストールされている instrumentation の gem を全て読み込みセットアップしようとするらしい。 今回だと opentelemetry-instrumentation-all という gem をインストールしていたので、opentelemetry-ruby-contrib にある全てをセットアップしようと試行している様子だった。

failed to install と表示されているように、当然アプリケーションに導入していないライブラリの instrumentation セットアップには失敗している。導入済みライブラリの検出にはクラス定義の有無を見ているようだ。(Sidekiq の 例)

動作に支障は無さそうなものの failed to install と大量に出力されるのはあまり気持ち良くないので、インストールする gem を絞るか、以下のように use_all をやめて個別に instrumentation を指定していくのが良いのかもしれない。

  # c.use_all
  c.use 'OpenTelemetry::Instrumentation::Rails'
  c.use 'OpenTelemetry::Instrumentation::PG'
  c.use 'OpenTelemetry::Instrumentation::Sidekiq'

これで使い方はなんとなく分かってきたので、ちゃんとした環境に仕込んで動かしてみたいところだ。