Slide 1

Slide 1 text

Prometheus Meetup Tokyo Takashi Kusumi Unit Testing for Prometheus Rules

Slide 2

Slide 2 text

Agenda ▶ Prometheus ルールの運⽤課題 ▶ promtool を使ったユニットテスト ▶ ユニットテストで学ぶ Prometheus 2

Slide 3

Slide 3 text

Prometheus ルールの運⽤課題

Slide 4

Slide 4 text

Prometheus ルールの運⽤課題 ▶ Prometheus ルール + Alert Rule と Recording Rule の2 種類 ▶ ルールを簡単に検証できない + 作成・変更が難しい + レビューが難しい + 学習しづらい ▶ 原因はメトリクスのデータを準備するのが難しいこと 4

Slide 5

Slide 5 text

Who will watch the watchmen? ▶ ルールの構⽂が誤っていると Prometheus が起動に失敗する ▶ CD で設定を配布するような場合は特に注意 ▶ 設定のリロードを使っていると、次の再起動時で失敗 5 level=error error loading config from \"/etc/prometheus/config/ prometheus.yml\": one or more errors occurred while applying the new configuration (--config.file=\"/etc/prometheus/config/prometheus.yml\")"

Slide 6

Slide 6 text

空⽩が含まれている !

Slide 7

Slide 7 text

Promtool is watching you 7

Slide 8

Slide 8 text

promtool を使ったユニットテスト

Slide 9

Slide 9 text

promtool とは ▶ Prometheus に付属する CLI ツール + Docker イメージ や homebrew パッケージにも含まれる ▶ サブコマンドとしてユニットテストが実装されている + promtool test rules + メトリクスデータが簡単に準備できる ▶ 設定・ルールファイルの構⽂チェックも便利 + promtool check rules 9

Slide 10

Slide 10 text

promtool test rules ... 10 $ promtool test rules myalert-test.yaml # シンプルに結果だけが表⽰される Unit Testing: myalert-test.yaml SUCCESS # テストが通ると戻り値が 0 になる $ echo $? 0

Slide 11

Slide 11 text

テスト失敗時 11 $ promtool test rules myalert-test.yaml Unit Testing: myalert-test.yaml FAILED: alertname:InstanceDown, time:5m0s, exp:"[Labels:{alertname=\"InstanceDown\", .. # 期待するアラート got:”[]" # 実際の値 (アラートがあがらなかった) $ echo $? # テストが失敗すると戻り値が 1 になる 1

Slide 12

Slide 12 text

公式ドキュメント 12 https://prometheus.io/docs/prometheus/latest/configuration/unit_testing_rules/

Slide 13

Slide 13 text

ユニットテストの書き⽅

Slide 14

Slide 14 text

テストするアラート 14 1 groups: 2 - name: example 3 rules: 4 # 5 分間 scrape 失敗が続くとアラートをあげる 5 - alert: InstanceDown 6 expr: up == 0 7 for: 5m 8 annotations: 9 summary: "Instance {{ $labels.instance }} down" 10 description: "{{ $labels.instance }} of job {{ $labels.job }} has been down for more than 5 minutes."

Slide 15

Slide 15 text

アラートはいつあがるか? 15 0m 1m 2m 3m 4m 5m 6m 7m 8m 9m 10m up 1 1 0 0 0 0 0 0 0 1 1 for: 5m InstanceDown アラートが pending になる InstanceDown アラートが発⽕ (firing) @2m @7m アラートが解消 @9m 経過時間

Slide 16

Slide 16 text

16 1 rule_files: 2 - myalert.yaml # ルールファイルを読みこむ 3 4 tests: # テストを記載していく 5 - interval: 1m # 値の間隔 (scrape_interval 相当) 6 input_series: # 想定する⼊⼒の timeseries を定義 7 - series: 'up{instance="myinstance", job="myjob"}' 8 values: 1 1 0 0 0 0 0 0 0 1 1 # メトリクスの値 9 10 alert_rule_test: 11 - eval_time: 7m # テストするタイミング 12 alertname: InstanceDown # 期待するアラート 13 exp_alerts: 14 - exp_labels: # 期待するアラートのラベル 15 instance: myinstance 16 job: myjob 17 exp_annotations: # 期待するアラートのアノテーション 18 summary: 'Instance myinstance down' 19 description: 'myinstance of job myjob has been down for more than 5 minutes.' ユニットテスト

Slide 17

Slide 17 text

アラートが上がらないことをテストする 1 alert_rule_test: 2 - eval_time: 6m 3 alertname: InstanceDown 4 exp_alerts: [] # 空でアラートがあがらないことを確認する 5 - eval_time: 7m 6 # 略: アラートがあがることを確認する 7 - eval_time: 9m 8 alertname: InstanceDown 9 exp_alerts: [] # アラートが解消したことを確認 17

Slide 18

Slide 18 text

Demo

Slide 19

Slide 19 text

値の省略記法

Slide 20

Slide 20 text

値の省略記法 20 values: 0 0 0 0 0 0 100 110 120 130 140 150 10 9 8 values: 0+0x5 100+10x5 10-1x2 初期値 加算値 回数 ※初期値も合わせると回数 + 1 の数列になる

Slide 21

Slide 21 text

省略記法の活⽤ ▶ アラートの for が⻑いとき + e.g. for: 1d ▶ range vector の範囲が広いとき + e.g. predict_linear(mymetric[4h], ) ▶ カウンタ値のテスト + e.g. sum(rate(mymetric[5m])) 21

Slide 22

Slide 22 text

PromQL の試し⽅

Slide 23

Slide 23 text

PromQL の試し⽅ 23 1 tests: 2 - interval: 1m 3 input_series: 4 # ⼊⼒値を定義 (省略) 5 promql_expr_test: 6 - eval_time: 10m 7 expr: 'sum(http_requests_total)' 8 exp_samples: 9 - value: 3000 Recording Rule も この⽅法でテストする

Slide 24

Slide 24 text

24 1 tests: 2 - interval: 1m 3 input_series: 4 - series: 'http_requests_total{path="/foo"}' 5 values: 0+100x10 6 - series: 'http_requests_total{path="/bar"}' 7 values: 0+200x10 8 promql_expr_test: # sum と sum_over_time の違いを試す例 9 - eval_time: 10m 10 expr: 'sum(http_requests_total)' 11 exp_samples: 12 - value: 3000 # 10分時点の値の合計 (1000 + 2000) 13 - eval_time: 10m 14 expr: 'sum_over_time(http_requests_total[10m])' 15 exp_samples: 16 - labels: '{path="/foo"}' 17 value: 5500 # 100 200 ... 1000 の合計 18 - labels: '{path="/bar"}' 19 value: 11000 # 200 400 ... 2000 の合計 PromQL の確認

Slide 25

Slide 25 text

Demo

Slide 26

Slide 26 text

Promtool Tips

Slide 27

Slide 27 text

まずは構⽂チェックから CI に組み込む ▶ まずは promtool check rules でルールの構⽂をチェックする + これだけで構⽂エラーで落ちる問題を防げる 27 $ promtool check rules myalert.yaml Checking myalert.yaml FAILED: # PromQL の構⽂が誤っている例 myalert.yaml: 7:11: group "example", rule 0, "InstanceDown": could not parse expression: 1:4: parse error: unexpected "="

Slide 28

Slide 28 text

promtool を Docker で実⾏する ▶ promtool は prom/prometheus イメージに含まれている ▶ entrypoint を変える必要がある ▶ nobody ユーザで動作するの -u root オプションが必要な場合も 28 $ docker run -u root -v "$PWD:/work" -w "/work" \ --entrypoint /bin/promtool prom/prometheus:v2.18.1 \ test rules *.yaml

Slide 29

Slide 29 text

ユニットテスト時のタイムスタンプ ▶ Unix エポック (1970/1/1 00:00:00) から始まる ▶ 時刻関連の関数を含むクエリでは意識する必要がある + UTC なことにも注意 29 1 tests: 2 - promql_expr_test: 3 - eval_time: 8760h # 1 年後 (24*365) 4 expr: 'year()' 5 exp_samples: 6 - value: 1971 # 1970 年の 1 年後

Slide 30

Slide 30 text

ユニットテストで学ぶ Prometheus

Slide 31

Slide 31 text

浮動⼩数点の精度

Slide 32

Slide 32 text

0.1 + 0.2 = ? 32 1 tests: 2 - promql_expr_test: 3 - expr: '0.1 + 0.2' 4 exp_samples: 5 - value: 0.3 FAILED: expr: "0.1 + 0.2", time: 0s, exp:"{} 3E-01" got:"{} 3.0000000000000004E-01"

Slide 33

Slide 33 text

不動⼩数点の精度 ▶ メトリクスの値は Go の float64 で保持される + 差分圧縮の効率が良い ▶ 0.1 + 0.2 = 0.30000000000000004 + 浮動⼩数点の精度による ▶ 実⽤上は問題ない精度 ▶ 演算結果を == で⽐較する場合は注意 33

Slide 34

Slide 34 text

Storing 16 Bytes at Scale (PromCon 2017) 34 https://promcon.io/2017-munich/talks/storing-16-bytes-at-scale/

Slide 35

Slide 35 text

Staleneess の仕組み

Slide 36

Slide 36 text

メトリクスはいつ取得できなくなる? (Staleness) 36 1 tests: 2 - interval: 1m 3 input_series: 4 - series: 'metric1' 5 values: 0 1 2 3 4 # 0〜4 分⽬のみ値がある 6 promql_expr_test: 7 - eval_time: 5m 8 expr: 'metric1' 9 exp_samples: [] # 5 分⽬は値が取得できない? ※ 通常は後述の Staleness Marker が最後に⼊る

Slide 37

Slide 37 text

直近 5 分のメトリクスは取得できる 37 FAILED: expr: "metric1", time: 5m0s, exp:"nil" got:"{__name__=\"metric1\"} 4E+00” 時間 0m 1m 2m 3m 4m 直近 5 分間は値が有効 ※1 最後の値が取れる ※1 —query.lookback-deltaで変更可能

Slide 38

Slide 38 text

Staleness Marker を追加 38 1 tests: 2 - interval: 1m 3 input_series: 4 - series: 'metric1' 5 values: 0 1 2 3 4 stale # staleness makrer 6 promql_expr_test: 7 - eval_time: 5m 8 expr: 'metric1' 9 exp_samples: [] # 今度はこれでテストが通る

Slide 39

Slide 39 text

Staleness Marker の役割 39 時間 0m 1m 2m 3m 4m 以降は値が取れない Staleness Marker (StaleNaN) ▶ Scrape の失敗や、Time Series が存在しなくなった場合に Staleness Marker という特別な値が⼊る ▶ 判定が遅れる問題に重複カウントされる問題が回避される

Slide 40

Slide 40 text

Staleness in Prometheus 2.0 (PromCon 2017) 40 https://promcon.io/2017-munich/talks/staleness-in-prometheus-2-0/

Slide 41

Slide 41 text

まとめ

Slide 42

Slide 42 text

まとめ ▶ Promtool を使えばルールの検証が簡単に⾏える + メトリクスデータの準備が簡単 + ルールの作成やレビューの効率が上がる ▶ ルールの構⽂チェックだけでも有⽤ ▶ PromQL を学ぶにも promtool は最適 42

Slide 43

Slide 43 text

ご清聴ありがとうございました