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技術を使用しております。