Posted at

Spring Boot ✕ Prometheus ✕ Grafana でアプリケーションのモニタリング

More than 1 year has passed since last update.

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クライアントライブラリを依存関係に追加する。


pom.xml

    <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

これらのアノテーションをアプリケーション側に設定する。


SpringBootPrometheusGrafanaSampleApplication.java

@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

設定は以下。


pom.xml

    <dependency>

<groupId>io.prometheus</groupId>
<artifactId>simpleclient_logback</artifactId>
<version>0.2.0</version>
</dependency>

resources/logback-spring.xmlInstrumentedAppenderとrootレベルでMETRICSを追加する。


logback-spring.xml

<?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で持っているメトリックス値とかぶるが、設定は以下。


pom.xml

    <dependency>

<groupId>io.prometheus</groupId>
<artifactId>simpleclient_hotspot</artifactId>
<version>0.2.0</version>
</dependency>

DefaultExports.initialize()を初期処理で呼び出す。


SpringBootPrometheusGrafanaSampleApplication.java

@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を付与する。


HelloController.java

@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の実装例を作った。


CounterController.java

@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は以下のようにした。


Dockerfile

FROM maven:3.5.2-jdk-8-alpine

ADD . /code
WORKDIR /code
RUN mvn clean package -DskipTests=true
CMD mvn spring-boot:run


docker-compose.yml

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]を押下すればよい。

スクリーンショット 2018-02-26 12.12.43.png

グラフ表示にする場合は、[Graph]タブにする。

スクリーンショット 2018-02-26 12.13.12.png


GrafanaのGUI確認

Grafanaのデフォルトポートは3000なので、http://localhost:3000/にアクセスする。

とくに設定していなければ、admin/adminでログインできる。

まずはデータソースにPrometheusを設定する必要があるので、[Configuration] -> [Data Sources]を選択し、データソースの追加画面へいく。

スクリーンショット 2018-02-26 12.17.15.png

データソースの設定は以下の通り。

項目

Type
Prometheus

URL
http://localhost:9090

Access
direct

Scrap interval
5s

スクリーンショット 2018-02-26 12.21.27.png

次にダッシュボードを作成する。

[Create] -> [Dashboard] -> [Graph](ここは表示したいものにあわせて変更する)と選ぶ。

スクリーンショット 2018-02-26 12.30.11.png

表示されたグラフを選択し、[Edit]を選択する。

スクリーンショット 2018-02-26 12.24.37.png

項目

Data Source
prometheus

A
logback_appender_total

を追加すればグラフが表示される。

スクリーンショット 2018-02-26 12.26.02.png


所感

Prometheusを使い、クライアントソースに軽く手をいれることで、アプリケーションのモニタリングにおいてほしい情報は取得できるようになった。

修正量も大したことはないので、使い勝手は良さそう。

とくにSpring Bootアプリであれば、すぐに使えるようになるのは魅力的。

ただ、他のモニタリングツールでもアプリケーションの情報が多く取れるものがある。

たとえば、New Relic APMであれば、トランザクションごとのスループットは表示することができる。

そのように他のモニタリングツールとの比較は今後、必要になってくる。


GitHub

今回のサンプルソースはいつものようにGitHubに公開している。

https://github.com/aha-oretama/spring-boot-prometheus-grafana-sample


参考