OpenTelemetry + Jaeger でトレーシングを試してみた
OpenTelemetryとJaegerはTechnology Radar Vol.22でTRIALとして取り上げられており、今後に向けて触れておきたい要素です。
今回はOpenTelemetryの中でも OpenTelemetry Auto-Instrumentation for Javaを使って、すでにリリースされているprismatixのような既存サービスへ簡単に導入ができる方法を試してみます。
またOpenTelemetry Auto-Instrumentation for Javaで対応しているフレームワークとライブラリは supported-java-libraries-and-frameworks を見てください。
注) 執筆時点でOpenTelemetry Auto-Instrumentation for JavaはBeta版のため、検証以外の利用にはご注意ください
構成
検証していく構成はSpring Boootを用いて次のように2つのマイクロサービスが連携するように作成します。
マイクロサービスの作成
マイクロサービスはSpring Bootを使用して構築します。ひな形としてSpring Initializr を使用して作成します。
demo1 の作成
外部からリクエストを受けて、demo2へリクエストを投げる処理を記載します。
- settings.gradle
アプリケーションの名前が重複するとJARを見た際に区別がつかなくなるため、わかりやすい名前にします。
rootProject.name = 'demo1'
- build.gradle
レスポンスデータを作成する際に
ImmutableMap
を使いたいため以下を追加します。dependencies { // ... 省略 ... compile group: 'com.google.guava', name: 'guava', version: '29.0-jre' // ... 省略 ... }
- application.properties
アプリケーションを複数起動するため、ポートや名前は重複しないように設定します。
// Springの起動PORT server.port=8080 // アプリケーション名 spring.application.name = demo1 // demo2のリクエスト送信URL backendBaseUrl=http://localhost:8081/api
- APIエンドポイント作成
リクエストを受けて demo2 へリクエストを送り、その結果と自身のアプリケーション名、現在時刻を返す処理を記載します。
//packageとimportの記載は省略 @EnableAutoConfiguration @RestController public class ApiController { @Autowired RestTemplate restTemplate; @Value("${backendBaseUrl}") private String backendBaseUrl; @Value("${spring.application.name}") private String applicationName; @RequestMapping("/link") public Map<String, String> printDate(@RequestHeader Map<String, String> headers) throws JsonProcessingException { String result = restTemplate.getForObject(backendBaseUrl, String.class); Map<String, String> articles = ImmutableMap.of( "appName", this.applicationName, "dateTime", new Date().toString(), "backendResult", result ); return articles; } @Bean RestTemplate restTemplate() { return new RestTemplate(); } }
demo2 の作成
demo1と基本的には同じのため差分のAPIエンドポイントのみ記載します。
- APIエンドポイント作成
demo1 からリクエストを受けて、自身のアプリケーション名と現在時刻を返す処理を記載します。
//packageとimportの記載は省略 @EnableAutoConfiguration @RestController public class ApiController { private static final Marker MARKER = MarkerFactory.getMarker("AUDIT"); private static final Logger logger = LoggerFactory.getLogger(ApiController.class); @Value("${spring.application.name}") private String applicationName; @RequestMapping("/api") public Map<String, String> printDate(@RequestHeader Map<String, String> headers) throws JsonProcessingException { logger.info(MARKER, ImmutableMap.of("RequestHeader", headers).toString()); Map<String, String> articles = ImmutableMap.of("appName", this.applicationName, "dateTime", new Date().toString()); return articles; } }
Spring Boot をビルド
demo1 と demo2 をそれぞれ gradlew build
でビルドします。ビルドされた JARは build/libs/
内に出力されます。
環境構築
1. OpenTelemetry Auto-Instrumentation for Javaをダウンロード
OpenTelemetry Auto-Instrumentation for Javaのリリースページ より以下の2つをダウンロードします
- opentelemetry-auto-0.3.0.jar
- opentelemetry-auto-exporters-jaeger-0.3.0.jar
ダウンロードしたファイルは、わかりやすいところに配置します。今回は以下のような配置としました。
├── demo1 (Spring Bootで今回作成したアプリケーション) ├── demo2 (Spring Bootで今回作成したアプリケーション) ├── opentelemetry-auto-0.3.0.jar ├── opentelemetry-auto-exporters-jaeger-0.3.0.jar └── opentelemetry-auto-exporters-logging-0.3.0.jar ([おまけ]の項で使います)
注) 執筆時点で最新版のBeta v0.3.0を使用します。
2. Jaegerの起動
DockerでJaegerを起動します1。起動後にブラウザから http://localhost:16686 でトレーシング結果を確認できます。
docker run --rm -it --name jaeger \ -p 16686:16686 \ -p 14250:14250 \ jaegertracing/all-in-one:1.18
3. アプリケーションの起動
アプリケーションの demo1 と demo2 をそれぞれ以下のコマンドで起動します2。
- demo1 の起動
java -javaagent:opentelemetry-auto-0.3.0.jar \ -Dota.exporter.jar=opentelemetry-auto-exporters-jaeger-0.3.0.jar \ -Dota.exporter.jaeger.endpoint=localhost:14250 \ -Dota.exporter.jaeger.service.name=demo1 \ -jar demo1/build/libs/demo1-0.0.1-SNAPSHOT.jar
- demo2 の起動
java -javaagent:opentelemetry-auto-0.3.0.jar \ -Dota.exporter.jar=opentelemetry-auto-exporters-jaeger-0.3.0.jar \ -Dota.exporter.jaeger.endpoint=localhost:14250 \ -Dota.exporter.jaeger.service.name=demo2 \ -jar demo2/build/libs/demo2-0.0.1-SNAPSHOT.jar
Jaeger でトレーシング
demo1 と demo2 の両方が起動できたことを確認し、 http://localhost:8080/link でアプリケーションにリクエストを送信します。
そしてブラウザで Jaeger を表示させると、このようにトレースされた結果が確認できます。
また個別のリクエストをクリックして開くことで、demo1
-> demo2
にリクエストが送られている様子がわかり、各処理時間も確認することもできます。
このように既存のアプリケーションのJARに改修を入れることなく、トレースできるため使いやすいのではないでしょうか。(簡単に設定できるため記事の半分以上が、アプリケーションを作成する内容になってしまいました。)
[おまけ] コンソールにログ出力する
OpenTelemetry Auto-Instrumentation for Javaのリリースページ より以下を追加でダウンロードします。
- opentelemetry-auto-exporters-logging-0.3.0.jar
実行
Jaegerに送信していたときと同様にJARの実行時パラメーターに指定しますが、 -Dota.exporter.jar
を先ほどダウンロードしたJARに変えるだけです。
# demo1 java -javaagent:opentelemetry-auto-0.3.0.jar \ -Dota.exporter.jar=opentelemetry-auto-exporters-logging-0.3.0.jar \ -jar demo1/build/libs/demo1-0.0.1-SNAPSHOT.jar # demo2 java -javaagent:opentelemetry-auto-0.3.0.jar \ -Dota.exporter.jar=opentelemetry-auto-exporters-logging-0.3.0.jar \ -jar demo2/build/libs/demo2-0.0.1-SNAPSHOT.jar
ログ
Jaegerの際と同様に demo1 のエンドポイントにリクエストを送ると、それぞれ以下のようなログが出力されます。このログはスパンの名前とその属性を標準出力しているのみであり、テストやデバッグ用だそうです 3 。またログのフォーマットに関する記載が見つけられなかったため、詳細がわかりませんがログ出力だけでトレーシングは現時点で難しそうです。
注) 実際のログは1リクエストが1行にまとめて出力されますが、読みづらいため適宜改行を入れています。
- demo1
Logging Exporter: HTTP GET 1f32fe36ced2c95f net.peer.name="localhost" http.url="http://localhost:8081/api" http.status_code=200 net.peer.port=8081 http.method="GET" Logging Exporter: ApiController.printDate edceaceb2608f708 Logging Exporter: /link ed9a1a1116d4fef5 http.status_code=200 net.peer.port=50087 servlet.path="/link" servlet.context="" http.url="http://localhost:8080/link" net.peer.ip="0:0:0:0:0:0:0:1" http.method="GET" span.origin.type="org.apache.catalina.core.ApplicationFilterChain"
- demo2
Logging Exporter: ApiController.printDate adcecb77d9cf7351 Logging Exporter: /api 21c6c3ce6f5fd64d http.status_code=200 net.peer.port=50089 servlet.path="/api" servlet.context="" http.url="http://localhost:8081/api" net.peer.ip="127.0.0.1" http.method="GET" span.origin.type="org.apache.catalina.core.ApplicationFilterChain"