背景
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の使用率
クエリ
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
単位で格納されています。
クエリ解説
- 欲しいデータは指定インスタンスの
mode
がidle
でないCPUの使用時間の直近$interval
分間のデータですnode_cpu{instance="$node",mode!="idle"}[$interval]
- この時点で、
mode
の数 x CPUコア数 のレコードが返り、valueとして$interval
分間のデータが配列で格納されています
- 返ったデータを、1秒ごとの平均増加値に直します
irate()
- この時点で、
mode
の数 x CPUコア数 のレコードが返り、valueが1秒ごとの平均増加値に変換されます
- すべてのCPUの平均を取ります
avg() without(cpu)
-
without
句には、集計関数を適用するときに無視したいラベルを指定します。今回はcpu
,instance
,job
,mode
というラベルがあり、cpu
を無視してモードごとの平均を取ります。結果的に、すべてのcpuの平均を取ることになります
avg
avg
演算子は、レコードごとに格納されたスカラー値の平均値を返します。レコードのvalueが配列(Range Vector)の場合は適用できません。
rate()
とirate()
rate()
とirate()
関数はどちらも、ある時間範囲で取得したデータが、1秒あたり平均どれだけ増加しているかを計算します。
あくまで増加量のため、この関数を適用すべきメトリクスはnode_cpu
やhttp_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.
without
とby
without
句は上述の通り、指定したラベルを無視して集計関数を適用します。実際にwithout(cpu)
を実行した場合の生データは以下のように、cpu
ラベルが欠落した状態になります。
{instance="docker104:9100",job="node",mode="user"} 0.0005000000000002558
by
句は逆に、指定したラベルに絞って、つまり他のラベルを無視して集計関数を適用します。by(mode)
を実行すると、mode
以外のすべてのラベルが欠落した状態になります。
{mode="user"} 0.0005000000000002558
今回はラベルが4つで、instance
とjob
ラベルは固定です。つまり、cpu
とmode
しか返されるレコード中で変化しないため、値だけ見ればwithout(cpu)
とby(mode)
は同じ結果になります
CPUのコア数
以下のクエリはどちらも同じ値を返します。
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))
- 指定インスタンスの直近の
node_cpu
メトリクスを取得しますnode_cpu{instance="$node"}
- この時点で、
mode
の数 x CPUコア数 のレコードが返り、valueとして直近のデータがスカラー値で格納されています
- CPU単位で、メトリクスの数を取得します
count() by(cpu)
- この時点で、
system
,user
などのCPU使用率データの個数がCPUごとに返ります。この個数は目的の値でないので無視します
- CPUの数を数えます
count()
count(node_cpu{instance="$node", mode="system"})
- 指定インスタンスの
mode="system"
となるメトリクスを取得しますnode_cpu{instance="$node", mode="system"}
- この時点で、CPUコア数分の
system
使用率が返ります
- CPUの数を数えます
count()
空きメモリ量
( 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])
- 指定インスタンスの直近の
$interval
分間のnode_memory_MemFree
メトリクスを取得しますnode_memory_MemFree{instance=~"$node"}[$interval]
- この時点で、1レコードのみ返り、valueに
$interval
分間のすべてのデータが配列で格納されています
- 取得されたvalueの平均値を取得します
avg_over_time()
avg_over_time()
ある時間範囲で取得したデータの、それぞれのデータの平均を取りたい場合はavg_over_time()
を使用します。上述のrate()
系がサンプルでたくさん出てくることもあり、わかりにくいですが、こちらは単純に時間範囲の平均を計算できます。
まとめ
Prometheusのメトリクスにはデータ型があり、一口に「平均」といっても計算の仕方が複数あります。また、メトリクスも現在値を示すものと合計を示すものがあるため、このあたりを理解すればサクサク書けるようになれそうです。