LoginSignup
45

More than 5 years have passed since last update.

サンプルで学ぶ!PromQLで自在にグラフを描こう (Prometheus + Grafana)

Posted at

背景

PrometheusとGrafanaを使ってリソース監視を始めてみたはいいものの、Grafana Dashboards | Grafana Labsにあるダッシュボードはなんかしっくり来ない。しかも、どれも微妙にクエリが違っててどれが正しいのかわからない!

ならば自分でちゃんとクエリを書いてみよう!ということで書いてみます。ここで紹介する3種類のクエリをマスターすれば、だいたい応用が効くと思います。

参考

基本的なことですが、Grafanaでグラフを書くときに入力するクエリ文は、PrometheusのPromQLです。(最初Grafanaのクエリ構文だと思ってた…)

node-exporterのメトリクスは、curlしてみるとヘルプもついてきます。デモサイトもあるのでこちらでもOK

Grafana Templating

GrafanaのTemplating機能を使って、クエリに変数を埋め込みます。以下の変数を定義している前提で記述します。

変数名 説明 サンプル値
node クエリの対象とするノード。Prometheusが自動的に付けるinstanceラベルに対して使用することを想定 localhost:8080
interval クエリで表示する値の単位時間 5m

CPUの使用率

スクリーンショット 2017-11-09 16.39.44.png

クエリ

avg(irate(node_cpu{instance="$node",mode!="idle"}[$interval])) without(cpu)

概要

指定インスタンス($node)の、単位時間($interval)内での1秒ごとのCPU使用率の平均を表示します。また、idleに関しては空きを示すため表示しません。

Prometheusの生データ抜粋

# HELP node_cpu Seconds the cpus spent in each mode.
# TYPE node_cpu counter
node_cpu{cpu="cpu0",instance="docker104:9100",job="node",mode="iowait"} 1.57
node_cpu{cpu="cpu0",instance="docker104:9100",job="node",mode="system"} 42.51
node_cpu{cpu="cpu0",instance="docker104:9100",job="node",mode="user"}   74.37
node_cpu{cpu="cpu1",instance="docker104:9100",job="node",mode="iowait"} 5.13
node_cpu{cpu="cpu1",instance="docker104:9100",job="node",mode="system"} 34.1
node_cpu{cpu="cpu1",instance="docker104:9100",job="node",mode="user"}   72.7

node_cpuメトリクスには、各インスタンスのCPUごとにCPU使用時間(秒)がmode単位で格納されています。

クエリ解説

  1. 欲しいデータは指定インスタンスのmodeidleでないCPUの使用時間の直近$interval分間のデータです
    • node_cpu{instance="$node",mode!="idle"}[$interval]
    • この時点で、modeの数 x CPUコア数 のレコードが返り、valueとして$interval分間のデータが配列で格納されています
  2. 返ったデータを、1秒ごとの平均増加値に直します
    • irate()
    • この時点で、modeの数 x CPUコア数 のレコードが返り、valueが1秒ごとの平均増加値に変換されます
  3. すべてのCPUの平均を取ります
    • avg() without(cpu)
    • without句には、集計関数を適用するときに無視したいラベルを指定します。今回はcpu,instance,job,modeというラベルがあり、cpuを無視してモードごとの平均を取ります。結果的に、すべてのcpuの平均を取ることになります

avg

avg演算子は、レコードごとに格納されたスカラー値の平均値を返します。レコードのvalueが配列(Range Vector)の場合は適用できません。

rate()irate()

rate()irate()関数はどちらも、ある時間範囲で取得したデータが、1秒あたり平均どれだけ増加しているかを計算します。

あくまで増加量のため、この関数を適用すべきメトリクスはnode_cpuhttp_requests_totalなど、何らかの合計値を保持するもののみになります。

rate(v range-vector) calculates the per-second average rate of increase of the time series in the range vector.
irate(v range-vector) calculates the per-second instant rate of increase of the time series in the range vector.

Query functions | Prometheus

withoutby

without句は上述の通り、指定したラベルを無視して集計関数を適用します。実際にwithout(cpu)を実行した場合の生データは以下のように、cpuラベルが欠落した状態になります。

{instance="docker104:9100",job="node",mode="user"}  0.0005000000000002558

by句は逆に、指定したラベルに絞って、つまり他のラベルを無視して集計関数を適用します。by(mode)を実行すると、mode以外のすべてのラベルが欠落した状態になります。

{mode="user"}   0.0005000000000002558

今回はラベルが4つで、instancejobラベルは固定です。つまり、cpumodeしか返されるレコード中で変化しないため、値だけ見ればwithout(cpu)by(mode)は同じ結果になります

CPUのコア数

スクリーンショット 2017-11-09 16.38.52.png

以下のクエリはどちらも同じ値を返します。

count(count (node_cpu{instance="$node"}) by(cpu))
count(node_cpu{instance="$node", mode="system"})

概要

CPUのコア数を直接返すメトリクスはありません。node_cpuメトリクスは上述の通り、CPUごとに使用率を返すため、これを利用して計算します。

クエリ解説

count(count (node_cpu{instance="$node"}) by(cpu))
  1. 指定インスタンスの直近のnode_cpuメトリクスを取得します
    • node_cpu{instance="$node"}
    • この時点で、modeの数 x CPUコア数 のレコードが返り、valueとして直近のデータがスカラー値で格納されています
  2. CPU単位で、メトリクスの数を取得します
    • count() by(cpu)
    • この時点で、system,userなどのCPU使用率データの個数がCPUごとに返ります。この個数は目的の値でないので無視します
  3. CPUの数を数えます
    • count()
count(node_cpu{instance="$node", mode="system"})
  1. 指定インスタンスのmode="system"となるメトリクスを取得します
    • node_cpu{instance="$node", mode="system"}
    • この時点で、CPUコア数分のsystem使用率が返ります
  2. CPUの数を数えます
    • count()

空きメモリ量

スクリーンショット 2017-11-09 16.39.59.png

( avg_over_time(node_memory_MemFree{instance=~"$node"}[$interval])
  + avg_over_time(node_memory_Buffers{instance=~"$node"}[$interval])
  + avg_over_time(node_memory_Cached{instance=~"$node"}[$interval])
) / node_memory_MemTotal{instance=~"$node"} * 100

概要

空きメモリ量は、Linuxカーネル3.14以上であればnode_memory_MemAvailableメトリクスが使用可能ですが、そうでないホストがあることを考慮しています。

クエリ解説

繰り返しとなるため以下の部分のみ解説します。

avg_over_time(node_memory_MemFree{instance=~"$node"}[$interval])
  1. 指定インスタンスの直近の$interval分間のnode_memory_MemFreeメトリクスを取得します
    • node_memory_MemFree{instance=~"$node"}[$interval]
    • この時点で、1レコードのみ返り、valueに$interval分間のすべてのデータが配列で格納されています
  2. 取得されたvalueの平均値を取得します
    • avg_over_time()

avg_over_time()

ある時間範囲で取得したデータの、それぞれのデータの平均を取りたい場合はavg_over_time()を使用します。上述のrate()系がサンプルでたくさん出てくることもあり、わかりにくいですが、こちらは単純に時間範囲の平均を計算できます。

まとめ

Prometheusのメトリクスにはデータ型があり、一口に「平均」といっても計算の仕方が複数あります。また、メトリクスも現在値を示すものと合計を示すものがあるため、このあたりを理解すればサクサク書けるようになれそうです。

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
45