TT Lab
はじめる
学ぶ 学習パス コース

PCA — Prometheus認定アソシエイト

正常データに隠されたカウンターリセット

TT Labで続きを見る

一言でいうと

ルールファイルの文法が正しいことと、そのルールが正しい数字を作ることは、別の主張です。counterを先に合算すると、個々のプロセスのリセットが隠れてしまうことがあります。正常なデータだけのテストは、まさにその誤ったルールも通してしまいます。この単元では、本物のPromQLエンジンに反例を入れて2つの主張を区別し、受講生がレコーディングルールとテストファイルを自分で直します。

なぜ必要なのか

決済サービスのリクエスト率のダッシュボードを直した日を考えてみましょう。レビューではpromtool check rulesが成功し、普段のトラフィックのグラフもなめらかでした。ところが、インスタンスの1つが再起動された時間帯にだけ、ダッシュボードのリクエスト率が下がりました。文法のエラーではなく、計算順序のエラーだったので、パーサーには見つけられなかったのです。集計された数値はもっともらしく、アラートも静かなので、人はかえって安心しやすくなります。

テストは、名前にtestが入るコマンドを一度実行する儀式ではありません。どんな入力でどんなラベルと値を期待するのかを明示し、その期待を壊す実装を拒否する仕組みです。誤った実装を入れても緑のランプが点くなら、テストに抜けている状況があるということです。本番ですべての障害を発見してからでないとテストを追加できない、ということはありません。ルールが隠しやすい情報を先に見つけて、小さな時系列としてモデリングできます。

どう動くのか

ラボの入力には、routeとinstanceの2つのラベルがあります。/checkoutのaは1分ごとに60、bは1分ごとに600ずつ、累積のcounterが増えます。したがって、十分な窓では、aは毎秒1、bは毎秒10です。/statusには、1分あたり60ずつ増える2つのインスタンスを別に置きます。この対照用のrouteがあってこそ、routeまで消した集計をテストが拒否できます。/checkoutだけを入れると、ラベルを誤って消した結果も、数字だけを見ても気づきにくくなります。

正しいrecording ruleは、元の時系列ごとにrateを計算してから、routeごとに合計します。比較用のルールは、元のcounterをrouteごとに足した時系列を先に記録し、その合計にrateを適用します。正常な入力の6分の時点では、2つのルールとも/checkoutで11を返します。ここまでしかテストしないと、どちらの実装が間違っているかを判断する根拠がありません。

反例では、aが0、60、120、180、0、60、120と変化します。bは増え続けます。aのリセットが起きた瞬間にも、bの増加量のほうが大きいので、2つの値の合計は減りません。元の時系列を見られるrateは、aのリセットを考慮しますが、すでに合計だけを受け取ったrateは、その事実を復元できません。合算の過程で消えた情報は、あとから関数の名前を変えても戻ってこない、というのが核心です。

固定した1分の入力・評価間隔、6分の評価、5分の窓で、実際のPrometheus 3.14.0の結果は、正しい/checkoutのリクエスト率が10.75、誤ったリクエスト率が10です。元のresetsのrouteごとの合計は1ですが、記録した合計のresetsは0です。/statusは相変わらず2です。この4つの結果を一緒に検査すれば、単に定数の10.75を返したり、routeを消したりする修正も、受け入れなくなります。

ここでの10.75は、イベント元帳に記録されたすべてのリクエストを秒単位で数えたという意味ではありません。rateは、範囲内のサンプルと、境界の外挿を使います。評価時刻や窓、入力間隔を変えると、結果も変わることがあります。そのため、テストファイルには、入力の値だけでなく、interval、evaluation_interval、eval_timeも一緒に書きます。時間の条件を省いて数字だけを暗記すると、次の事例を説明できません。

テストファイルを読む順序

まず、rule_filesが何を読むのかを見ます。ラボのヘルパーは、学習者のステップごとのルールを、一時フォルダーのrules.jsonにコピーします。拡張子はJSONですが、Prometheusが受け取るYAMLの表現として有効です。そのあと、入力の時系列のラベルと値、評価間隔、検査時刻、期待するサンプルを読みます。配列の順序は変えてもかまいませんが、必要な時系列や期待サンプルを抜くことは、別のテストを作ることです。

それぞれのpromql_expr_testには、expr、eval_time、exp_samplesがあります。exp_samplesのlabelsには、数字の持ち主が誰なのかを書き、valueには期待値を書きます。数字だけが一致する確認では不十分です。routeラベルが消えたのに合計だけ同じという状態も、誤った結果になりえます。リセットのテストの正解を10に下げると、誤ったルールが通りますが、それは実装を直したのではなく、要件を変えたのです。この演習の入力と期待値の契約は、そのような緩和を拒否します。

ラボは、同じ結果を出す式を、文字列が違うという理由では拒否しません。たとえば、この入力のラベルがrouteとinstanceだけのとき、sum without(instance)は、sum by(route)と同じ結果を出すことがあります。だからといって、他のラベルが追加されたすべての本番の入力でも、2つの式が同じという意味ではありません。テストが保証する範囲は、テストに入れた入力の範囲です。

現場での姿

デプロイパイプラインには、文法チェックと意味のテストを両方置きます。文法チェックは、誤った関数呼び出し、フィールドや式を早く知らせ、意味のテストは、サービスが約束したラベル・値・時間の関係を確認します。レビューには、正常な入力を1つだけ付けるのではなく、再起動、routeの分離、リクエストなし、データの欠損のように、計算を揺さぶりうる事例を付けます。変更したルールの結果だけでなく、既存の重要な事例が維持されているかも見ます。数字を直したら、もともとのrouteの分離が壊れるというリグレッションがありうるからです。

また、一度は意図的に誤った実装を入れてみてください。今回の実験では、正常なデータだけを残したテストと、空のtests配列が、誤った集計ルールも実際に通してしまいました。ツールが成功で終了したことは、実行するよう求められた検査で問題がなかったという意味であり、必要な検査をすべて行ったという保証ではありません。何を検査したのかをあわせてレビューする習慣は、デプロイゲート全般にそのまま当てはまります。

続けて学ぶこと

次のレッスンでは、欠損とアラートの時間軸を先に読みます。そのあとのラボでは、同じ誤ったルールが、正常のテストでは通り、リセットのテストでは失敗する出力を読みます。 続いて、rate-rules.ymlのレコーディングルールを直し、reset-tests.ymlの誤った期待値を修正します。 ヘルパーは、学習者のファイルの一時コピーで、実際のpromtoolを実行し、文法と意味の結果を分けて残します。 ホストや本番のモニタリングは変更せず、入力は学習用の合成時系列です。

公式ドキュメント