opentelemetry-ruby で Rails アプリケーションをトレーシングしてみる

## はじめに

まだ beta 版のようですが、気になってた [opentelemetry-ruby](https://github.com/open-telemetry/opentelemetry-ruby) を使って Rails アプリケーションへのリクエストをトレースし、[Jaeger](https://www.jaegertracing.io/) で可視化するところまでやってみようと思います。

## [Opentelemetry](https://opentelemetry.io/) とは
[OpenCensus](https://opencensus.io/) と [OpenTracing](https://opentracing.io/) の２つのプロジェクトが統合されたもので、Tracing だけでなく Metrics、Logs といった Observavility 全体を実現するための標準化された API およびライブラリが提供されています。今回は、Opentelemetry のなかの Tracing を使ってみようと思います。

公式ページの絵がかわいくてほっこりしますよね。

なお、Opentelemetry のアーキテクチャについては、New Relic 社の『[OpenTelemetry: 計装器を長く使い続けるために](https://blog.newrelic.co.jp/product-news/what-is-opentelemetry/)』が図示されていてわかりやすかったです。

## 環境
Docker for Mac
Ruby on Rails 6.0.3.4
MySQL 5.7
Jaeger 1.20

## Jaeger を起動する
[Jaeger](https://www.jaegertracing.io/) の [Getting Started](https://www.jaegertracing.io/docs/1.20/getting-started/) の All in One を使用します。

それぞれのポート役割については [こちら](https://www.jaegertracing.io/docs/1.21/getting-started/#all-in-one) に記載されています。

```sh
docker run --rm --name jaeger \
  -p 5775:5775/udp \
  -p 6831:6831/udp \
  -p 6832:6832/udp \
  -p 5778:5778 \
  -p 16686:16686 \
  -p 14268:14268 \
  -p 14250:14250 \
  -p 9411:9411 \
  jaegertracing/all-in-one:1.20
```

これで Jaeger の GUI に下記の URL からアクセスできるようになりました。

http://localhost:16686/search

## Rails アプリ

Rails アプリケーションは、 [Rails チュートリアル](https://railstutorial.jp/)の [Toy アプリケーション](https://railstutorial.jp/chapters/toy_app?version=6.0#cha-a_toy_app)で作ったものを使用します。

今回は簡単にユーザの一覧と詳細ページにアクセスし、トレースできるか確認してみようと思います。

![スクリーンショット 2020-11-26 1 40 00](https://user-images.githubusercontent.com/15954325/100256641-5d29a480-2f88-11eb-80f7-929c4d528d31.png)

## opentelemetry-ruby の使い方

opentelemetry-ruby に関連する Gem は全てこのリポジトリにあるようです。

https://github.com/open-telemetry/opentelemetry-ruby

そして、Jaeger にトレースデータを Export するには、opentelemetry-ruby リポジトリ内にある [opentelemetry-exporter-jaeger](https://github.com/open-telemetry/opentelemetry-ruby/tree/master/exporter/jaeger) を Gem インストールすれば良さそうです。

また opentelemetry-ruby リポジトリ内に [instrumentation](https://github.com/open-telemetry/opentelemetry-ruby/tree/master/instrumentation) ディレクトリがあり、Rails や mysql2、faraday など様々な [Instrumentation](https://railsguides.jp/active_support_instrumentation.html) 機能が用意されてるようです。

今回は、Rails と mysql2 の Instrumentation 機能を使ってみようと思います。

これを使うことで、Controller・Action の処理時間や実行されたクエリなどがトレースできそうです。

Rails の Instrumentation は [opentelemetry-instrumentation-rails](https://github.com/open-telemetry/opentelemetry-ruby/tree/master/instrumentation/rails)、mysql2 の Instrumentation は [opentelemetry-instrumentation-mysql2](https://github.com/open-telemetry/opentelemetry-ruby/tree/master/instrumentation/mysql2) を Gem インストールすれば良さそうですが、opentelemetry-instrumentation-rails はまだ [rubygems.org](https://rubygems.org/) に公開されていないようでした。
 
なので、opentelemetry-instrumentation-rails は opentelemetry-ruby を git clone して、Gemfile で path 指定をして使ってみようと思います。
 
（執筆時点（11/27）では公開されていませんでしたが、現在は[公開](https://rubygems.org/search?utf8=%E2%9C%93&query=opentelemetry-instrumentation-rails)されているようです。）

## 実装

opentelemetry-ruby を git clone してきます。

```sh
$ git clone git@github.com:open-telemetry/opentelemetry-ruby.git
```

次に、Rails アプリ の Gemfile に下記を追加します。

```ruby:Gemfile
gem 'opentelemetry-sdk'
gem 'opentelemetry-exporter-jaeger'
gem 'opentelemetry-instrumentation-mysql2'
gem 'opentelemetry-instrumentation-rails', path: '/path/to/opentelemetry-ruby/instrumentation'
```

opentelemetry-instrumentation-rails だけ clone してきた path を指定します。

また Docker を使う場合は、clone してきた opentelemetry-ruby を Volume マウントした先の path を指定します。

ここで、path を`/path/to/opentelemetry-ruby/instrumentation/rails`ではなく、`/path/to/opentelemetry-ruby/instrumentation`とすることで、依存関係にある [opentelemetry-instrumentation-rack](https://github.com/open-telemetry/opentelemetry-ruby/tree/master/instrumentation/rack) も clone してきた opentelemetry-ruby の`instrumentation/rack`から取得してきてくれます。

次に、config/appliction.rb に下記を追加します。

```ruby:config/appliction.rb
require 'opentelemetry/sdk'
require 'opentelemetry/exporter/jaeger'

OpenTelemetry::SDK.configure do |c|
  c.use 'OpenTelemetry::Instrumentation::Rails'
  c.use 'OpenTelemetry::Instrumentation::Mysql2'
  c.add_span_processor(
    OpenTelemetry::SDK::Trace::Export::BatchSpanProcessor.new(
      exporter: OpenTelemetry::Exporter::Jaeger::AgentExporter.new(host: 'host.docker.internal', port: 6831)
    )
  )
  c.service_name = 'toy-app'
  c.service_version = '0.1.0'
end
```

この use の部分ですが、

```ruby 
c.use 'OpenTelemetry::Instrumentation::Rails'
c.use 'OpenTelemetry::Instrumentation::Mysql2'
```

use_all とも書けるようです。
 
```ruby
c.use_all
```

use_all すると利用可能な Instrumentation を全て use してくれるようです。

そして、Jaeger にトレースデータを Export する方法は、UDP を介した ThriftCompact 形式 で Jaeger Agent に Export する方法と、HTTP を介した ThriftBinary 形式で Jaeger Collector に Export する方法の2種類用意されているようです。

今回は、Jaeger の[アーキテクチャ](https://www.jaegertracing.io/docs/1.21/architecture/#components)的に自然な UDP で Jaeger Agent に Export する方法でやってみます。

host は、Docker for Mac で検証しているため`host.docker.internal`を指定します。

port は、Jaeger の UDP の ThriftCompact 形式の公開ポートである 6831 を指定します。

```ruby
OpenTelemetry::Exporter::Jaeger::AgentExporter.new(host: 'host.docker.internal', port: 6831)
```

service_name は、アプリケーションの名前を指定します。

service_version は、アプリケーションのバージョンを指定します。今回は適当に 0.1.0 を指定しました。

実装はこれで完了です。

## 動作確認

それでは bundle install 後、Rails アプリケーションを再起動して、Toy アプリケーション の ユーザ一覧とユーザの詳細ページにアクセスしてみます。

![スクリーンショット 2020-11-26 1 40 00](https://user-images.githubusercontent.com/15954325/100256641-5d29a480-2f88-11eb-80f7-929c4d528d31.png)

アクセスすると、Jaeger の Service のところに service_name で設定した toy-app が出現するので選択して、Find Traces をクリックします。

![スクリーンショット 2020-11-26 9 46 55](https://user-images.githubusercontent.com/15954325/100295202-60468400-2fcc-11eb-96a7-ba4098561b1f.png)

ちゃんとトレースできてそうですね。

![スクリーンショット 2020-11-26 10 00 48](https://user-images.githubusercontent.com/15954325/100297344-d00b3d80-2fd1-11eb-8a1c-e44f3cf3e90e.png)

SQL も確認できました。

 **<ユーザ一覧>** 

![スクリーンショット 2020-11-26 10 02 09](https://user-images.githubusercontent.com/15954325/100297347-d26d9780-2fd1-11eb-9c6c-c2be197d9826.png)

 **<ユーザ詳細>** 

![スクリーンショット 2020-11-26 10 27 23](https://user-images.githubusercontent.com/15954325/100297432-05179000-2fd2-11eb-9fa8-73e5a0015b86.png)

うまくできました！

## さいごに
とても簡単に opentelemetry-ruby を使って Rails アプリケーションのトレースデータを Jaeger で確認することができました！

公式リリースが待ち遠しいですね！

明日は、[@tsukasa_oishi](https://qiita.com/tsukasa_oishi) さんの「どれだけリクエストをさばけるのかを待ち行列理論で考えてみた」です。お楽しみに!!