NHN Cloud NHN Cloud Meetup!

PrometheusとGrafanaでJavaアプリケーションをモニタリングする

要約

Spring Boot Actuator と Micrometer Registryの導入

  • spring boot actuatorとmicrometer-registry-prometheusを追加

pom.xml

<dependency>  
     <groupId>org.springframework.boot</groupId>  
     <artifactId>spring-boot-starter-actuator</artifactId>  
</dependency>  
<dependency>  
     <groupId>io.micrometer</groupId>  
     <artifactId>micrometer-registry-prometheus</artifactId>  
</dependency>

application.yml

  • spring boot actuatorのprometheusエンドポイントを有効化
  • サービスを区別するためのcommon tagを追加(サービスは1つ以上のインスタンスで行われる)
    • 一般的に、applicationという名前でspring.application.name属性がよく使われる
    • 各インスタンスを区別するためのinstanceタグは、Prometheusによって追加されるため設定不要
spring:
  application:
    name: my_spring_boot_app
management:
  endpoints:
    web:
      exposure:
        include: "prometheus"
  metrics:
    tags:
      application: ${spring.application.name}    #  サービス単位の識別子。Prometheus labelに追加。

Spring Bootでない場合

Prometheusの導入手順

インストール

wget https://github.com/prometheus/prometheus/releases/download/v2.17.1/prometheus-2.17.1.linux-amd64.tar.gz
tar -xzf prometheus-2.17.1.linux-amd64.tar.gz
cd prometheus-2.17.1.linux-amd64

prometheus.ymlファイルの編集

  • targetsにモニタリングするSpring BootアプリケーションIP:Portを追加
  • Service Discoveryを使用する場合、関連Service Discoveryの設定追加
global:
  scrape_interval: "15s"
  evaluation_interval: "15s"
scrape_configs:
- job_name: "springboot"
  metrics_path: "/actuator/prometheus"
  static_configs:
  - targets:
    - "<my_spring_boot_app_ip>:<port>"
- job_name: "prometheus"
  static_configs:
  - targets:
    - "localhost:9090"

起動

  • 基本ポート:9090
  • 設定ファイル:prometheus.yml
./prometheus

Grafanaの導入手順

インストールと起動

  • 基本ポート:3000
  • デフォルトのアカウントID / PW:admin / admin
wget https://dl.grafana.com/oss/release/grafana-6.7.2.linux-amd64.tar.gz
tar -zxvf grafana-6.7.2.linux-amd64.tar.gz
cd grafana-6.7.2
./bin/grafana-server

Prometheus datasourceの追加

  • 実行したPrometheusをGrafanaのデータソースとして追加

初回ページの場合、以下のボタンを追加Prometheusを選択
インストールしたPrometheusの情報を入力

Dashboardを追加

Marketplace

直接グラフを追加

  • 各ダッシュボードは変数を追加して使用できる
    • my dashboard -> Setting -> Variablesで設定
    • PromQL内で$variable_name形式で使用可能
  • PromQL(Prometheus Query Language)から直接指標を加工・照会できる
    • PromQL例
      • 特定サービス内のインスタンスAPI別の初回リクエスト数を照会
        • irate(http_server_requests_seconds_count{application=”$application”, instance=”$instance”}[3m])
      • 特定サービスの1秒あたりのエラーリクエスト数の合計を照会
        • sum by (application) (irate(http_server_requests_seconds_count{application=”$application”, outcome=~”CLIENT_ERROR|SERVER_ERROR”}[3m]))
    • 照会結果のデータラベルは凡例名で使用できる。{{ label_name }}形式でラベル指定


モニタリング環境の構築

Prometheusとは

Prometheusは、2012年に登場したオープンソースのモニタリングプラットフォームです。
他のモニタリングプラットフォームとは異なり、Pull方式を使用して各モニタリング対象のExporterまたはPrometheus clientから指標をかき集める(scrape)方式でデータを収集します。Prometheusは、他のモニタリングプラットフォームに比べて使用と設定が簡単な上、柔軟で非常に良好なパフォーマンスを見せ、多くの方に愛用されました。

Prometheusは、Googleの内部モニタリングシステムであったBorgmonに影響を受けて開始されたGo言語のオープンソースプロジェクトです。2012年から着実にコミュニティが成長しており、現在では毎年PromConというカンファレンスを行うほどに大きく成長しています。また、CNCF(Cloud Native Computing Foundation)のGraduatedプロジェクトとなり、現在のコンテナモニタリングの事実上の標準として使用されています。
Prometheusの詳しい説明は省略し、ここではPrometheusを使って、自分たちのアプリケーションをモニタリングできるように、Prometheusの実践に重点を置くことにしましょう。

インストール

Prometheusは、単一コードで複数のオペレーティングシステムのバイナリをビルドできるGo言語で開発されました。Go言語の生態系がそうであるように、PrometheusもさまざまなOSのバイナリファイルとコンテナイメージを提供しています。
ここではコンテナ環境ではなく、オンプレミス環境を基準とします。

必要なOSのバイナリファイルのみをダウンロードして実行するだけで簡単に設定できます。
下記からダウンロードしましょう。
https://prometheus.io/download/

Linuxの主要コマンドを活用すれば、以下のように使うこともできます。
CentOS7を使用しました。

wget https://github.com/prometheus/prometheus/releases/download/v2.17.1/prometheus-2.17.1.linux-amd64.tar.gz
tar -xzf prometheus-2.17.1.linux-amd64.tar.gz


実行

Prometheusの実行は非常に簡単です。
Go言語は最終的に各OSにも動作できるバイナリを生成します。
一般的なバイナリファイルのように実行します。

cd prometheus-2.17.1.linux-amd64
./prometheus

これでモニタリング指標を収集するための基本的な準備は完了しました。基本的に、設定ファイルは同じディレクトリ内のprometheus.ymlを使用し、ポートは9090を使用します。ブラウザを使ってlocalhost:9090にアクセスすると、以下のようなPrometheusウェブ画面が表示されます。

このウェブ画面でPrometheusの照会クエリをテストしたり、追加設定から指標記録や通知状態などを確認できます。
しかし、私たちがこのPrometheusウェブ画面を使用ことはほとんどありません。
なぜなら、Grafanaという優れたオープンソースのダッシュボードがあるからです。

Grafana

Grafanaはさまざまなデータソースからデータを取得し、ダッシュボードを構成できるようにするオープンソースのプラットフォームです。
バックエンドウェブサーバーはGo言語で開発され、フロントエンドは初期にはAngularを使用していましたが、現在はTypeScript + Reactを主に使用しています。一般的に、Grafanaで言うデータソースとは、実際の時系列指標の性質を持つデータを保存して照会できるプラットフォームを指します。現在、公式的にサポートするデータソースは、Graphite、Prometheus、InfluxDB、Elasticsearch、AWS CloudWatch、OpenTSDB …などで、一般的なストレージはほとんどサポートされています。PrometheusデータソースはGrafanaの公式対応データソースのため、Grafanaに基本的にビルトイン(built-in)されており、個別にインストールする必要はありません。

インストール

下記のページで、Grafanaバイナリと各OSおよびインストール方法についてガイドが提供されています。
やはり単一バイナリ形式で提供されるため、機能テストの用途であれば、ただバイナリファイルをダウンロードして簡単に実行してみることができます。
https://grafana.com/grafana/download

wget https://dl.grafana.com/oss/release/grafana-6.7.2.linux-amd64.tar.gz
tar -zxvf grafana-6.7.2.linux-amd64.tar.gz

起動

standaloneで実行すれば、特別な設定は必要ありません。
高可用性やセキュリティ(認証、権限など)が必要な場合や、多くのトラフィックを処理する必要がある場合は、別途設定が必要です。

cd grafana-6.7.2
./bin/grafana-server

Grafana ウェブサーバーの基本ポートは3000です。
ブラウザからhttp://localhost:3000にアクセスすると、Grafanaログインページが表示されます。
デフォルトの管理者アカウントは、ID: adminPW: adminです。
ログインに成功したら、Grafanaで次のステップのためのガイドを表示します。

Prometheus Data Sourceを追加

上段のAdd data sourceボタンをクリックするか、左側のサイドメニューのConfiguration > Data sources > Add data sourceボタンをクリックして、データソースを追加することができます。

クリックすると、下図のような画面が表示されます。
Time series databasesタブでPrometheusを選択します。

希望の名前でNameフィールドを作成し、HTTPタブのURLフィールドには先ほどインストールして実行したPrometheusのURLを入力します。
GrafanaとPrometheusを同じホストにインストールした場合は、http://lcoalhost:9090を入力すればよいでしょう。
値を入力して、下のSave & Testボタンをクリックし、データソースを保存します。
入力した設定値に問題がなければ、Successという緑色のウィンドウメッセージが表示されます。

このように簡単にPrometheusとGrafanaを連動することができます。

Java メトリクス

Spring Boot Actuatorとの利用

Spring Bootバージョン2以上を基準とします。
Spring Boot Actuatorは、Spring Bootアプリケーションを管理するためのツールをJMXまたはHTTPエンドポイント形式で提供しています。info、logger、env、healthなど多くの機能を提供していますが、ここではprometheusエンドポイントを使用します。その名のとおり、Prometheusサーバーが指標を収集できるように、アプリケーションの指標をPrometheusフォーマットでexposeします。これを利用すると容易にSpring Bootアプリの指標をPrometheusで収集できるように準備できます。このエンドポイントは通常のHTTP APIであるため、直接リクエストしてデータを確認することもできます。(NSightのSpring Bootモニタリングもprometheusエンドポイントを使って指標を収集しています)

必要な依存性

Spring Boot Actuatorはもちろん必要であり、さらに Micrometer Registryが必要です。
Micrometerは、汎用指標測定ライブラリで、Spring Bootバージョン2からSpring Boot Actuatorの根幹を担う重要なライブラリです。
そのため、Micrometer CoreはすでにSpring Boot Actuatorに含まれています。Micrometerのもう1つの目的は、指標収集の抽象化です。
Micrometerは、自身を次のように紹介しています。

Think SLF4J, but for metrics.

SLF4Jにログを残すように、同じインターフェースで指標を測定し、異なるモニタリングプラットフォームで収集できるようにサポートします。このときMicrometerで測定された指標を、実際のバックエンドに保存する機能を実装したものが、Micrometer Registryです。Spring Boot Actuatorは、依存性にどのようなMicrometer Registryが含まれているかを確認し、検出されたレジストリに合わせてAutoConfigurationを実行します。現在、Prometheus、Elastic、InfluxDB、Dynatraceなど多くのレジストリを提供しており、完全なリストは以下のページから確認できます。
https://micrometer.io/docs

私たちはただ、Spring Boot ActuatorとMicrometer Registry Prometheusを依存性に追加し、prometheusエンドポイントをexposeするだけです。また、必須ではありませんが、各アプリケーションサービスを区別するためのタグ(Micrometerのtag)を追加することをお勧めします。ここで言うアプリケーションサービスは、全体の構造から見た各サービスコンポーネントを意味します。つまり、1つのサービスは1つ以上のインスタンスで構成されていると考えるとよいでしょう。
一般的にタグ名はapplicationで、タグの値はSpring Bootのspring.application.name属性が多く使用されています。management.metrics.tags.<key>: <value>に入力されたタグはすべての指標に追加され、Prometheusフォーマットで変換するとすべての指標のラベルに追加されます。各アプリケーションのインスタンスを区別するための識別子は、私たちが追加しなくても問題ありません。Prometheusが収集しながら自動的にIP:Portで構成されたinstanceラベルを追加します。

整理すると、以下のコードが必要です。
この設定が正常に適用されると、http://{host}:{port}/actuator/prometheusURLからPrometheusで収集する指標を確認できます。

pom.xml

<dependency>
     <groupId>org.springframework.boot</groupId>
     <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
     <groupId>io.micrometer</groupId>
     <artifactId>micrometer-registry-prometheus</artifactId>
</dependency>


application.yml

spring:
  application:
    name: my_spring_boot_app
management:
  endpoints:
    web:
      exposure:
        include: "prometheus"
  metrics:
    tags:
      application: ${spring.application.name}    # サービス単位の識別子。Prometheus labelに追加。

正常動作を確認

curl "http://localhost:8080/actuator/prometheus"

 

JMX Exporterとの利用

Spring Bootの場合、独自のPrometheusをサポートし、便利に使用することができました。同様に、多くのプラットフォーム、フレームワーク、ライブラリがPrometheusをサポートしていますが、そうでないケースもあります。その場合は、JMX Exporterを使用することができます。
https://github.com/prometheus/jmx_exporter

JMX Exporterは、JavaアプリケーションのJMXで提供される指標をPrometheusフォーマットに変更し、Prometheusが収集できるようにサポートします。どのJMX Beanを指標として使用するか、どのように値を抽出し、どの名前で使用するか、などのJMXをPrometheusフォーマットに加工するには、正規表現を使用します。そのため正規表現に慣れていない方は、設定が難しいかもしれません。もしくは、あらかじめ提供されている設定例を活用する方法もあります。有名なJavaプロジェクトは、設定を事前にプリセットにして提供しています。
https://github.com/prometheus/jmx_exporter/tree/master/example_configs

JMX Exporterは、一般的にJVM agent方式で使用されます。
以下のようなJVMオプションを実行するアプリケーションに追加する必要があります。このように設定すると、config.ymlファイルを参照して、モニタリングするJMXをPrometheusフォーマットに変換し、HTTP/metrics エンドポイントをポート8080に登録します。

java -javaagent:./jmx_prometheus_javaagent-0.12.0.jar=8080:config.yaml -jar my_app.jar


JMXを使用すると、実質的にほぼすべてのJavaアプリケーションをモニタリングできます。

Grafana + Prometheus + Spring Boot連動

PrometheusにSpring Bootエンドポイントを登録する

それでは、指標を収集するために、再びPrometheusを見てみましょう。
前述したように、PrometheusはPull方式の収集方法を使用しており、直接各モニタリング対象にHTTPリクエストを行い指標データを取得します。そのため、Promeheusにモニタリング対象のアドレスを登録する必要があります。もし、Service Discoveryと連動している場合は、自動的に新しい対象を見つけてモニタリングを開始しますが、ここで用意した最小設定にはそのような設定がありませんので、直接入力する必要があります。
prometheus.ymlを開き、static_configstargetsに先ほど作成したアプリケーションのアドレスを入力します。そして、どのURLパスから指標を収集するかmetrics_pathのプロパティに入力します。

global:
  scrape_interval: "15s"
  evaluation_interval: "15s"
scrape_configs:
- job_name: "springboot"
  metrics_path: "/actuator/prometheus"          # prometheusエンドポイントパス
  static_configs:
  - targets:
    - "<my_spring_boot_app_ip>:<port>"         # モニタリング対象をリストに指定
- job_name: "prometheus"
  static_configs:
  - targets:
    - "localhost:9090"


PromQLで指標を照会する

Prometheusは指標を照会するためのPromQL(Prometheus Query Language)と呼ばれるクエリ言語を提供しています。SQLのように収集された指標をフィルタリング・集計することができ、加工して通知を発生させることができます。そして、重要な点は、Prometheusのモニタリング対象がexposeする指標データは、以前の収集時期からの変化量ではなく、アプリ実行時から累積された値である点です。(gaugeタイプを除く)指標の変化量は、サーバーで照会する際にPromQLによって計算することができ、rateirateのような関数がその役割を果たします。
詳しい内容はこの記事の範囲外であるため、PromQLの公式文書を参照したり、PromQLが作成されているダッシュボードをGrafana Marketplaceで確認することをお勧めします。

PromQLの例

  • 特定サービス内のインスタンスのJVM Heap Eden領域を照会
jvm_memory_used_bytes{instance="123.0.0.1:8080", application="my_api", id="G1 Eden Space"}
  • 特定サービス内のインスタンスのAPI(Method + URL Path + Status Code)別リクエスト数を照会
    • count指標はアプリ実行時からの累積値のため、irate関数を使用しています。
irate(http_server_requests_seconds_count{application="my_api", instance="123.0.0.1:8080"}[3m])
  • 特定サービスの1秒あたりのエラーリクエスト数の合計を照会
sum by (application) (irate(http_server_requests_seconds_count{application="my_api", outcome=~"CLIENT_ERROR|SERVER_ERROR"}[3m]))`

 

Grafanaにダッシュボードやグラフを作成する

Marketplaceに必要なダッシュボードがなかったり、インポートされたダッシュボードに必要なグラフがない場合は、直接グラフを作成することができます。このときもやはりPromQLを使用します。

参考までに、各ダッシュボードには変数を追加して使用することができます。定数で指定することもできますが、label_valuesという関数を使ってPrometheusから特定ラベルのリストを取得することもできます。設定した変数は、各PromQLで$variable_name形式で使用できます。Grafanaダッシュボードの変数が非常に便利な機能ですが、変数機能を使用するとGrafana Alertを登録できないという問題もあります。このような場合は、アラート専用ダッシュボードを別途作成して使用することをお勧めします。

パネル追加ボタンをクリック

クエリ追加ボタンをクリック

PromQL入力

  • 2つ以上のPromQLを作成してグラフを作成することができます。
  • PromQLを含むほとんどのフィールド内で$variable_name 形式で、ダッシュボードの変数を使用できます。
  • 凡例名にPromQL結果のデータラベルを使用できます。{{ label_name }}形式で使用します。

付録

  1. すでにScouterやPinpointを使っているので、この場合は必要ありませんか?
    いいえ。厳密に言えばPrometheusとScouter(Pinpoint)は役割が違います。
    そのため、Javaアプリケーションをモニタリングするには、ScouterとPrometheusの両方が必要だと思われます。ScouterがJavaアプリケーション、特にTomcatなどのウェブ基盤アプリケーションに特化しています。各リクエストに対するメソッド呼び出しを追跡(tracing)できている場合、Prometheusはアプリケーションの状態を表す各指標(metric)を調べて照会することに特化しています。
    JVMに関する(heap、GCなど)指標は、重なるかもしれませんが、その他に使用するライブラリの指標や(Hystrix、Ribbonなど)、内部構成、ロジックに関する指標(Memtable状態、Redis使用率、毎秒メール送信量と遅延時間など)、さまざまな目的別に管理されているThread/Connection Poolなど、より具体的に収集可能であり、ユーザーの選択によって追加/削除することができます。また、Micrometerのようなツールを使って直接指標を作成することも非常に簡単です。そして、これらの指標をPromQLから好みに合わせて照会、処理することができます。
    森を見るにはPrometheusが適しており、木を見るには(そして追跡するには)Scouterが適しています。

  2. エンドポイントセキュリティ
    Prometheusは、収集のエンドポイントセキュリティについては関与していません。これはあくまでもクライアントの責任と考えています。エンドポイントも結局はHTTP APIなので、どのようにセキュリティを適用するかはユーザーの方が詳しいと判断したようです。Spring Bootの場合はSpring Boot Securityを使用することができ、より便利な設定のために、Spring Boot Actuatorは各エンドポイントのRequestMatcherを提供しています。

  3. DisableExplicitGC
    JMXを使ってモニタリングを行い、内部的にSystem.gc()が重要な役割をしていない場合は、-XDisableExplicitGCJVMオプションを追加することをお勧めします。
    JMXにアクセスするためにRMIポートを使用する場合、JVMはRMIに使用されたメモリをクリーンアップするため、定期的に強制的なFullGCを実行します。基本周期は1時間のため、このオプションを追加していなければ、JVMのヒープメモリに余裕があっても1時間単位で周期的なFullGCが発生することになるでしょう。(どのGCを使用するかによって異なる様子を見せることがあります)

  4. バッチのように短く実行されるアプリケーションは、どうすればよいですか?
    短いバッチ処理をモニタリングするため、PrometheusはPush Gatewayを提供しています。Dock処理を進めながら測定された指標をPush Gatewayに送信してモニタリングすることができます。ただし、Push Gatewayをバッチ処理に加え、他のすべての指標をPush収集する用途には使用してはいけません。PrometheusはあくまでもPull基盤のプラットフォームであり、Push Gatewayは大容量トラフィックと継続的な時系列データを受信するように設計されていません。

  5. クラスタリングと高可用性
    残念ながら、Prometheusは別途クラスタリングを構成することができません。また、高可用性も保証されません。Federationやremote write/readなどの機能によって補完できますが、さまざまな制限があります。これに関連してPrometheusコミュニティでもプロジェクトを進めています。Service as a Prometheusを目指す2つのプロジェクトが進行しています。両プロジェクトは目的は同じですが、方法は大きく異なっています。

NHN Cloud Meetup 編集部

NHN Cloudの技術ナレッジやお得なイベント情報を発信していきます
pagetop