2021-12-02

prometheusのrate()関数の罠

 久しぶりのAdventカレンダー挑戦、うまくいく気がしません。

閑話休題。実のところ、rate()関数というよりは、サーバー側のmetric初期化問題です。

さて、何らかのサーバーAがあったとして、それが更に他のサーバーBにRPCを送っているとします。サーバーBの方でホワイトボックスモニタリングをするのは当然として、サーバーA側でもサーバーBからのレスポンスを記録すると思います。というわけで、サーバーAでは以下のようなmetricsを吐くとします。

rpc_response{status=OK, service=B} 100

T0でこの状態だったとします。そしてT1(T0 + 1分)で 

rpc_response{status=OK, service=B} 160

となってた場合、

irate(rpc_response[5m])
=> {status=OK, service=B} 1

となります。そしてT2(T0+1分)でエラー発生、metrics

rpc_response{status=OK, service=B} 190
rpc_response{status=ERR, service=B} 60

となったとしたら

irate(rpc_response[5m])
=> {status=OK, service=B} 0.5

であり、ここにstatus=ERRが出てきません。これはirate()が最低でも2つのデータを必要とするからです。

じゃー、T3でどうなるかと。このエラーは突発的なものだったのでにstatus=ERRは変化がなかったとします。

rpc_response{status=OK, service=B} 250
rpc_response{status=ERR, service=B} 60

はいとりあえずT2、T3の2つのデータがあるのでirate()が計算できるわけですが…

irate(rpc_response[5m])
=> {status=OK, service=B} 0.5
   {status=ERR, service=B} 0

となります。status=ERRの方は値が0です。T2とT3の間では値が変わってないので仕方がないですね。

というわけで、irate() (rate())を使っている限り、このエラーはグラフにも表示されず、アラートも発生することはありません。

この問題の原因は「データ(カウンター)が存在しない != 値が 0」というところにあります。そこで、この問題の解決策は…

  1. もし取りうるstatusが最初から分かっているのなら、事前にrpc_responseカウンターを全てのstatusに対して0で初期化しておく。
  2. あきらめるw
  3. rate()の結果が存在しないけど、その元となるmetricが存在する場合には、その元となるmetricを直接使うようにPromQLをゴリゴリ書く
のいずれかです。番外として「Prometheusに手を入れて『rate()では、データが存在しない == 値が 0とみなす』」というのもありますが、これはprometheusの先祖であるborgmonがすでに通った茨の道、絶対におすすめしません。

なお、計算を簡単にするためにirate()を上では使ってましたが、「0で初期化されてないカウンター」問題はrate()でも一緒です。

ところで、bloggerのエディターはしょぼいですね。中の人も全然使ってないんじゃないのかな…

0 件のコメント:

prometheusのrate()関数の罠

 久しぶりのAdventカレンダー挑戦、うまくいく気がしません。 閑話休題。実のところ、rate()関数というよりは、サーバー側のmetric初期化問題です。 さて、何らかのサーバーAがあったとして、それが更に他のサーバーBにRPCを送っているとします。サーバーBの方でホワイトボ...