寝て起きて寝る

過ぎたるは及ばざるが如し

MicroProfile OpenTracing with Helidon

はじめに

この記事は、UL Systems Advent Calendar 2019 - 18日目の転記です。

本日は、HelidonTracing MP Guideを参考にEclipse MicroProfile OpenTracingJaegerで分散トレーシングの可視化を試したいと思います。(Tracing MP GuideではZipkinを使用していますが今回はロゴの可愛さからJaegerを使用してみたいと思います。)

最近では、マイクロサービスアーキテクチャでシステム全体を構成する事も多くなってきました。その際、従来のモノリシックなシステムで使用していたメソッドトレースなどでは複数のサービスを跨がる処理に障害や遅延が起きた場合、どこで発生しているのか判別が難しい為、全体を通してトレースする分散トレーシングと呼ばれる仕組みが必要になります。

jaeger.png

Helidon とは

2018年9月にOracle社より発表された、Java向けのマイクロサービス作成に活用できる軽量マイクロサービス開発のためのオープンソースベースのフレームワークです。

Helidonの特徴

  • 2つのプログラミングモデル
    • Helidon SE
      • 関数型スタイル開発、シンプル
      • インジェクション非対応
    • Helidon MP
  • Docker, k8s との連携
  • Oracleによる有償サポートサービス有り
  • GraaalVMへの対応

今回は MaicroProfile をサポートしているHelidon MPをしようします。

Eclipse MicroProfile とは

Eclipse MicroProfileは、マイクロサービス向け Enterprise Java API の仕様です。現在では、JAX-RSJSON-P、CDIなどのAPIが含まれており、構成、メトリック、フォールトトレランスなどのAPIも存在します。 今回使用する Helidon MP は Eclipse MicroProfile 3.2 をサポートしています。

OpenTracing とは

OpenTracingは、API仕様とそれを実装したフレームワークとライブラリ、およびプロジェクトのドキュメントで構成されています。 OpenTracingを使用すると、開発者は特定の製品やベンダーにロックされていないAPIを使用してアプリケーションコードにトレーシング機能を追加できます。

Jaeger とは

DapperとOpenZipkinにインスパイヤーされUber TechnologiesによってリリースされたオープンソースのOpenTracing互換の分散トレースシステムです。 今回は下記の図の OpenTracing API 部分が MicroProfile OpenTracing になりjaeger-client の部分が Helidon から提供されているJaeger用のClientになります。

20181125234256.png

Jaeger のセットアップ

それではまず、Client以外の部分に関してセットアップを行いたいと思います。セットアップに関してはAll-in-oneのDockerイメージが公開されているのでそれを使用します。 (必要最低限のポートのみを指定して起動しています)

$ docker run -d --name jaeger \
   -p 5778:5778 \
   -p 16686:16686 \
   -p 14268:14268 \
   jaegertracing/all-in-one:1.15

各ポートの説明

Helidon の jaeger-client はデフォルトではHTTPの5778を使用しているようです。 (これは設定により変更可能です)

キャプチャ004.JPG

Main サービス の作成

次にメインとなるサービスプロジェクトを Helidon MP Maven archetype の quickstart を使用して作成します。

$ mvn archetype:generate -DinteractiveMode=false \
    -DarchetypeGroupId=io.helidon.archetypes \
    -DarchetypeArtifactId=helidon-quickstart-mp \
    -DarchetypeVersion=1.4.0 \
    -DgroupId=io.helidon.main_service \
    -DartifactId=main_service \
    -Dpackage=io.helidon.main_service

Jaeger Clientを追加

pom.xml に以下の依存関係を追加し Jaeger Client を追加します。

<dependency>
    <groupId>io.helidon.tracing</groupId>
    <artifactId>helidon-tracing-jaeger</artifactId>
</dependency>

トレーシング サービス名の追加

Helidon から Jaeger に送信されるトレーシングデータへ紐付ける為のサービス名をMETA-INF/microprofile-config.propertiesへ指定します。

tracing.service=helidon-main-service

Main サービス を動かして見る

では、ここまで作成した結果を見てみたいと思います。quickstart で作成したプロジェクトは、io.helidon.main_service.MainクラスのMainメソッドを実行する事でサーバが起動するように作成されています。

サーバ起動後にhttp://localhost:8080/greetへアクセスすると quickstart で作成されたサービスが実行され{"message":"Hello World!"}が表示されるはずです。

$ curl http://localhost:8080/greet
{"message":"Hello World!"}

数回アクセスした後に jaeger UI(http://localhost:16686/search) からトレーシングデータを見ると以下のようなトレーシングデータが可視化されるはずです。 キャプチャ003.JPG

さらに各トレースをクリックすると、スパンがリストされているトレース詳細ページが表示されます。 ルートスパンと、トレース内のすべてのスパン間の関係、およびタイミング情報を明確に確認できます。 キャプチャ005.JPG

各スパン行をクリックすると、スパンの詳細も確認できます。 キャプチャ006.JPG

クラスやメソッドレベルでのトレーシング

さらに、MicroProfile OpenTracingで提供されている@Tracedアノテーションを付与することでクラスやメソッドレベルでのトレーシングも可能です。

@Traced
@ApplicationScoped
public class GreetingProvider {
...
}

キャプチャ007.JPG

サービス間を跨いだトレーシング

次にサービス間を跨いだトレーシングを行って見たいと思います。

Second サービスの作成

Main サービスと同様に Helidon MP Maven archetype の quickstart を使用して作成し、Jaeger Client を追加します。

$ mvn archetype:generate -DinteractiveMode=false \
    -DarchetypeGroupId=io.helidon.archetypes \
    -DarchetypeArtifactId=helidon-quickstart-mp \
    -DarchetypeVersion=1.4.0 \
    -DgroupId=io.helidon.second_service \
    -DartifactId=second_service \
    -Dpackage=io.helidon.second_service
<dependency>
    <groupId>io.helidon.tracing</groupId>
    <artifactId>helidon-tracing-jaeger</artifactId>
</dependency>

設定変更

  • greetingで返される文字をSecond サービスからの返却だと識別しやすいようにHello From Second Serviceと変更します。
  • 起動ポートを8081へ変更します。
# Application properties. This is the default greeting
app.greeting=Hello From Second Service

# Microprofile server properties
server.port=8081

動作確認

Second サービスを実行し動作確認をすると以下のような結果になります。

$ curl http://localhost:8081/greet
{"message":"Hello From Second Service World!"}

Main サービスからSecond サービスの呼出

最初に作成したMain サービスからSecond サービスを呼び出すように変更します。

まずは、Second サービスを呼び出すためのRest ClientをMain サービスに作成する必要があります。 Tracing MP Guide とは違いここではMicroProfile Rest Clientを使用して作成してみたいと思います。

package io.helidon.main_service;

import org.eclipse.microprofile.rest.client.inject.RegisterRestClient;

import javax.json.JsonObject;
import javax.ws.rs.GET;
import javax.ws.rs.Path;

@RegisterRestClient(baseUri="http://localhost:8081")
public interface SecondServiceClient {
    @Path("/greet")
    @GET
    JsonObject getDefaultMessage();
}

そして、作成したClientを使用しMain サービスからSecond サービスの呼出を行います。 作成したSecondServiceClientを呼出クラスへインジェクションし元々レスポンスを返していたメソッドへ SecondServiceClient を使用しSecond サービスの呼出を行います。

@Path("/greet")
@RequestScoped
public class GreetResource {
    @Inject
    @RestClient
    SecondServiceClient secondServiceClient;

    @GET
    @Produces(MediaType.APPLICATION_JSON)
    public JsonObject getDefaultMessage() {
        // return createResponse("World");
        return secondServiceClient.getDefaultMessage();
    }

動作確認

ポート8080のMain サービスを実行しSecond サービスが呼ばれていることを確認します。

$ curl http://localhost:8080/greet
{"message":"Hello From Second Service World!"}

分散トレーシングデータの確認

今回の処理は、コンソールからのリクエストがMain サービスを回しSecond サービスを呼び出すと言う構成になっています。 キャプチャ009.JPG

このリクエストのトレーシングデータをJaeger UIで見てみると全体でそれだけの処理時間がかかり、処理の流れが確認できます。 キャプチャ008.JPG

まとめ

今回はEclipse MicroProfile OpenTracingを使用し分散トレーシングを試してみました。 分散システムを採用した場合に分散トレーシングの可視化が出来ている状態であれば、仮にシステムに障害や遅延が発生しても把握しやすいのではないでしょうか。

また、Eclipse MicroProfile には OpenTracing の他にもEclipse MicroProfile MetricsやEclipse MicroProfile Fault Toleranceなどの仕様が有り Helidon もそれらをサポートしています。現在JavaEEを使用しているアプリケーションからの移行など Spring Framework 以外の選択として検討する事が出来るのではないでしょうか。