Prometheusでのさまざまな監視データ取得法

Prometheusでは、Prometheus Serverが監視対象にアクセスしてデータを収集するアーキテクチャを採用している。このデータ収集のためのアクセス先(エンドポイント)は「exporter」と呼ばれており、さまざまなソフトウェアやハードウェア向けのものが提供されている。今回はこのexporterについて、独自のデータ取得方法なども含めて紹介する。

公式exporterとサードパーティによるexporter

Prometheusのアーキテクチャは前々回の記事で詳しく紹介しているが、Prometheus Serverが監視対象にアクセスしてデータを収集するような構成(Pull型アーキテクチャ)となっている(図1)。このようなアーキテクチャにおいては、監視対象ごとにPrometheus Serverにデータを渡すためのexporterを用意する必要がある。

図1 Prometheusのアーキテクチャ
図1 Prometheusのアーキテクチャ

たとえば「node_exporter」は実行しているホストの統計情報を収集するexporterだが、ホストの情報に加えてそのホストで稼動しているWebサーバーの統計情報も取得したいといった場合、node_exporterとは別にWebサーバーの統計情報をPrometheus Serverに渡すexporterを用意しなければならない。

Prometheus向けに用意されているexporterは、公式ドキュメントの「Exporters and Integrations」ページにまとめられている。たとえばデータベースではMySQLやPostgreSQL、Microsoft SQL Server、Memcached、CouchDB、Redisなどに向けたexporterが用意されている。

また、これ以外にもIPMIの情報を取得するものや、CephやGlusterといった分散型ストレージの情報を取得するもの、API経由でクラウドサービスの情報を取得するもの、ほかのモニタリングシステムと連携するものなど、さまざまなexporterが用意されている。そのため、よく使われるようなソフトウェアやハードウェアであれば容易に監視を行えるだろう。

ちなみにこのページに掲載されているexporterのうち、Prometheusプロジェクトが公式に開発に参加しているのは「official」と書かれているものだけで、それ以外のものはサードパーティによって開発されている。サードパーティ製だからといって品質が劣るというわけではないが、開発やメンテナンスが停滞する可能性はあるので導入時にはリポジトリなどを確認しておきたい。

exporterが提供されてないアプリケーションや、独自に開発したシステムを監視したいという場合、独自にexporterを作成することで対応できる。Prometheusではそのためのライブラリ(Client Libraries)も提供している。これについては後述する。

既存のexporterを使ってみる

公開されているexporterの多くは、単体で実行させるプログラムとなっている。監視対象のデータを適当なタイミングで取得し、Prometheus Serverからのリクエストに応じてそれらをフォーマッティングして送信する、という仕組みだ。以下ではこれらのうちのいくつかを紹介しよう。

MySQL向けのexporter「MySQL server exporter」

まず紹介するのは、MySQL向けのexporterである「MySQL server exporter(mysqld_exporter)」だ。

mysqld_exporterはPrometheusプロジェクトが開発する「official」なexporterの1つだ。ほかのPrometheusプロジェクトのコンポーネント同様、ダウンロードページでコンパイル済みのバイナリが配布されているほか、Docker向けのイメージも公開されている。また、Debian 9(stretch)では「prometheus-mysqld-exporter」という名称で公式パッケージも提供されている。

mysqld_exporterでは監視対象のMySQLサーバーにアクセスし、MySQLのシステム変数(「SHOW VARIABLES」コマンドで取得できる変数の値)およびサーバーステータス変数(「SHOW STATUS」コマンドで取得できる変数の値)、INFORMATION_SCHEMAテーブルの値を取得する。そのため、MySQLサーバーにあらかじめ監視用のユーザーを作成し、適切な権限を付与しておく必要がある。たとえば監視用に「exporter」というユーザーを利用する場合、次のようなSQLコマンドを実行すれば良い。

CREATE USER 'exporter'@'localhost' IDENTIFIED BY '<パスワード>';
GRANT PROCESS, REPLICATION CLIENT, SELECT ON *.* TO 'exporter'@'localhost';

ここではmysqld_exporterをMySQLサーバーと同じホストで実行させる前提としているが、それ以外の場合、適切にホスト名等を変更する必要がある。

MySQLサーバー側での設定が完了したら、続いてmysqld_exporterのインストールを行う。Debian 9など、パッケージを利用できる環境であればそれを利用することをおすすめする。また、Dockerイメージを利用する場合は次のようにdockerコマンドを実行すれば良い。

docker pull prom/mysqld-exporter
docker run -d --name mysqld-exporter -e DATA_SOURCE_NAME="exporter:<パスワード>@(localhost:3306)/" -p 9104:9104 prom/mysqld-exporter

mysqld-exporterでは、「DATA_SOURCE_NAME」環境変数でMySQLサーバーへの接続情報を指定できるようになっている。ここではこれを利用してユーザー名やパスワード、接続先ホスト、ポートなどを指定している。

PrometheusのWebサイトで公開されているバイナリを利用する場合は、前々回記事でも説明したとおりsystemd経由で起動すると良いだろう。まず、ダウンロードしたバイナリを展開して適当なディレクトリ(今回は/usr/local/bin)にコピーする。

$ tar xvzf mysqld_exporter-0.10.0.linux-amd64.tar.gz
$ cd mysqld_exporter-0.10.0.linux-amd64/
# cp mysqld_exporter /usr/local/bin/

続いて、以下の内容の設定ファイルを「/etc/systemd/system/prometheus-mysqld-exporter.service」という名前で作成する。

[Unit]
Description=Prometheus exporter for MySQL server

[Service]
Restart=always
User=prometheus
Environment="DATA_SOURCE_NAME=exporter:<パスワード>@(localhost:3306)/"
ExecStart=/usr/local/bin/mysqld_exporter

[Install]
WantedBy=multi-user.target

最後に「systemctl daemon-reload」コマンドでsystemdの設定を再読込みすれば完了だ。これで「systemctl start prometheus_mysqld_exporter」コマンドでmysqld_exporterが起動できるようになる。

# systemctl daemon-reload
# systemctl start prometheus_mysqld_exporter

あとはPrometheusの設定ファイル(/etc/prometheus/prometheus.yml)の「scrape_configs:」セクションにmysqld_exporterからデータを取得するための設定を追加し、Prometheus Serverを再起動すればデータ取得が開始される。

  - job_name: mysql
    static_configs:
      - targets: ['localhost:9104']
# systemctl restart prometheus

前述のおり、mysqld_exporterではMySQLサーバーのサーバーステータス変数やシステム変数、INFORMATION_SCHEMAテーブルの情報を確認できる。たとえばSQLコマンドの発行状況は、「mysql_global_status_comands_total」で確認可能だ。MySQLサーバーのサーバーステータス変数ではコマンドの種類毎に別の変数に回数が格納されるが(MySQLのドキュメント、mysqld_exporterではこれらをラベルを使って分類するようになっている(図2)。

図2 mysqld_exporterによるコマンド発行状況の収集結果
図2 mysqld_exporterによるコマンド発行状況の収集結果

Apache HTTP Serverの情報を取得できる「Apache exporter」

続いては、サードパーティ製のexporterである「Apache exporter(apache_exporter)」を紹介しよう。このexporterは名前のとおり、Apache HTTP Serverの統計情報を取得するexporterだ。

Apache HTTP Serverでは、Webサーバーの動作状況を取得できる「mod_status」というモジュールが用意されている。apache_exporterはこのmod_status経由でステータス情報を取得する仕組みとなっている。

apache_exporterもPrometheusと同様Go言語で実装されている。GitHubリポジトリでソースコードが公開されているほか、リリースページからコンパイル済みバイナリも入手可能だ。

Apache exporterを利用するには、あらかじめApache HTTP Serverでmod_statusを有効にし、ステータス情報取得が行えるようになっている必要がある。多くのLinuxディストリビューションではApache HTTP Serverにmod_statusが同梱されているため、設定ファイルを変更するだけで良い。たとえばローカルホストから「http://localhost/server-status」というURLでステータスを取得できるようにするには、Apache HTTP Serverの設定ファイルに次のような内容を追加する。

<Location /server-status>
    SetHandler server-status
    Order deny,allow
    Deny from all
    Allow from localhost
</Location>

ここでは「Deny from all」および「Allow from localhost」というアクセス制限設定を行うことで、localhostからのアクセスのみを許すようにしている。localhost以外からのアクセスできるようにするには「Allow from localhost」の部分を適宜修正すれば良い。

apache_exporterのインストールについては、mysqld_exporterと同様GitHubのリリースページからバイナリをダウンロードして適当なディレクトリ(今回は/usr/local/bin)にコピーし、systemd向けの設定ファイルを作成する、という流れで行える。

$ wget https://github.com/Lusitaniae/apache_exporter/releases/download/v0.4.0/apache_exporter-0.4.0.linux-amd64.tar.gz
$ tar xvzf apache_exporter-0.4.0.linux-amd64.tar.gz
$ cd apache_exporter-0.4.0.linux-amd64
# cp apache_exporter /usr/local/bin/

systemd向けの設定ファイルは次のようになる。今回はこれを「 /etc/systemd/system/prometheus-apache-exporter.service」として保存した。

[Unit]
Description=Prometheus exporter for Apache HTTP Server

[Service]
Restart=always
User=prometheus
ExecStart=/usr/local/bin/apache_exporter -scrape_uri "<Apache HTTP Serverのステータス情報取得URL>"

[Install]
WantedBy=multi-user.target

「Apache HTTP Serverのステータス情報取得URL」部分は、Apache HTTP Serverの設定ファイルで指定したもの(たとえば「http://localhost/server_status」など)を指定する。

設定ファイルの作成・修正後に「systemctl daemon-reload」を実行して設定を反映させたら、「systemctl start prometheus-apache-exporter」コマンドでapche_exporterを起動できる。

# systemctl daemon-reload
# systemctl start prometheus-apache-exporter

apache_exporterの起動後、curlコマンドで「http://localhost:9117/metrics」というURLにアクセスし、データを取得できているかを確認しておこう。

$ curl http://localhost:9117/metrics

最後にPrometheusの設定ファイル(/etc/prometheus/prometheus.yml)の「scrape_configs:」セクションに下記のようなapache_exporterからデータを取得するための設定を追加し、Prometheus Serverを再起動する。

  - job_name: apache
    static_configs:
      - targets: ['localhost:9117']
# systemctl restart prometheus

これでデータ取得のための設定が完了し、Prometheus ServerがApache HTTP Serverの監視を行い始める。

apache_exporterでは、以下の情報が取得可能だ。

apache_accesses_total
apache_scoreboard
apache_sent_kilobytes_total
apache_cpu_load
apache_up
apache_uptime_seconds_total
apache_workers

たとえば「apache_accesses_total」ではApache HTTP Serverへのこれまでのアクセス数を、「apache_sent_kilobytes_total」では送信したデータ量の合計(単位はKB)を確認できる(図3)。

図3 apache_exporterではApache HTTP Serverへのアクセス数や送信したデータ量などを取得できる
図3 apache_exporterではApache HTTP Serverへのアクセス数や送信したデータ量などを取得できる

記録データの管理

Prometheus Serverはローカルストレージに独自形式で取得したデータを保存する仕組みとなっている。Prometheusのドキュメントによると、Prometheusでは取得したデータを圧縮して保存しており、1サンプル当たりに必要な容量はPrometheus 1系では3.3バイト、Prometheus 2系ではでは1サンプルあたり1~2バイト程度になるという。このように1サンプル当たりのデータ量は少ないものの、データ取得の頻度を短くしたり、監視対象を多くすると当然データ量は増える。たとえば、15秒に1回、1000種類のデータを取得するというケースの場合、単純計算でデフォルトではバージョン1で1日当たりおよそ19MB、バージョン2系では12MB(1サンプルあたり2バイトで換算)程度のストレージ容量が必要となる。

バージョン1系の場合
(60 / 15)× 60 × 24 × 1000 × 3.3 = 19008000

バージョン2系の場合
(60 / 15)× 60 × 24 × 1000 × 2 = 11520000

ちなみに、取得するデータの種類がどれくらいあるのかは、exporterが出力するデータを見ることで確認できる。exporterにcurlコマンドでアクセスするとテキスト形式で取得したデータを取得できるので、そこから「#」で始まるコメント行を除いた行数がデータ数となる。これは、次のようにしてカウントできる。

$ curl http://<ホスト>:<ポート>/metrics | grep -v "#" | nl

実際にnode_exporterおよびmysql_exporterを使用しているテスト環境(Prometheus 1.8を使用)で確認したところ、node_exporterのデータ数は1652、prometheus自体の出力するデータ数は321、mysql_exporterのデータ数は1688だった。この場合、1日分のデータを保存するのに必要なストレージ容量はおよそ70MBとなる。

Prometheus Serverのデフォルト設定では取得したデータの保持期間として360時間(15日)が設定されている。これを過ぎたデータについては自動的に破棄されるようになっているので、最大で必要となるストレージ容量は70MB×15で約1GBと試算できる。実際にデータが格納されているディレクトリの容量を確認したところ、926MBとおおよそこの試算と一致していた。

ちなみにデータの保存先はDebianやUbuntuで公式パッケージを使ってインストールした場合、/var/lib/prometheus/metrics以下となる。また、Prometheusプロジェクトが提供している公式Dockerイメージでは、/prometheus以下にデータを保存する設定となっている。

Prometheus Serverのストレージ関連設定

さて、データの保持期間や圧縮方法などについては、Prometheus Serverの起動時のオプションで変更できるのだが、バージョン2ではバージョン1系とは異なるストレージエンジンが導入されたため、設定可能な項目が大きく変更された(表1、2)。

表1 主要なストレージ関連オプション(バージョン1系の場合)
オプション名 説明 デフォルト値
-storage.local.path データの保存先 ./data
-storage.local.retention データの保存期間 360h0m0s
-storage.local.chunk-encoding-version データ圧縮方式 1
-storage.local.checkpoint-interval メモリ内に一時的に保存していたデータをストレージに書き出す頻度 5m0s
-storage.local.checkpoint-dirty-series-limit メモリ内に一時的に保存するデータ量の上限 5000
-storage.local.dirty 起動時に強制的にストレージ内データの整合性確認処理を実行する
表2 主要なストレージ関連オプション(バージョン2系の場合)
オプション名 説明 デフォルト値
-storage.tsdb.path データの保存先 ./data
-storage.tsdb.retention データの保存期間 15d
-storage.tsdb.min-block-duration データを取得してから圧縮を開始するまでの最小時間 2h
-storage.tsdb.max-block-duration データを取得してから圧縮を開始するまでの最大時間 (データ保存期間の10%)
-storage.tsdb.no-lockfile データ保存先ディレクトリにロックファイルを作らない

Prometheus 1系ではストレージに関連するさまざまなパラメータがあり、取得するデータ量が多い場合などはチューニングが必要だった。バージョン2系ではこれらチューニング用パラメータが減り、「-storage.tsdb.min-block-duration」および「-storage.tsdb.max-block-duration」の2つのみになっている。

なお、Prometheus Serverでは自身の統計情報も取得できるが、そのうち「prometheus_local_storage_」で始まるデータでストレージ関連の動作状況を確認できる。Prometheus Server自体のパフォーマンスチューニングを行う場合、これらデータが参考になるだろう。

クラッシュ時のデータ復元(Prometheus 1系の場合)

Prometheus 1系の場合、Prometheus Serverはexporterから取得したデータをすぐにはストレージに記録せず、一時的にメモリ内に保持し、一定期間後にまとめてストレージに書き出すようになっている。そのため、なんらかのトラブルでPrometheus Serverがクラッシュした場合、クラッシュ直前の一定期間のデータは消失する可能性がある。ストレージへの書き出し間隔は、「-storage.local.checkpoint-interval」オプションで変更できる。この間隔を小さくすることで、クラッシュ時のデータ消失を抑えることができる一方、頻繁な書き出しはパフォーマンス低下に繋がる可能性がある点には注意したい。

また、Prometheus Serverは起動時に前回適切にPrometheus Serverが終了したかをチェックし、必要に応じてストレージ内のデータの整合性確認処理を実行する。もしここで不整合が確認された場合復旧処理が行われて、不整合なデータは-storage.local.pathオプションで指定したディレクトリ以下の「orphaned」ディレクトリに移動される。また、「-storage.local.dirty」オプション付きでPrometheus Serverを実行することで、強制的に整合性確認処理を実行させることも可能だ。

自動クラッシュ復元とデータチャンク(Prometheus 2系の場合)

Prometheus 2系では、なんらかのトラブルでPrometheus Serverがクラッシュした場合でも、取得したデータを失わないようにする仕組みが導入されている。そのためか、データのストレージへの書き出しタイミングを調整するオプションや整合性確認を手動で実行するオプションは廃止されているようだ。

また、Prometheus 2系のストレージでは、取得したデータが記録されているファイルをバックグラウンドで徐々に圧縮していくという手法が導入された。この際の挙動を設定するオプションが「-storage.tsdb.min-block-duration」および「-storage.tsdb.max-block-duration」だ。前者は圧縮処理を開始するまでの最小の時間を、後者は圧縮処理を開始するまでの最大の時間を指定できる。

クライアントライブラリの利用

最後に、exporterが用意されていないような独自のアプリケーションなどをPrometheusで監視する方法について紹介しよう。こういった目的に向けて、Prometheusでは「Client Libraries(クライアントライブラリ)」というライブラリが用意されてる。Go言語およびJava/Scala、Python、Ruby向けのクライアントライブラリが公式に提供されているほか、サードパーティ製ライブラリとしてC++、Common Lisp、Lua、.NET(C#)、Node.js、PHP、Rustといった言語向けのものも開発されている。

また、これらクライアントライブラリを使わずにexporterを実装することも可能だ。PrometheusはHTTPで監視情報のやり取りを行うため、HTTPサーバーにPrometheusが使用するフォーマットで監視情報を出力する機能を実装すれば良い。とはいえ、通常のケースではクライアントライブラリを利用するほうが簡単なので、このやり方についてはあまりおすすめしない。

Prometheusのデータモデル

各言語向けのクライアントライブラリの詳細については本稿では解説しないが、独自のexporterを作成するのに必要となるPrometheusのデータモデルについて説明しておこう。

Prometheusでは、統計データの種類として次の4種類が用意されている。独自のexporterを作成する場合、出力するそれぞれの統計情報はこれらのいずれかの形式になっていなければならない。

  • Counter
  • Gauge
  • Histogram
  • Summary

クライアントライブラリではこれらのデータ型ごとにクラスが用意されており、データ型によって実行できる操作も異なる。

まず「Counter」(カウンター)だが、これはその名の通り対象とするイベントが発生した回数の総計や経過時間など、単調増加する(減少しない)値を格納することを目的とした形式だ。たとえばnode_exporterのCPU時間などはこの形式のデータとなっている。Counter型のオブジェクトでは値のインクリメント処理のみが可能で、任意の値にセットすることはできない。

「Gauge」(ゲージ)はCounterと似ているが、増加だけでなく減少も許されている。node_exporterではメモリ空き容量などでこの形式が使われている。Gauge型オブジェクトについてはインクリメントおよびデクリメント、任意の値へのセットが可能だ。

「Histogram」(ヒストグラム)および「Summary」(サマリ)はどちらも統計値の集合とその値が発生した回数を記録する形式だ。HistogramおよびSummary型オブジェクトでは、観測した値をオブジェクトに格納する処理のみを実行できる。このとき、値の出現回数のカウントなどが自動的に行われる。

ドキュメントによると、Histogramは事前に統計値の集合が予期できる場合に各統計値の出現回数を数えるといった用途に向いており、Summaryは中央値や最大値/最小値といった分布に関するデータを扱いたい場合に向いているとされている。また、Summaryはデータ記録時の処理コストが高く、逆にHistogramは取得したデータのクエリ時のコストが高いという性質がある。たとえばapache_exporterではリクエストサイズやレスポンスサイズの統計を取るためにSummary形式が使われている。

これらデータ形式ごとに、クエリ時に利用できる関数が異なる点にも注意したい。たとえば「histogram_quantile()」関数はHistgram形式のデータにのみ利用できる。

独自のexporterを作成する流れ

クライアントライブラリを使って独自のexporterを実装する際の処理の流れは以下のようになる。

  1. 出力したいデータに対応するオブジェクトを作成する
  2. データを取得してオブジェクトにその値をセットする
  3. Prometheus Serverからのリクエストに応じて出力を行う

以下は例としてPython版のクライアントライブラリを使っているが、ほかの言語向けのライブラリでも基本的な流れは同じだ。

まず、出力したい統計データに対応するオブジェクトを作成する。たとえば次のPythonコードは、「foobar_reuqest_per_seconds」という名前のGauge型のデータを出力する場合の例だ。ここでは名前に加えて、その説明についても定義できる。

from prometheus_client import core, Gauge, generate_latest
REQUEST_PER_SECONDS = Gauge('foobar_reuqest_per_seconds', 'Request per seconds')

また、ラベルを使用したい場合はオブジェクトの作成時点でその情報を与えておく必要がある。たとえば「method」および「code」の2つのラベルを使用したい場合、次のようにする。

REQUEST_PER_SECONDS = Gauge('foobar_reuqest_per_seconds', 'Request per seconds', ['method', 'code'])

続いて、このオブジェクトに値をセットする。前述のように、Gaugeオブジェクトでは任意の値を加算する.inc()、減算する.dec()、任意の値にセットする.set()メソッドが利用できる。なお、クライアントライブラリでは任意のタイミングでオブジェクトに値をセットすることができる。たとえばタイマーなどを使って一定期間おきにデータを取得しても良いし、何らかのイベントをトリガーにして値をセットすることも可能だ。

REQUEST_PER_SECONDS.set(<取得した値>)

値をセットする際、.labels()メソッドを使用してラベル情報を付与することもできる。

REQUEST_PER_SECONDS.labels("ラベル1", "ラベル2", ...).set(<取得した値>)

最後に、Prometheus Serverからのリクエストに応じて取得したデータを出力する処理を実装する。Pythonクライアントの場合、prometheus_clientモジュールに含まれているcoreプロパティおよびgenerate_latestメソッドを使って、次のように出力データを取得できる。

output = generate_latest(core.REGISTRY)

あとはこのデータをHTTP/HTTPSライブラリなどを使って出力すれば良い。

なお、Pythonクライアントの場合PythonデフォルトのHTTPライブラリ向けハンドラやTwisted、WSGIといったフレームワーク向けのハンドラクラスが用意されており、これらを利用することでより簡便にexporterを実装できるようになっている。そのほか、詳しい実装についてはClient Librariesページからリンクされている各言語ライブラリ向けの情報を確認して欲しい。

アプリケーション内に直接exporter機能を実装することも可能

現在公開されているexporterの多くは独立したプロセスとして実装されている。しかし、独自のアプリケーションなどをPrometheusによる監視に対応させる場合、アプリケーション自体にexporter機能を追加するという実装方法もある。これはPrometheusのクライアントライブラリを利用すれば比較的容易に実現できる。クライアントライブラリにはプロセス情報を自動的に取得する機能があり、このような実装にすることで監視対象のプロセス情報も同時に取得できるようになる。

とはいえ、アプリケーションの構造やセキュリティ要件などによってはこういった実装は難しいケースもあるので、状況に応じて適切な実装を選択するのが良いだろう。

exporterの準備は比較的容易

前述のとおり、Prometheusでは監視対象が自発的に統計データを出力するのではなく、Prometheus Serverからのリクエストに応じて統計データを出力する仕組みになっている。そのため監視対象にexporterを設置するという手間はあるが、今回解説したとおりこの作業は容易に行える。今後ディストリビューション側でのサポートが進めば、exporterの導入はより容易になるだろう。また、独自のexporter作成も比較的容易だ。どのようなデータを出力するか、といった検討は必要だろうが、気軽に試して見ると良いだろう。