spring-bootのアプリケーションのモニタリングを検討しはじめた。
モニタリングについてはほぼ初心者状態…
今回モニタリングしたい項目はメモリ、スレッドなどシステム的な項目ではなく、
アプリケーションに紐づく値(例えばAPIごとのスループットや呼び出し回数)であったので、
クライアントライブラリが充実しており、アプリケーションに紐づくモニタリング項目が出力できそうなPrometheusとSpring Bootの組み合わせを調べてみた。
(+可視化のためのツールとしてGrafanaも組み合わせた。)
環境
自身の環境は以下。
- OS: macOS Sierra 10.12.6
- Java: 1.8.0_102
- Spring Boot: 1.5.10.RELEASE
- Docker For Mac: 17.12.0-ce-mac49
- Prometheus: v2.1.0
- Grafana: 5.0.0-beta4
Prometheusについて
Prometheusは公式ページのトップに
From metrics to insight.
Power your metrics and alerting with a leading
open-source monitoring solution.
と書いてあるように、オープンソースのモニタリングツールだ。
Prometheusの一番の特徴はメトリクス収集方式としてPull型を採用していることだ(と思う)。
あとはKubernetesと相性がよいことだと思う。
他の特徴や詳しい説明については、この記事が参考になったので確認してほしい。
https://developers.cyberagent.co.jp/blog/archives/3814/
Prometheusには多くのクライアントライブラリがあり、主な言語はサポートされている。(正式かどうかはともかくとしてだが、、、)
https://prometheus.io/docs/instrumenting/clientlibs/
またJavaクライアントライブラリには、Spring、Spring Bootもサポートされている。
クライアントライブラリを使用することで、WEBアプリケーションのAPIごとのスループットなどが可視化できるので、今回はJavaクライアントライブラリ、Spring Bootを使用して、モニタリングの設定やその設定で何がモニタリングできるかを確認してみた。
今回、対象とするのはWEBアプリケーションの場合であり、バッチなどの短期間で終わる処理は対象外とする。
バッチなどはPrometheusのpushgatewayという機構を使用する必要があるが、今回は対象外。
Spring BootアプリケーションへのPrometheusクライアントの設定方法
PrometheusはPull側のメトリクス収集方式を採用しているため、
Spring BootアプリケーションでのPrometheusクライアントの役割は、アプリケーションのメトリックスを取得してエンドポイントとして公開すること。
それを実現するのはsimpleclient_spring_bootライブラリとなる。
ここでsimpleclient_spring_bootライブラリの内部と使い方について説明しておく。
simpleclient_spring_boot
設定方法
事前にSpring Webアプリケーションを作っておく必要がある。ここでは、Mavenの依存関係にspring-boot-starter-web
を追加しておく。
またSpring Boot用Prometheusクライアントライブラリを依存関係に追加する。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Prometheus library for spring boot-->
<dependency>
<groupId>io.prometheus</groupId>
<artifactId>simpleclient_spring_boot</artifactId>
<version>0.2.0</version>
</dependency>
ライブラリが提供しているアノテーションが2つある。
@EnablePrometheusEndpoint
@EnableSpringBootMetricsCollector
これらのアノテーションをアプリケーション側に設定する。
@SpringBootApplication
@EnablePrometheusEndpoint
@EnableSpringBootMetricsCollector
public class SpringBootPrometheusGrafanaSampleApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBootPrometheusGrafanaSampleApplication.class, args);
}
}
これだけで、Springが提供しているメトリクスが/prometheus
のエンドポイントに表示されるようになる。
詳しいライブラリの説明は以下。
@EnablePrometheusEndpoint
SpringのWEBアプリケーション上で、/prometheus
のエンドポイントを登録するためのアノテーション。
単体では/prometheus
のエンドポイントに空のメトリクス(空レスポンス)しか表示されない。
メトリックスは別途、追加する必要があり、後述するLogやGarbage Collectionなどの設定もここのエンドポイントに追加される。
(したがって、@EnablePrometheusEndpoint
は必須だが、後述する@EnableSpringBootMetricsCollector
はそのメトリックスが必要でなければ、必ずしも付与する必要はない。)
クラスのシーケンスは以下のようにシンプルである。
EnablePrometheusEndpoint | → | PrometheusEndpointConfiguration | → | PrometheusEndpoint,PrometheusMvcEndpoint |
---|
処理概要もまとめておいた。
クラス | 処理概要 |
---|---|
PrometheusEndpointConfiguration | Bean登録を行うConfigurationクラス。 |
PrometheusEndpoint |
/prometheus エンドポイントの追加。 |
PrometheusMvcEndpoint |
/prometheus エンドポイントの呼び出し処理。レスポンスのフィルターを行う。 |
レスポンスのフィルタは以下のような形で行えることを確認している。
フィルタをしないと、全項目が出力される。
http://localhost:8080/prometheus?name[]=jvm_threads_daemon&name[]=jvm_memory_bytes_used
EnableSpringBootMetricsCollector
Spring Actuatorで持っているメトリックスの追加。
(正確にはBean登録済みの)PublicMetricsインターフェースを実装しているものが追加される。
ex. TomcatPublicMetrics, SystemPublicMetrics
クラスのシーケンス、処理概要は以下の通り。
EnableSpringBootMetricsCollector | → | PrometheusMetricsConfiguration | → | SpringBootMetricsCollector |
---|
クラス | 処理概要 |
---|---|
PrometheusMetricsConfiguration | Bean登録を行うConfigurationクラス。 |
SpringBootMetricsCollector | (Bean登録済みの)PublicMetricsインターフェースの実装クラスに対して、メトリックスの収集を行う。 |
Logメトリックスの追加
メトリックスにLogレベルごとの集計を追加することができる。
# HELP logback_appender_total Logback log statements at various log levels
# TYPE logback_appender_total counter
logback_appender_total{level="debug",} 0.0
logback_appender_total{level="warn",} 0.0
logback_appender_total{level="trace",} 0.0
logback_appender_total{level="error",} 0.0
logback_appender_total{level="info",} 49.0
設定は以下。
<dependency>
<groupId>io.prometheus</groupId>
<artifactId>simpleclient_logback</artifactId>
<version>0.2.0</version>
</dependency>
resources/logback-spring.xml
をInstrumentedAppender
とrootレベルでMETRICS
を追加する。
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<include resource="org/springframework/boot/logging/logback/base.xml"/>
<appender name="METRICS" class="io.prometheus.client.logback.InstrumentedAppender" />
<root level="INFO">
<appender-ref ref="METRICS" />
</root>
</configuration>
Garbage collection, Memory pools, JMX, Classloading, Thread counts の追加。
若干、Spring-Actuatorで持っているメトリックス値とかぶるが、設定は以下。
<dependency>
<groupId>io.prometheus</groupId>
<artifactId>simpleclient_hotspot</artifactId>
<version>0.2.0</version>
</dependency>
DefaultExports.initialize()
を初期処理で呼び出す。
@SpringBootApplication
@EnablePrometheusEndpoint
@EnableSpringBootMetricsCollector
public class SpringBootPrometheusGrafanaSampleApplication {
public static void main(String[] args) {
DefaultExports.initialize();
SpringApplication.run(SpringBootPrometheusGrafanaSampleApplication.class, args);
}
}
メソッド単位の所要時間メトリックスの追加
Spring AOPを使って、メソッド単位でログを取得できる。
@EnablePrometheusTiming
を追加する。
@SpringBootApplication
@EnablePrometheusEndpoint
@EnableSpringBootMetricsCollector
@EnablePrometheusTiming
public class SpringBootPrometheusGrafanaSampleApplication {
}
メソッドに@PrometheusTimeMethod
を付与する。
@RestController
public class HelloController {
@GetMapping("/hello")
@PrometheusTimeMethod(name = "hello_controller_say_hello_duration_seconds", help = "Some helpful info here")
public String sayHello() {
return "hello";
}
}
AOPを使っているので、同じクラス内でのメソッド呼び出しやstaticメソッドなどでは@PrometheusTimeMethod
が効かないことに注意が必要。
個別実装
上記まではほぼ設定だけでメトリックスが取得できたが、もしそれでは取得することができないアプリケーションの値があればここで紹介する個別実装を行う必要がある。
Prometheusが提供しているメトリックスタイプはCounter,Gauge,Summary,Histogramの4種類。
それぞれのタイプの詳細は公式ページを参考にしてほしい。
今回はCounterの実装例を作った。
@RestController
public class CounterController {
private static int num = 0;
private static final Counter requests = Counter.build().name("count_requests_total").help("Total count requests.").register();
@GetMapping("count")
public int count() {
requests.inc();
return ++num;
}
}
リクエストがあれば、回数を増やして返すようなコントローラであり、その回数をメトリックスとして保存している。
Prometheus,Grafanaの設定
Grafanaはオープンソースの可視化ツールで、
Prometheusの公式サイトでもGrafanaを使用することがおすすめされている。
https://prometheus.io/docs/visualization/grafana/
ここではSpring Bootアプリ、Prometheus、GrafanaをDockerで構築する。
詳しい説明はしないが、Dockerfile
,docker-compose.yml
は以下のようにした。
FROM maven:3.5.2-jdk-8-alpine
ADD . /code
WORKDIR /code
RUN mvn clean package -DskipTests=true
CMD mvn spring-boot:run
version: '3'
services:
web:
build: .
ports:
- 8080:8080
volumes:
- .:/code
prometheus:
image: prom/prometheus
container_name: prometheus
volumes:
- ./env/prometheus.yml:/etc/prometheus/prometheus.yml
ports:
- 9090:9090
grafana:
image: grafana/grafana
container_name: grafana
ports:
- 3000:3000
env_file:
- ./env/grafana.env
grafana.env
はGrafanaの設定をいじるためのファイルとして用意したが、とくに設定が必要なかったので、空ファイル。
prometheus.yml
ほぼGetting startedに載っている内容。。。
すいません、Prometheusの設定に詳しくなくて、、、
global:
scrape_interval: 15s # By default, scrape targets every 15 seconds.
# Attach these labels to any time series or alerts when communicating with
# external systems (federation, remote storage, Alertmanager).
external_labels:
monitor: 'codelab-monitor'
# A scrape configuration containing exactly one endpoint to scrape:
# Here it's Prometheus itself.
scrape_configs:
# The job name is added as a label `job=<job_name>` to any timeseries scraped from this config.
- job_name: 'prometheus'
# Override the global default and scrape targets from this job every 5 seconds.
scrape_interval: 5s
static_configs:
- targets: ['localhost:9090']
# sample
scrape_configs:
- job_name: 'sample-random'
# Override the global default and scrape targets from this job every 5 seconds.
scrape_interval: 5s
metrics_path: /prometheus
static_configs:
- targets: ['web:8080']
簡単に補足しておくと、 - job_name: 'prometheus'
の部分がPrometheus自身のモニタリングで、 - job_name: 'sample'
の部分が今回のWEBアプリケーションへの設定部分になっている。
注意点は2つで、
- Prometheusがモニタリングするデフォルトのエンドポイントは
/metrics
のため、変更のためmetrics_path: /prometheus
を追加 - WEBアプリケーションがDocker内のため、targetsのURLにDockerのnameを使っている
PrometheusのGUI確認
Prometheusのデフォルトのポートは9090なので、http://localhost:9090/
にアクセスすると、Prometheusが表示される。
http://localhost:9090/graph
で基本的な表示ができる。
例えば、ログレベル別にログ数を表示してみる。
ドロップダウンリストから[logback_appender_total]を選択し、[Execute]を押下すればよい。
GrafanaのGUI確認
Grafanaのデフォルトポートは3000なので、http://localhost:3000/
にアクセスする。
とくに設定していなければ、admin/admin
でログインできる。
まずはデータソースにPrometheusを設定する必要があるので、[Configuration] -> [Data Sources]を選択し、データソースの追加画面へいく。
データソースの設定は以下の通り。
項目 | 値 |
---|---|
Type | Prometheus |
URL | http://localhost:9090 |
Access | direct |
Scrap interval | 5s |
次にダッシュボードを作成する。
[Create] -> [Dashboard] -> [Graph](ここは表示したいものにあわせて変更する)と選ぶ。
表示されたグラフを選択し、[Edit]を選択する。
項目 | 値 |
---|---|
Data Source | prometheus |
A | logback_appender_total |
を追加すればグラフが表示される。
所感
Prometheusを使い、クライアントソースに軽く手をいれることで、アプリケーションのモニタリングにおいてほしい情報は取得できるようになった。
修正量も大したことはないので、使い勝手は良さそう。
とくにSpring Bootアプリであれば、すぐに使えるようになるのは魅力的。
ただ、他のモニタリングツールでもアプリケーションの情報が多く取れるものがある。
たとえば、New Relic APMであれば、トランザクションごとのスループットは表示することができる。
そのように他のモニタリングツールとの比較は今後、必要になってくる。
GitHub
今回のサンプルソースはいつものようにGitHubに公開している。
https://github.com/aha-oretama/spring-boot-prometheus-grafana-sample