Diary of a Perpetual Student

Perpetual Student: A person who remains at university far beyond the normal period

監視ツールのカウンタへの向き合い方

arthur-1 Mackerel Advent Calendar 2023ラソン3日目の記事です。

カウンタ

監視で参照したい値の種類の1つにカウンタがあります。カウンタとは時間が経過するごとに累積して単調増加していく数量のことです。基本的には増加していくだけなのですが、特定のタイミングでカウンタがリセットされることもあります。

一番わかりやすい例はuptimeでしょう。サーバが起動してからの経過時間は単調増加していき、再起動するとカウンタが0に戻ります。

カウンタはその性質から、プログラム上ではunsigned integerとして表現されることが多いです。マイナスになることがないので、unsignedにすることでより大きな値を扱うことができます。

OpenTelemetry Metrics APIで実装されたメトリックの種別の1つとしても「Counter」が存在します。

カウンタの差分

カウンタそのものをメトリックとして監視サービスに投稿することもありますが、そのままでは扱いづらいこともあります。例えば、カウンタそのものを閾値を設定して監視する際には閾値を定期的に変えなければなりません。過去のグラフと現在のグラフを見比べるときも、絶対値が変わっているのでグラフの伸縮率なども違ってしまい比較しづらいこともあるでしょう。

そもそも、我々の興味の対象はカウンタそのものの値よりも、各時点でのカウンタの増加率だったり、他のカウンタの値と比較した割合であることがしばしばあります。

例えば、mackerel-agentがLinux OSからCPU使用率を取得するときに参照しているのは/proc/statという仮想ファイルなのですが、このファイルに直接CPU使用率が書かれているわけではありません。以下のように各種別(user, system, iowaitなど)ごとのCPU時間がカウンタ(すなわち、サーバが起動してからの累積のCPU時間)として記録されており、ここからCPU使用率を計算しています。

$ cat /proc/stat | grep cpu0
cpu0 82712 2186 88894 36369261 35961 0 20988 53626 0 0
$ cat /proc/stat | grep cpu0
cpu9 82720 2186 88904 36372456 35962 0 20989 53629 0 0

1分間隔を空けて2回実行することで、この1分間にどれだけCPU時間を消費したかがわかります。最新値から1分前の値との差分を取れば良いわけです。さらに1分間のトータル(user+nice+system+idle)のCPU時間を計算した上で割り算してあげると、各種別ごとのCPU使用率が得られます。

カウンタの差分はオーバーフローし得る

さて、単調増加するカウンタの差分は必ず正の値になりそうですが、そうもいかないことがあります。なぜならカウンタはリセットされることがあるからです。

カウンタがリセットされると0に戻り、その後測定するタイミングで0ではないにしろ前回の計測値よりは小さい値になっています。つまり、以下のような引き算が行われます。

var previous uint64 = 123456
var current uint64 = 30
diff := current - previous
fmt.Printf("%d - %d = %d\n", current, previous, diff)
// 30 - 123456 = 18446744073709428190

カウンタはunsigned intなので単純に引き算するとその結果もunsignedになるわけですが、カウンタがリセットされた場合には負のオーバーフローが発生してものすごく大きな値になってしまいます。

カウンタのリセットを検知していい感じにする

そこで、リセットされ得るカウンタの差分を取る際には、リセットされた(すなわち、現在値のほうが前回の値より小さい)時に0との差を差分とすると良いでしょう。

Prometheusのrate、irate関数などはこのような方針で実装されており、カウンタがリセットされてもオーバーフローしたり負の値になったりしません。

この手法では多少の誤差は許容することになりますが、この程度の誤差よりも計算結果がオーバーフローするデメリットの方がはるかに大きいです。閾値監視している時にあり得ないほどの大きな値として計測されアラートが発報されてしまいますし、平均値監視をするときも外れ値として大きく影響することになります。また、グラフも以下のようにオーバーフローした値が目立ち、他の変化が追いづらくなってしまうでしょう。

今年出したMackerelのOSSへのPRを見る

自分が今年MackerelのOSSに出したPRを振り返ると、カウンタのオーバーフローに関するものが2つもありました。

github.com

github.com


以上、監視ツールの中身がわかる話として楽しんでいただけたら幸いです。