お知らせ
私が開発した solr-exporter (https://github.com/mosuka/solr-exporter) は Apache Lucene/Solr プロジェクト (http://lucene.apache.org/solr/) へコントリビュートしました。
今後は Apache Solr に同梱される solr-exporter をご利用ください。
詳細は下記 URL を参照してください。
http://lucene.apache.org/solr/guide/monitoring-solr-with-prometheus-and-grafana.html
Solr のモニタリングに使えるソフトウェア で、Apache Solr (以下「Solr」) のモニタリングに使えるソフトウェアを紹介しました。
それらは、基本的に JMX の機能を利用して Solr のメトリクスをモニタリングできるようにしているものです。
Solr でインデックスしているデータをモニタリングすることはできません。
例えば、
- アプリケーションログを保存していたら、エラーがどれくらい、どのシステムで発生しているか
- EC であれば、商品カテゴリー毎の商品数の推移
- ユーザー投稿型のサービスであれば、問題のあるキーワードを含むコンテンツがどれだけあるか
Solr もインデックスされたデータの可視化であれば、Lucidworks が OSS で公開している Banana (Kibana3 相当) で行うことができます。これは Elastic が提供する Kibana にあたるソフトウェアです。
しかし、アラーティングまで行うとなると、X-Pack や、Yelp が OSS で公開している ElastAlert のようなソフトウェアが必要ですが、良さそうなのがなかなかありません。それに、監視のためにいろいろツールが増えてしまっても運用が大変になってきます。
そこで、モニタリングソフトウェアを Prometheus と Grafana に一本化し、Solr のメトリクスとインデックスをモニタリングできるようにする solr-exporter を作ってみました。
solr-exporter
solr-exporter を使うことで、Solr メトリクスと Solr がインデックスしているデータのモニタリングとアラーティングが可能となります。
下の図は、スタンドアローンの Solr へ接続する際のダイアグラムです。もちろん、SolrCloud へ接続することも可能です。
solr-exporter は Java で開発されています。
Java を採用した理由は、Solr プロジェクトが提供する SolrJ (Solr クライアントライブラリ) を使用するためです。
Solr は REST ライクなインターフェースを備えていて、HTTP でアクセスできるのですが、フェールオーバーなどを考慮すると実装が大変です。
SolrJ は スタンドアローンの Solr への接続だけでなく、SolrCloud を使用したクラスターの場合、ZooKeeper から Node Discovery を行なって、稼働中の Solr へ接続してくれます。
また、Solr に同梱されるクライアントライブラリなので、メンテンスも十分にされていて安心して使えます。
Solr に接続した solr-exporter は Solr のモニタリングに使える API で紹介した Metrics API や、Solr に対して全文検索を行い、それらのレスポンスを Prometheus のメトリクスとして公開 (expose) します。
設定は YAML フォーマットのファイルで管理されます。
返却される API の JSON レスポンスのスクレイピングには jq コマンドのクエリーを採用しています。
開発した経緯
solr-exporter を開発するに至った経緯は次のようになります。
-
検索エンジンとして Solr の運用ノウハウを持っている・監視システムとして Prometheus に統一・集約したい
- 想定する実行環境が Kubernetes で、Prometheus は Kubernetes との相性もよさそう
- Prometheus は、元 Google のエンジニアが Google 社内監視システムの Borgmon にインスパイアされて開発したもので筋がよさそう
- 監視システムとして、Solr と同じ Lucene をベースにした検索エンジンである Elasticsearch や、可視化・モニタリングのために Kibana、X-Pack を導入して運用コストを上げたくない
-
Solr でインデックスしたドキュメント (データ) のモニタリングをしたい
- 「このキーワードにマッチするドキュメントがどれくらいある」などをモニタリングしたい
- Solr の全文検索クエリーを実行したい
- Banana や Zeppelin では可視化はできても、アラーティングまでできない
- ElastAlert のような、Solr 向けのアラーティングのソフトウェアが欲しい
- メトリクスのような時系列データを Lucene の転置インデックスで扱うのに抵抗がある
- 時系列データには時系列データベースを使用した方がいいのではないか
- Solr の Exporter 作ったら、上記の課題を解決できる
というところから、「無いなら作ったらいいじゃない」ということで個人的に作ってみました。
インストール
solr-exporter のリポジトリのリリースページ から コンパイル済みのアーカイブをダウンロードします。
適当なディレクトリで解答してインストールは完了です。
$ cd ~/
$ unzip solr-exporter-0.3.8-bin.zip
$ cd solr-exporter-0.3.8
実行
インストールしたディレクトリ以下の ./bin/solr-exporter
を実行してください。
$ ./bin/solr-exporter -p 9983 -b http://localhost:8983/solr -f ./conf/config.yml
Windows 環境の人は、同じディレクトリの .\bin\solr-exporter.bat
を使用します。
> .\bin\solr-exporter.bat -p 9983 -b http://localhost:8983/solr -f .\conf\config.yml
SolrCloud モードでクラスタを組んでいる場合は、次のように ZooKeeper の接続文字列を指定します。
$ ./bin/solr-exporter -p 9983 -z localhost:2181/solr -f ./conf/config.yml
他のコマンドラインオプションについてはヘルプを確認してください。
$ ./bin/solr-exporter -h
usage: SolrCollector [-h] [-v] [-p PORT] [-b BASE_URL] [-z ZK_HOST] [-f CONFIG]
[-n NUM_THREADS]
Prometheus exporter for Apache Solr.
optional arguments:
-h, --help show this help message and exit
-v, --version show version
-p PORT, --port PORT solr-exporter listen port
-b BASE_URL, --baseurl BASE_URL
specify Solr base URL when connecting to Solr in standalone mode (for
example 'http://localhost:8983/solr')
-z ZK_HOST, --zkhost ZK_HOST
specify ZooKeeper connection string when connecting to Solr in
SolrCloud mode (for example 'localhost:2181/solr')
-f CONFIG, --config-file CONFIG
specify configuration file
-n NUM_THREADS, --num-thread NUM_THREADS
specify number of threads
設定ファイル例
solr-exporter の設定ファイルは次のような YAML ファイルになっています。
query
で Solr へのリクエストを設定し、jsonQueries
でそのレスポンスをパースする jq クエリーを設定します。
これは、Solr の solrconfig.xml
でリクエストハンドラのパスをユーザーが自由に変更できることに対応するのと、ユーザーが自由にメトリクス名、ラベル名を変更できるようにするためです。
メトリクス名などのルールについては Prometheus の Metric and label naming にガイドラインがありますので参考にしてみてください。
ping:
query:
path: /admin/ping
jsonQueries:
- |-
. as $object | $object |
(if $object.status == "OK" then 1.0 else 0.0 end) as $value |
{
name : "solr_ping",
type : "GAUGE",
help : "See following URL: http://lucene.apache.org/solr/guide/7_1/ping.html",
label_names : [],
label_values : [],
value : $value
}
metrics:
query:
path: /admin/metrics
params:
- group: 'all'
- type: 'all'
- prefix: ''
- property: ''
jsonQueries:
##############################
# jetty
##############################
# solr_metrics_jetty_response_total
- |-
.metrics["solr.jetty"] | to_entries | .[] | select(.key | startswith("org.eclipse.jetty.server.handler.DefaultHandler")) | select(.key | endswith("xx-responses")) as $object |
$object.key | split(".") | last | split("-") | first as $status |
$object.value.count as $value |
{
name : "solr_metrics_jetty_response_total",
type : "counter",
help : "See following URL: https://lucene.apache.org/solr/guide/7_1/metrics-reporting.html",
label_names : ["status"],
label_values : [$status],
value : $value
}
... [中略] ...
collections:
query:
path: /admin/collections
params:
- action: 'CLUSTERSTATUS'
jsonQueries:
# solr_collections_live_nodes
- |-
.cluster.live_nodes | length as $value|
{
name : "solr_collections_live_nodes",
type : "gauge",
help : "See following URL: https://lucene.apache.org/solr/guide/7_1/collections-api.html#clusterstatus",
label_names : [],
label_values : [],
value : $value
}
... [中略] ...
queries:
- query:
collection: collection1
path: /select
params:
- q: "*:*"
- start: 0
- rows: 0
- json.facet: |-
{
category: {
type: terms,
field: cat
}
}
jsonQueries:
# solr_facets_category
- |-
.facets.category.buckets[] as $object |
$object.val as $term |
$object.count as $value |
{
name : "solr_facets_category",
type : "gauge",
help : "Category facets",
label_names : ["collection", "term"],
label_values : ["collection1", $term],
value : $value
}
solr-exporter では、ping
、metrics
、collections
、queries
の設定を持っています。
- ping - Solr の Ping を利用し、コア、コレクションが利用可能かのメトリクスを取得
- metrics - Solr の Metrics API を利用して、メトリクスを取得
- collections - Solr の Collections API を利用して、クラスターの情報をメトリクスとして取得
- queries - Solr の検索を利用して、検索結果をメトリクスとして取得
Solr の返す JSON レスポンスを、次のフォーマットでメトリクスとして JSON を組み立てる jq のクエリー設定すると、Prometheus 形式のデータに変換して公開 (expose) します。
{
"name": "some_metric_name",
"type": "gauge",
"help": "describe metric.",
"label_names": ["label_name1", "label_name2"],
"label_values": ["label_value1", "label_value2"],
"value": 1.0
}
例えば、上記 JSON は、下記のような Prometheus フォーマットに変換されます。
# HELP some_metric_name describe metric.
# TYPE some_metric_name gauge
some_metric_name{label_name1 ="label_value1", label_name2 ="label_value2",} 1.0
solr-exporter を実行したら、Solr のメトリクスが expose できることを、次の URL へアクセスして確認してください。
$ curl -s 'http://localhost:9983/metrics'
# HELP solr_metrics_jetty_response_total See following URL: https://lucene.apache.org/solr/guide/7_1/metrics-reporting.html
# TYPE solr_metrics_jetty_response_total counter
solr_metrics_jetty_response_total{base_url="http://localhost:8983/solr",status="1xx",} 0.0
solr_metrics_jetty_response_total{base_url="http://localhost:8983/solr",status="2xx",} 516.0
solr_metrics_jetty_response_total{base_url="http://localhost:8983/solr",status="3xx",} 0.0
solr_metrics_jetty_response_total{base_url="http://localhost:8983/solr",status="4xx",} 0.0
solr_metrics_jetty_response_total{base_url="http://localhost:8983/solr",status="5xx",} 0.0
solr_metrics_jetty_response_total{base_url="http://localhost:8984/solr",status="1xx",} 0.0
solr_metrics_jetty_response_total{base_url="http://localhost:8984/solr",status="2xx",} 517.0
solr_metrics_jetty_response_total{base_url="http://localhost:8984/solr",status="3xx",} 0.0
solr_metrics_jetty_response_total{base_url="http://localhost:8984/solr",status="4xx",} 0.0
solr_metrics_jetty_response_total{base_url="http://localhost:8984/solr",status="5xx",} 0.0
... [中略] ...
# TYPE solr_collections_shard_leader gauge
solr_collections_shard_leader{zk_host="localhost:2181/solr",collection="banana-int",shard="shard1",replica="core_node3",core="banana-int_shard1_replica_n1",base_url="http://localhost:8983/solr",node_name="localhost:8983_solr",type="NRT",} 0.0
solr_collections_shard_leader{zk_host="localhost:2181/solr",collection="banana-int",shard="shard1",replica="core_node4",core="banana-int_shard1_replica_n2",base_url="http://localhost:8984/solr",node_name="localhost:8984_solr",type="NRT",} 1.0
solr_collections_shard_leader{zk_host="localhost:2181/solr",collection="apache-log",shard="shard1",replica="core_node3",core="apache-log_shard1_replica_n1",base_url="http://localhost:8983/solr",node_name="localhost:8983_solr",type="NRT",} 0.0
solr_collections_shard_leader{zk_host="localhost:2181/solr",collection="apache-log",shard="shard1",replica="core_node4",core="apache-log_shard1_replica_n2",base_url="http://localhost:8984/solr",node_name="localhost:8984_solr",type="NRT",} 1.0
solr_collections_shard_leader{zk_host="localhost:2181/solr",collection="solr-log",shard="shard1",replica="core_node3",core="solr-log_shard1_replica_n1",base_url="http://localhost:8983/solr",node_name="localhost:8983_solr",type="NRT",} 0.0
solr_collections_shard_leader{zk_host="localhost:2181/solr",collection="solr-log",shard="shard1",replica="core_node4",core="solr-log_shard1_replica_n2",base_url="http://localhost:8984/solr",node_name="localhost:8984_solr",type="NRT",} 1.0
# HELP solr_scrape_duration_seconds Time this Solr scrape took, in seconds.
# TYPE solr_scrape_duration_seconds gauge
solr_scrape_duration_seconds 1.584429789
上記のようにメトリクスを出力できていれば solr-exporter の準備は完了です。
デフォルトで、主要なメトリクスや、Solr の検索クエリーのサンプルが設定されています。
不要なメトリクスや、別のメトリクス、検索結果を取得したい場合は適宜修正をしてください。
Prometheus
ここから solr-exporter の公開するメトリクスを Pull してくるため、Prometheus の準備を行います。
Prometheus のインストールと設定
Prometheus のインストールについては、[Installation | Prometheus](https://prometheus.io/docs/prometheus/latest/installation/ を参考にして、環境に合わせて行なってください。
インストール後、prometheus.yaml
の scrape_configs
に solr-exporter の情報を追加します。
$ vi ./prometheus.yaml
# my global config
global:
scrape_interval: 15s # Set the scrape interval to every 15 seconds. Default is every 1 minute.
evaluation_interval: 15s # Evaluate rules every 15 seconds. The default is every 1 minute.
# scrape_timeout is set to the global default (10s).
... [中略] ...
# 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'
# metrics_path defaults to '/metrics'
# scheme defaults to 'http'.
static_configs:
- targets: ['localhost:9090']
- job_name: 'solr'
static_configs:
- targets: ['localhost:9983']
以上で、solr-exporter からメトリクスを Pull することができます。
Promethues の実行
次のコマンドで Prometheus を起動します。
$ ./prometheus --config.file ./prometheus.yml
Prometheus を起動すると、次の URL で Prometheus の UI にアクセスできます。
http://localhost:9090/graph
Status メニューから Targets をクリックすると、Prometheus が収集しているターゲットの一覧を確認できます。
次のように、先ほど登録した solr-exporter がターゲットに存在することを確認してください。一覧にない場合、または、エラーが発生する場合は、設定ファイルの内容が間違っていないか確認してください。
Prometheus が収集したメトリクスは、次のようにグラフで可視化できます。
が、やっぱりちょっと寂しいです。Grafana のサンプルダッシュボードを用意しているのでそちらを表示してみます。
Grafana
ここから Prometheus に保存されたメトリクスをより高度に可視化するため、Grafana の準備を行います。
Grafana のインストール
Grafana のインストールについては、Installing Grafana を参考にして、環境に合わせて行なってください。
Grafana ダッシュボード
Grafana を起動すると、次の URL で UI にアクセスできます。
http://localhost:3000
初期の管理者アカウントは次のようになります。
User : admin
Password : admin
今回、サンプルのダッシュボードを用意しました。次の URL で公開しています。
https://grafana.com/dashboards/3888
サンプルのダッシュボードは、次のようにしてインポートできます。
Dashboards
→ Import
で Import Dashboard
ダイアログを表示します。
Grafana.com Dashboard
に 3888
(サンプルのダシュボードの ID) を入力してください。
インポートに成功すると、次のようなダッシュボードが表示できます。
サンプルのダッシュボードなので、自由に編集して、好みのダッシュボードを作成してください。
まとめ
Solr の Metrics API の API のレスポンスに一貫性がなく、機械的に一発で Prometheus フォーマットへ変換することが困難でしたが、jq のクエリを使うことで、不揃いなフォーマットを統一したフォーマットに直すことができるようになりました。
Solr は、全文検索エンジンとして豊富な機能を提供し、稼働実績もあるソフトウェアですが、モニタリングやアラーティングについてはエコシステムが整っていないため、弱い部分です。
solr-exporter は、そんな Solr の弱点を、モニタリングやアラーティングで強力な機能を提供する Prometheus を繋ぐソフトウェアです。
実験的な試みなので、不具合もあるかもしれませんが、興味あれば使ってみてください。
また、もっとこうした方がいいなどあれば、ぜひ Pull Request ください。
Solr コミュニティでも Metrics API のレスポンスを Prometheus フォーマットで公開 (expose) する [SOLR-10654] Expose Metrics in Prometheus format という Issue も上がっています。
また、OpenMetrics という Prometheus のフォーマットをベースに、メトリクスのフォーマットの標準化をしようという試みもされています。
Prometheus Advent Calendar 2017 もあります。Prometheus にも興味がでてきた方は、こちらもご覧ください。
Solr のインデックスしたデータを抽出するには Facet や Stats API、検索クエリーを利用することになると思います。[改訂第3版]Apache Solr入門 に検索クエリーの書き方や各種 API が解説されているので、興味ある方は是非読んでみてください。