MicroProfileによるマイクロサービスの分散トレース(Part1):MicroProfile OpenTracingとJaegerによる可視化
富士通技術者ブログ~Javaミドルウェア~

2019年8月2日 初版
2019年10月25日 更新
数村 憲治

はじめに

マイクロサービスアーキテクチャ(MSA)は、たくさんのサービスがRESTなどによりコミュニケーションを行うアーキテクチャです。このようなアーキテクチャでは、従来のモノリシックシステムで使われていたメソッドトレースのような技術はそのままでは利用できず、分散トレースと呼ばれる技術が必要になります。分散トレース技術とは、1つのリクエストが複数のサービスにまたがって処理をされる時に、どのサービスが呼び出されているか、どれぐらいの時間で処理されているか、などを調査できる技術になります。MSAではREST呼出しを分散トレースする技術が必要になります。
OpenTracing」は、分散トレース技術の一つで、現在デファクトスタンダードになりつつあります。MicoroProfileでは、OpenTracingに対応した仕様、「MicroProfile OpenTracing」を提供しています。以下に、MicroProfile OpenTracingの使用方法を、可視化ツールの一つである「Jaeger」とともに紹介します。
なお、ここで紹介するプログラムの完全なソースコードは、以下で参照できます。

RESTサービスの作成

3つのサービス、ServiceA、ServiceB、ServiceCをJAX-RSを使って作成し、それぞれ別のプロセスとして動作させます。ServiceAはServiceBを呼び、ServiceBはServiceCを呼ぶという関係(図1)になります。

図1

ServiceAのソースは以下のように、GETメソッドのエントリーポイントを持ち、JAX-RSクライアントAPIを使用し、ServiceBの呼び出しを行います。

@Path("service")
public class ServiceA {
  @GET
  public String accept(@QueryParam("option") String option) {
    String result = ClientBuilder
      .newClient()
      .target("http://localhost:7070/service?option=" + option) // call ServiceB
      .request()
      .get(String.class);
    return result;
  }
}

ServiceBのソースは以下のように、GETメソッドのエントリーポイントを持ち、JAX-RSクライアントAPIを使用し、ServiceCの呼び出しを行います。

@Path("service")
public class ServiceB {

  @Inject
  ServiceClient client;
  
  @GET
  public String accept(@QueryParam("option") String option) {
    return client.call(option);
  }
}

@RequestScoped
class ServiceClient {
  public String call(String option) {
    String result = ClientBuilder
      .newClient()
      .target("http://localhost:6060/service?option=" + option) // call ServiceC
      .request()
      .get(String.class);
    return result;
  }
}

また、ServiceCのソースでは、optionパラメーターの値によって、意図的に500msスリープするコードを入れておきます。

public class ServiceC {
  @GET
  public String accept(@QueryParam("option") String option) {
    if (option.equals("sleep"))
      try {
        Thread.sleep(500);   // intentionally delay response
      } catch (Exception e) {}
    return "Service accepted : " + option + "\n";
  }
}

これらのソースは、JAX-RSのAPIしか使っておらず、OpenTracing用のコードは一切含まれていません。すなわち、既存のJAX-RSのプログラムは、何も変更しなくても、トレースできるということを意味しています。
各サービスは、Webアプリケーションとして、それぞれ、「service-A.war」、「service-B.war」、「service-C.war」というwarファイルにパッケージしておきます。詳細については、pom.xmlを参照してください。

作成したサービスの起動

作成した3つのサービスを動かす前に、JaegerとLauncherの準備をします。その後、Launcherを使ってサービスを起動します。

Jaegerの準備

可視化ツールのJaegerを準備します。ここでは、以下のように、docker imageを使います。

$ docker pull jaegertracing/all-in-one

$ docker run -d --name jaeger \
  -e COLLECTOR_ZIPKIN_HTTP_PORT=9411 \
  -p 5775:5775/udp \
  -p 6831:6831/udp \
  -p 6832:6832/udp \
  -p 5778:5778 \
  -p 16686:16686 \
  -p 14268:14268 \
  -p 9411:9411 \
  jaegertracing/all-in-one

トレース対象のプログラムを動かす際には、以下の環境変数を設定しておきます。

export JAEGER_SERVICE_NAME=DEMO201907
export JAEGER_AGENT_HOST=localhost
export JAEGER_AGENT_PORT=6831

また、Jaeger UIは、以下でアクセスできるようになります。

http://localhost:16686

Launcherの準備

MicroProfile OpenTracingを使用するために、MicroProfileの実装の一つである、「Launcher」を用意します。ここでは、以下より、2.0-alpha-1版をダウンロードして利用します。

Launcherの使い方は、ダウンロードした「launcher-2.0-a01.jar」を任意の場所に置いて、javaコマンドの-jarオプションに指定するだけです(インストール作業は不要です)。詳細なLauncherの使用方法は、ドキュメント「lncusg」を参照してください。

3つのサービスの起動

作成した3つのサービス(warファイル)を以下のように起動させますが、javaコマンドの実行時には、前々節で紹介した以下の3つの環境変数を忘れずに設定してください。

export JAEGER_SERVICE_NAME=DEMO201907
export JAEGER_AGENT_HOST=localhost
export JAEGER_AGENT_PORT=6831

ServiceA

$ java -jar launcher-2.0-a01.jar --http-listener 8080 --https-listener 8081 --deploy service-A.war

ServiceB

$ java -jar launcher-2.0-a01.jar --http-listener 7070 --https-listener 7071 --deploy service-B.war

ServiceC

$ java -jar launcher-2.0-a01.jar --http-listener 6060 --https-listener 6061 --deploy service-C.war

Jaegerによるトレースの可視化

ServiceAに対して、以下のようなリクエストを何度か送信します。

$ curl -X GET "http://localhost:8080/service?option=run"
$ curl -X GET "http://localhost:8080/service?option=sleep"

その後、Jeagerのコンソールにアクセスすると、図2の画面が出ます。

図2

左上のServiceフィールドに出ている「DEMO201907」というのは、環境変数「JAEGER_SERVICE_NAME」に設定した値が表示されています。
次に、左下の「Find Traces」ボタンを押すと、図3の画面が出ます。

図3

図3では、ServiceA.acceptへのリクエストが画面上4つ表示されています。それぞれのトレース情報の右上に、リクエストに要した時間が表示されています。よく見ると、一番上のトレースが552.48msで、残りの3つのトレース時間に比べて、10倍近くかかっていることが分かります。
次に、上から2つ目のトレースをクリックすると、図4の画面になります。

図4

各サービス間の呼出し経路と、経過時間が表示されています。サービスのところをクリックすると、図5のように、HTTPのステータスコード、URLなどの各サービスの詳細が出ます。

図5

次に、トレース一覧の画面(図3)にもどって、一番上のトレースをクリックすると、図6の画面になります。同様に、ServiceC.acceptの詳細情報が確認できます。URLを見ると、「option=sleep」が指定されていることが分かり、意図的に入れた「Thread.sleep(500)」のルートに入り、このメソッドで502.16ms費やすことになったことが分かります。

図6

まとめと予告

今回Part1では、OpenTracing APIは一切使わず、既存のJAX-RSのみのプログラムでもサービスの呼出し経路や、サービスの詳細情報を見ることができました。MSAではサービス間の呼出し関係が複雑になりがちですが、MicroProfile OpenTracingを使うことで、呼出し関係やボトルネックの可視化ができるようになります。
次回Part2では、OpenTracing APIを使って、より詳細なトレース情報の取り方を見ていきます。

本コンテンツに関するお問い合わせ

お電話でのお問い合わせ

Webでのお問い合わせ

当社はセキュリティ保護の観点からSSL技術を使用しております。

ページの先頭へ