OpenLiberty with MicroProfile Telemetryで手軽に分散トレーシング

2023年4月頭にOpen Liberty 23.0.0.3がGAしました。
このリリースではJakarta EE 10とMicroProfile 6への正式対応が発表されています。

https://openliberty.io/blog/2023/04/04/23.0.0.3.html

MicroProfile 6への対応の一環として、オブザーバビリティのAPI・実装を提供するOpenTelemetryを使用したMiroProfile Telemetryの実装が提供されました。
この記事ではOpen LibertyでMicroProfile Telemetryを使って簡易的な分散トレーシングを試します。


## 本記事で使用するコンポーネント

本記事では以下のコンポーネント・バージョンで動作確認を行なっています。
各コンポーネントの別バージョンでは動作しない可能性がありますので、本番投入する際には事前に手元で動作確認することをお勧めします。

|コンポーネント名|バージョン|
|:--|:--|
|JDK|OpenJDK 17.0.6 (IBM Semeru Runtime Open Edition)|
|Open Liberty|23.0.0.3|
|Podman|4.4.4|
|Jaeger|1.44|

```bash
$ java -version
openjdk version "17.0.6" 2023-01-17
IBM Semeru Runtime Open Edition 17.0.6.0 (build 17.0.6+10)
Eclipse OpenJ9 VM 17.0.6.0 (build openj9-0.36.0, JRE 17 Mac OS X aarch64-64-Bit 20230117_255 (JIT enabled, AOT enabled)
OpenJ9   - e68fb241f
OMR      - f491bbf6f
JCL      - 927b34f84c8 based on jdk-17.0.6+10)
```


## Open Libertyにおける分散トレーシングの実現方法

Open Libertyでは次の3つの方法で分散トレーシングを実装する方法が公式ドキュメントで紹介されています。

1. MicroProfile Telemetryの自動instrumentation
1. MicroProfile Telemetryの手動instrumentation
1. OpenTelemetry Java Agentを用いた自動instrumentation

https://openliberty.io/docs/latest/microprofile-telemetry.html

1は設定のみで実現可能な方法であり、本記事でもこの実現方法を解説します。

ただし1の方法はJakarta Restful web service (JAX-RS) アプリケーションのみ使用可能です。トレースで取得できる情報量も最低限のものしかありません。HTTP以外のプロトコルを分散トレーシングの対象にしたい場合や詳細な情報を取りたい場合は2または3の方法を検討する必要があります。

※ 参考: Java Agentを用いた手法は別の記事をご参照ください

https://ponzmild.hatenablog.com/entry/2022/06/12/134631


## 分散トレーシングの実装

それではOpen LibertyとMicroProfile Telemetryで分散トレーシングを実装しましょう。
次の6ステップで実装を進めます。

1. アプリケーションの準備
1. 依存関係のインストール
1. Featureのインストール
1. MicroProfile Telemetryの設定追加
1. 分散トレーシング基盤の起動
1. アプリ稼働確認

Jakarta EEで実装された以下2本のアプリを題材とします。

|アプリケーション|トレース上の名称|説明|
|:--|:--|:--|
|フロントエンドUI|liberty-frontend-ui|ブラウザ上でリクエストを受け付け、バックエンド・サービスにAPIコールを送信するJakarta Facesアプリケーション。|
|バックエンドサービス|liberty-backend-service|HTTPでリクエストを受け付け、データベース (Derby DB)を操作するJakarta Restful web serviceアプリケーション。|

### (1) アプリケーションの準備

この記事ではアプリの中身が主題ではないため、Open Libertyのマイクロサービス実装で紹介されている既存のアプリを流用します。

次のGitリポジトリをGitHubから任意のディレクトリにクローンします。
リポジトリ内の `finish` ディレクトリにある2つのアプリケーション (`frontendUI` と `backendServices`) を書き換えながら実装します。

https://github.com/openliberty/guide-jpa-intro

### (2) 依存関係のインストール

Apache Mavenでアプリケーションに必要な依存関係をインストールします。
Mavenの設定ファイル `pom.xml` に次の設定を加えます。
また、MavenのJavaバージョンもJDKに合わせておきます。

必要箇所のみ次の通り変更・追加します。

```xml
<project
  xmlns="http://maven.apache.org/POM/4.0.0"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

  <properties>
    <!-- 1.8 → 17に変更 -->
    <maven.compiler.source>17</maven.compiler.source>
    <maven.compiler.target>17</maven.compiler.target>
  </properties>

  <dependencies>
    <!-- Jakarta EE 10 -->
    <dependency>
      <groupId>jakarta.platform</groupId>
      <artifactId>jakarta.jakartaee-api</artifactId>
      <version>10.0.0</version>
      <scope>provided</scope>
    </dependency>

    <!-- MicroProfile 6 -->
    <dependency>
      <groupId>org.eclipse.microprofile</groupId>
      <artifactId>microprofile</artifactId>
      <version>6.0</version>
      <type>pom</type>
      <scope>provided</scope>
    </dependency>
  </dependencies>

  <!-- ...後略... -->
</project>
```

### (3) Featureのインストール

MicroProfile Telemetryの実装はOpen LibertyのFeatureという形で提供されます。
Open Libertyの設定ファイル `server.xml` に必要なFeatureと、MicroProfile Telemetryの実装を提供する `mpTelemetry-1.0` Featureを追加します。

フロントエンドUI: `frontendUI/src/main/liberty/config/server.xml`

```xml
<server description="Frontend UI application">
  <featureManager>
    <!-- Webアプリケーション部分 -->
    <feature>restfulWS-3.1</feature>
    <feature>jsonb-3.0</feature>
    <feature>jsonp-2.1</feature>
    <feature>cdi-4.0</feature>
    <feature>faces-4.0</feature>
    <feature>mpConfig-3.0</feature>
    <feature>mpRestClient-3.0</feature>
    <!-- MicroProfile Telemetry -->
    <feature>mpTelemetry-1.0</feature>
  </featureManager>

  <!-- 他の設定は省略 -->
</server>
```

バックエンドサービス: `backendServices/src/main/liberty/config/server.xml`

```xml
<server description="Frontend UI application">
  <featureManager>
    <!-- Webアプリケーション部分 -->
    <feature>restfulWS-3.1</feature>
    <feature>jsonb-3.0</feature>
    <feature>jsonp-2.1</feature>
    <feature>cdi-4.0</feature>
    <feature>persistence-3.1</feature>
    <feature>mpConfig-3.0</feature>
    <!-- MicroProfile Telemetry -->
    <feature>mpTelemetry-1.0</feature>
  </featureManager>

  <!-- 他の設定は省略 -->
</server>
```

### (4) MicroProfile Telemetryの設定追加

このパートが最も重要です。

MicroProfile Telemetryではトレース情報の送信先やトレース情報をMicroProfile Configまたは環境変数で指定します。
今回はMicroProfile Config Featureをインストール済みのためMicroProfile Configの設定ファイル `microprofile-config.properties` に設定値を追加します。

フロントエンドUI: `frontendUI/src/main/webapp/META-INF/microprofile-config.properties`

```properties
# バックエンドサービスの呼び出し先URL
io.openliberty.guides.event.client.EventClient/mp-rest/url=http://localhost:5050

# MicroProfile Telemetry
otel.sdk.disabled=false
otel.traces.exporter=otlp
otel.exporter.otlp.endpoint=http://localhost:4317
otel.service.name=liberty-frontend-ui
```

バックエンドサービス: `backendServices/src/main/webapp/META-INF/microprofile-config.properties`

```properties
# MicroProfile Telemetry
otel.sdk.disabled=false
otel.traces.exporter=otlp
otel.exporter.otlp.endpoint=http://localhost:4317
otel.service.name=liberty-backend-service
```

デフォルトではMicroProfile Telemetryの機能はオフになっているため、 `otel.sdk.disabled=false` で有効化します。`otel.exporter.otlp.endpoint` で設定しているホスト名とポート番号は次のステップで立てるJaegerのエンドポイントを指定しています。その他の設定値はOpen Libertyのドキュメント、またはOpenTelemetry Java SDKのドキュメントを参照してください。

- [MicroProfile Config properties for MicroProfile Telemetry](https://openliberty.io/docs/latest/microprofile-config-properties.html#telemetry)
- [OpenTelemetry SDK Autoconfigure - OTLP exporter](https://github.com/open-telemetry/opentelemetry-java/blob/main/sdk-extensions/autoconfigure/README.md#otlp-exporter-span-metric-and-log-exporters)

### (5) 分散トレーシング基盤の起動

トレース情報を保管・参照する分散トレーシング基盤をローカルで起動します。
この記事ではオープンソースの分散トレーシング基盤では [Jaeger](https://www.jaegertracing.io/) をコンテナで準備します。

次のコマンドをターミナルで実行してコンテナを起動します。
OpenTelemetryのインターフェイスでトレース情報を送信するため、環境変数 `COLLECTOR_OTLP_ENABLED` を必ず指定してください。トレース情報を受け付けるエンドポイントとJaeger UIのポートも開放しておきます。

```bash
podman run --name jaeger --rm -d \
  -e COLLECTOR_OTLP_ENABLED=true \
  -p 16686:16686 \
  -p 4317:4317 \
  -p 4318:4318 \
  docker.io/jaegertracing/all-in-one:1.44
```

> この記事ではOSSを使った例を提示していますが、実運用では分散トレーシング基盤にInstanaやNew RelicなどのSaaSを使用することも良い選択肢です。
> これらのSaaSの利用方法は次のドキュメントを参考にしてください。
> Instana: https://www.ibm.com/docs/ja/instana-observability/current?topic=apis-opentelemetry
> New Relic: https://docs.newrelic.com/jp/docs/more-integrations/open-source-telemetry-integrations/opentelemetry/get-started/opentelemetry-tutorial-java/

### (6) アプリ稼働確認

2つのアプリケーションを起動します。次のMavenコマンドでアプリケーションをOpen Libertyの開発モードで起動します。「Liberty is running in dev mode.」とコンソールに表示されていれば起動OKです。

```bash
# フロントエンドUI
cd frontendUI
mvn clean liberty:run

# バックエンドサービス
cd backendServices
mvn clean liberty:run
```

ブラウザで http://localhost:9090/eventmanager.jsf を開いて適当に参照、作成、更新、削除と操作します。

![スクリーンショット 2023-04-21 16.44.14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/137947/b100147d-8ab0-cd81-6e13-e00ee700bff8.png)

### (7) トレース情報の確認

アプリケーションを適当に操作していればJaegerにトレース情報が送信されているはずです。
ブラウザで http://localhost:16686 を開いてJaegerのUIを表示します。

「Search」タブの左メニュー「Service」に(6)で起動したアプリケーションがドロップダウンに表示されます。操作の起点である「liberty-frontend-ui」を選択して「Search」ボタンを押下するとトレース情報の一覧が表示されます。

![スクリーンショット 2023-04-21 16.37.55.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/137947/e44becba-200a-bbc0-a8e7-9d9005fc0f52.png)

とあるGETリクエストのトレース詳細情報を見ると、アプリケーションの呼び出し階層が表示されています。
サービスごとのスパンはドリルダウン可能であり、HTTPステータスコードは200、OpenTelemetry Java SDK 1.19.0を使用し、バックエンドの呼び出しはApache HTTP Clientであることが分かります。
トレース情報取得・送信にOpenLibertyが実装したMicroProfile Telemetryを使用されていることも分かりますね。(`otel.library.name = io.openliberty.microprofile.telemetry`)

![スクリーンショット 2023-04-21 16.39.05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/137947/1e7f9033-4f0e-3e34-6a13-0d1956e2a1ae.png)

バックエンドサービスはDBアクセスも実施していますが、トレース情報では表示されません。
サービスの中身の呼び出し階層を確認したい場合は、MicroProfile Telemetryの手動instrumentationやJava Agentで対応することになります。実装方法はぜひドキュメントを参照して試してみてください！

## 参考

- [Open Liberty - MicroProfile Telemetry Feature](https://openliberty.io/docs/latest/reference/feature/mpTelemetry-1.0.html)
- [Open Liberty - Enable distributed tracing with MicroProfile Telemetry](https://openliberty.io/docs/latest/microprofile-telemetry.html)