Spring Cloudシリーズの第5回です。
概要
今回は「OpenTracing API」と「Jaeger(イェーガー)」を使ってリクエストをトレーシング(追跡)してみましょう。
前回までで、上記の構成を作りましたが、コンポーネントやサービスが多くなったり、冗長化して負荷分散したりすると、1つのリクエスト(トランザクション)がどこのインスタンスを通り、それぞれどれだけの処理時間を要しているのかが、わかりにくくなります。
トレーシング技術を使うことによって、それらを可視化することができます。
環境
以下が動く環境を前提とします。
- docker & docker-compose
- JDK11
前回までの投稿の続きですので、前回のゴール地点のソースを手元に用意し、それをベースとします。
作業ステップ
本記事では以下のステップで進めていきます。
- Jaegarサービスの追加
- トレーシングの設定追加(Dependencyの追加と設定ファイルの記述)
- R2DBCのトレーシングの追加
ステップ1:Jaegarサービスの追加
Jaegerの全部入りdockerイメージが用意されていますので、docker-compose.ymlに追加するだけです。実にお手軽に開発環境を構築できますね。
volumes:
pgdata:
networks:
default:
services:
db:
・・・
adminer:
・・・
gw:
・・・
・・・
jaeger:
image: jaegertracing/all-in-one
container_name: jaegar
environment:
- COLLECTOR_ZIPKIN_HTTP_PORT=9411
ports:
- 5775:5775/udp
- 6831:6831/udp
- 6832:6832/udp
- 5778:5778
- 16686:16686
- 14268:14268
- 9411:9411
networks:
- default
以下で起動・停止ができます。
(起動)
$ docker-compose up -d jaeger
(停止)
$ docker-compose stop jaeger
ステップ2:トレーシングの設定追加(Dependencyの追加と設定ファイルの記述)
今回もあまりやることがありません。2つだけ。
まず1つ目。pom.xmlにdependencyを追加します。
Spring Cloud Gateway には opentracing-spring-jaeger-cloud-starter
を追加します。
<dependencies>
・・・
<dependency>
<groupId>io.opentracing.contrib</groupId>
<artifactId>opentracing-spring-jaeger-cloud-starter</artifactId>
<version>3.2.0</version>
</dependency>
・・・
</dependencies>
Account には opentracing-spring-jaeger-web-starter
を追加します。
<dependencies>
・・・
<dependency>
<groupId>io.opentracing.contrib</groupId>
<artifactId>opentracing-spring-jaeger-web-starter</artifactId>
<version>3.2.0</version>
</dependency>
・・・
</dependencies>
やること2つ目。
設定ファイル「application.yml」にJaegerへの接続設定を追加します。
ここではHTTPでJaegerにトレーシング情報を送信する設定を追加しています。
JaegerへのリクエストURLは環境変数で上書きできるようにしておきましょう。
server:
・・・
spring:
・・・
management:
・・・
eureka:
・・・
opentracing:
jaeger:
log-spans: false
http-sender:
url: ${JAEGER_HTTP_URL:http://localhost:14268/api/traces}
account/src/main/resources/application.yml にも上記と同様の「opentracing」の定義を追加します。
docker-compose.ymlで環境変数「JAEGER_HTTP_URL」を定義します。
docker-composeではサービス名をホスト名にしておくと、よしなに名前解決してくれるので、「jaeger」をホスト名に指定します。
・・・
gw:
・・・
environment:
・・・
- JAEGER_HTTP_URL=http://jaeger:14268/api/traces
account:
・・・
environment:
・・・
- JAEGER_HTTP_URL=http://jaeger:14268/api/traces
account2:
・・・
environment:
・・・
- JAEGER_HTTP_URL=http://jaeger:14268/api/traces
account3:
・・・
environment:
・・・
- JAEGER_HTTP_URL=http://jaeger:14268/api/traces
ここまでで、とくに追跡コードなしに自動的にgateway-->accountの範囲でトレーシングができるようになります。
親切な世の中です。
$ cd account
$ ./mvnw clean package -DskipTests=true
$ docker-compose up -d --build account
http://localhost:5000/api/account にアクセスした後に、
http://localhost:16686/ にアクセスし、リクエストを選択すると以下のように呼び出し階層が見えると思います。
ステップ3:R2DBCのトレーシングの追加
R2DBCを使ったDBアクセスもトレーシングできるようにしてみましょう。
これでDBアクセスがどのタイミングでどのくらい処理時間がかかっているかが把握できるようになります。
R2DBC+Springbootで自動的にトレーシング情報を送信するコンポーネントはまだ存在しないようなので、現在公開されている情報を頼りに少し手作りします。
以下を行います。
- 【1】 r2dbc-proxyを使ったリスナーを用意し、R2DBCのConnectionとQuery実行のトレーシング情報を送信する。
- 以下の公式サンプルを参考にし、Zipkin(?)のAPIをOpentracingのAPIに置き換える。
- 【2】 r2dbc-proxyと【1】のリスナーを有効化する。
- ConnectionFactoryのセットアップとプロキシ/リスナーの追加を設定する。(ベタで)
- 以下のプルリクエストと既存のConnectionFactory(R2DBC)のセットアップコードを参考にする。
- https://github.com/spring-projects/spring-boot/pull/21689
- なお、このautoconfigurationへのPRは、まだmasterに取り込まれていない(2020/10/05現在)
spring-msa
├── account
│ └── src/main/java/org/example/account
│ ├── AccountApplication.java
│ ├── AccountConfiguration.java <-- ★新規追加【2】
│ ├── controller
│ ├── entity
│ ├── repository
│ └── TracingExecutionListener.java <-- ★新規追加【1】
├── db
├── docker-compose.yml
├── gateyway
└── sd
【1】TracingExecutionListener
R2DBCの公式サイトにあるExample をベースにし、OpenTracing APIを使うように加工する。
ソースはこちら
account/src/main/java/com/example/account/TracingExecutionListener.java
【2】AccountConfiguration
package com.example.account;
import io.r2dbc.pool.ConnectionPool;
import io.r2dbc.pool.ConnectionPoolConfiguration;
import io.r2dbc.proxy.ProxyConnectionFactory;
import io.r2dbc.proxy.listener.LifeCycleListener;
import io.r2dbc.spi.ConnectionFactory;
import org.springframework.boot.autoconfigure.r2dbc.ConnectionFactoryBuilder;
import org.springframework.boot.autoconfigure.r2dbc.EmbeddedDatabaseConnection;
import org.springframework.boot.autoconfigure.r2dbc.R2dbcProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ResourceLoader;
import org.springframework.util.StringUtils;
@Configuration(proxyBeanMethods = false)
public class AccountConfiguration {
@Bean
public LifeCycleListener tracingExecutionListener() {
return new TracingExecutionListener();
}
// https://github.com/spring-projects/spring-boot/pull/21689
// 上記のPull Request が取り込まれたら、以下のコードは不要となる
@Bean
public ConnectionFactory connectionFactory(R2dbcProperties properties,
ResourceLoader resourceLoader,
LifeCycleListener tracingExecutionListener) {
ConnectionFactory original = ConnectionFactoryBuilder
.of(properties, () ->
EmbeddedDatabaseConnection.get(resourceLoader.getClassLoader())).build();
ConnectionFactory proxyConnectionFactory =
ProxyConnectionFactory.builder(original).listener(tracingExecutionListener).build();
R2dbcProperties.Pool pool = properties.getPool();
ConnectionPoolConfiguration.Builder builder
= ConnectionPoolConfiguration.builder(proxyConnectionFactory)
.maxSize(pool.getMaxSize()).initialSize(pool.getInitialSize()).maxIdleTime(pool.getMaxIdleTime());
if (StringUtils.hasText(pool.getValidationQuery())) {
builder.validationQuery(pool.getValidationQuery());
}
return new ConnectionPool(builder.build());
}
}
さて、これでビルドして動かしてみましょう。
$ cd account
$ ./mvnw clean package -DskipTests=true
$ docker-compose up -d --build account
http://localhost:5000/api/account にアクセスした後に、
http://localhost:16686/ にアクセスし、リクエストを選択すると以下のように呼び出し階層が見えると思います。
R2DBCのクエリ内容も見えてますね。
さいごに
これまでと同様に、ここまでのソースを以下に置いています。
さて、ここからはPrometheusを立てて、JVMやコンテナのモニタリングをしてみたいところですが、
この記事シリーズはいったんここまでとさせていただきます。
また別の記事を投稿予定ですので、そちらをお待ちください。
ではでは。