バッチは止まったのにアラートが勝手に解消した
目標
時刻を値として持つ指標とtimestamp()の違い、staleness markerとlookback、*_over_time・サブクエリ・derivを、固定された240分の時系列の上で評価して数字で確認し、ターゲットが消えても解除されないアラートルールを作ります。
なぜ重要なのか
バッチ処理の鮮度アラートは、たいてい最大の事故で沈黙します。Podが消えると時系列も消え、消えた時系列にかかった条件式は、真でも偽でもない空の結果になるからです。PromQLがいつサンプルを見て、いつ見ないのか(lookback、staleness)、瞬間値と時間集計が何を隠すのかを知ってこそ、アラートが静かな理由を信じられます。
用意されている環境
python3 /opt/fixtures/pca_staleness_lab.py initが、/root/pca-staleness/README.txtに時系列のストーリーを置きます。python3 /opt/fixtures/pca_staleness_lab.py seriesで、入力の表記を見られます。時刻は0分から240分まで1分間隔で、promtoolのテスト時刻なので、time()は評価時刻の秒数です(150mなら9000)。
python3 /opt/fixtures/pca_staleness_lab.py eval -f 파일 --at 150m(プレースホルダーはファイル名です)は、lab-k8sのpromtool 3.0.1エンジンで評価します。採点ツールは、同じ時系列であなたの式と基準の式を再計算し、最後のステップでは、あなたのルールファイルを、採点ツールが作成したルールテストにかけます。
ステップ
/root/pca-staleness/01-age.promqlに、nightly-exportの最後の成功から経過した秒数を、time()とbatch_last_success_timestamp_secondsで書きます。python3 /opt/fixtures/pca_staleness_lab.py eval -f /root/pca-staleness/01-age.promql --at 150mの結果を、/root/pca-staleness/01-age.txtにage_seconds=として書いてください。材料は、python3 /opt/fixtures/pca_staleness_lab.py initが作ります。/root/pca-staleness/02-wrong.promqlにtime() - timestamp(batch_last_success_timestamp_seconds{job="nightly-export"})を書いて、150mで評価します。/root/pca-staleness/02-trap.txtに、wrong_age_seconds=(この式の結果)、sample_timestamp=(timestamp()の結果)、value=(指標の値)を書いてください。- 2つのjobの
batch_last_success_timestamp_secondsを178mから1分ずつ評価して、結果が出る最後の分を探してください。/root/pca-staleness/03-lookback.txtに、nightly_last_minute=(staleness markerが入ったjob)とfederated_last_minute=(markerなしでサンプルだけが途切れたjob)を、整数で書きます。 time() - batch_last_success_timestamp_seconds{job="nightly-export"} > 3600を170mと200mで評価して、結果の時系列の数を、/root/pca-staleness/04-alert.txtのnaive_series_170m=、naive_series_200m=に書きます。次に、/root/pca-staleness/04-fixed.promqlに、ターゲットが消えたあとでも結果を返す式を書きます。採点ツールは、60m・125mでは空の結果、135m・170m・182m・200m・235mでは結果があるかを確認します。/root/pca-staleness/05-availability.promqlに、job="api"の直近30分の可用率を、avg_over_timeで書きます。120mで評価して、/root/pca-staleness/05-availability.txtに、availability_30m=、instant_up=(同じ時刻のup{job="api"})、min_up_30m=(min_over_timeの結果)を書いてください。/root/pca-staleness/06-slope.promqlに、queue_depth{queue="exports"}の10分の傾き(毎秒)を、derivで書きます。/root/pca-staleness/06-slope.txtに、deriv_110m=(110mの結果)、rate_125m=(125mでのrate(queue_depth[10m]))、deriv_125m=(125mでのderiv)を書いてください。120mに、キューのほとんどが処理されました。/root/pca-staleness/07-peak.promqlに、直近1時間にわたって1分間隔で見たrate(batch_rows_processed_total[5m])の最大値を、サブクエリで書きます。80mで評価して、/root/pca-staleness/07-peak.txtに、peak_rows_per_sec=とhour_avg_rows_per_sec=(rate(...[1h])の結果)を書いてください。/root/pca-staleness/08-rules.ymlに、グループ1つとアラートBatchExportStaleを書きます。exprは、ステップ4のようにターゲットが消えても結果が出る式、for: 10m、labels.severity: ticketです。promtool check rulesで文法を確認してください。採点ツールは、同じ時系列でルールテストを作成して、60m・125m・135m(待機中)には発火したアラートがなく、145m・150m・200m・235mには、job="nightly-export"、severity="ticket"のアラートが発火中かを確認します。
参考
- 範囲の選択は、左側が開いています。
[5m]は、評価時刻の5分前のサンプルを含みません。 absent(v)は、vがないとき値が1の時系列を1つ返し、あれば空の結果です。- よくある間違い: 経過時間を
timestamp()で測ること、ゲージにrateをかけること、up == 0だけで消えたターゲットを捉えようとすることです。 - この時系列は、promtoolのテスト用の擬似的な資料です。実際のサーバーのスクレイプ遅延・リトライは再現しません。
- Querying basics — Staleness ・ Functions ・ Unit testing for rules
最後の成功は何秒前か
/root/pca-staleness/01-age.promqlに、nightly-exportの最後の成功から経過した秒数を、time()とbatch_last_success_timestamp_secondsで書きます。python3 /opt/fixtures/pca_staleness_lab.py eval -f /root/pca-staleness/01-age.promql --at 150mの結果を、/root/pca-staleness/01-age.txtにage_seconds=として書いてください。材料は、python3 /opt/fixtures/pca_staleness_lab.py initが作ります。
指標の値そのものが、Unix時刻(秒)です。評価時刻からその値を引けば、経過時間になります。
timestamp()で測るといつも新鮮
/root/pca-staleness/02-wrong.promqlにtime() - timestamp(batch_last_success_timestamp_seconds{job="nightly-export"})を書いて、150mで評価します。/root/pca-staleness/02-trap.txtに、wrong_age_seconds=(この式の結果)、sample_timestamp=(timestamp()の結果)、value=(指標の値)を書いてください。
timestamp()は、サンプルをスクレイプした時刻です。1分ごとにスクレイプしている間、その時刻はずっと新しくなり続けます。
消えた時系列はいつまで見えるか
2つのjobのbatch_last_success_timestamp_secondsを178mから1分ずつ評価して、結果が出る最後の分を探してください。/root/pca-staleness/03-lookback.txtに、nightly_last_minute=(staleness markerが入ったjob)とfederated_last_minute=(markerなしでサンプルだけが途切れたjob)を、整数で書きます。
ターゲットが消えると、Prometheusはstaleness markerを入れて、すぐに消します。markerがなければ、lookback(既定5分)の間、最後のサンプルを返し続けます。範囲の境界が含まれるかどうかも、確認してください。
ターゲットが消えたらアラートが解除された
time() - batch_last_success_timestamp_seconds{job="nightly-export"} > 3600を170mと200mで評価して、結果の時系列の数を、/root/pca-staleness/04-alert.txtのnaive_series_170m=、naive_series_200m=に書きます。次に、/root/pca-staleness/04-fixed.promqlに、ターゲットが消えたあとでも結果を返す式を書きます。採点ツールは、60m・125mでは空の結果、135m・170m・182m・200m・235mでは結果があるかを確認します。
消えた時系列には比較する値がないので、条件式が空の結果になります。「ない」をシグナルに変える関数を、orで付けてください。
今はup=1、30分の可用率は
/root/pca-staleness/05-availability.promqlに、job="api"の直近30分の可用率を、avg_over_timeで書きます。120mで評価して、/root/pca-staleness/05-availability.txtに、availability_30m=、instant_up=(同じ時刻のup{job="api"})、min_up_30m=(min_over_timeの結果)を書いてください。
0と1のゲージの時間平均は、1だったサンプルの割合です。瞬間値は、ウィンドウ内の障害を見せてくれません。
ゲージにrateをかけると
/root/pca-staleness/06-slope.promqlに、queue_depth{queue="exports"}の10分の傾き(毎秒)を、derivで書きます。/root/pca-staleness/06-slope.txtに、deriv_110m=(110mの結果)、rate_125m=(125mでのrate(queue_depth[10m]))、deriv_125m=(125mでのderiv)を書いてください。120mに、キューのほとんどが処理されました。
rateは、値が減ると、カウンターが再起動したとみなして補正します。ゲージの減少は、再起動ではなく実際の変化です。
1時間の平均に隠れた最高スループット
/root/pca-staleness/07-peak.promqlに、直近1時間にわたって1分間隔で見たrate(batch_rows_processed_total[5m])の最大値を、サブクエリで書きます。80mで評価して、/root/pca-staleness/07-peak.txtに、peak_rows_per_sec=とhour_avg_rows_per_sec=(rate(...[1h])の結果)を書いてください。
식[범위:간격](プレースホルダーは式、範囲、間隔です)は、内側の式を間隔ごとに評価し直して、レンジベクトルを作ります。その上に*_over_timeをかけられます。
消えても鳴り続けるアラートルール
/root/pca-staleness/08-rules.ymlに、グループ1つとアラートBatchExportStaleを書きます。exprは、ステップ4のようにターゲットが消えても結果が出る式、for: 10m、labels.severity: ticketです。promtool check rulesで文法を確認してください。採点ツールは、同じ時系列でルールテストを作成して、60m・125m・135m(待機中)には発火したアラートがなく、145m・150m・200m・235mには、job="nightly-export"、severity="ticket"のアラートが発火中かを確認します。
absent()は、等号マッチャーのラベルを結果に付けます。2つの系統のラベルが同じなら、アラートが途切れずに続きます。forは、発火前の待機時間です。