Prometheus メトリクス入門

## はじめに
こんにちは、Datadog Japan で Sales Engineer をしている [**AoTo**](https://qiita.com/AoTo0330) です。

この投稿は **[<font color="#6495ED">AoTo Advent Calendar 2023</font>](https://qiita.com/advent-calendar/2023/aoto) 18日目**の記事です。

SoundCloud によって2012年から始まった [**Prometheus**](https://prometheus.io/docs/introduction/overview/) ですが、現在では OSS の監視・通知ツールとして様々な機能を兼ね備えています。

そのため、今回は　Prometheus に入門ではなく、Prometheus のメトリクスのみに入門してみましょう。

## Prometheus のデータ形式
Prometheus コレクターのメトリクスは一般的に累積集計一時性(`cumulative`)を持ちます。[^1]これは、単調に増加する単一のカウンターを表す累積的なメトリクスで、再起動時に0にリセットされる特性を持ちます。こうしたメトリクスは [Counter 形式のメトリクス](https://prometheus.io/docs/concepts/metric_types/#counter)と呼ばれます。
[^1]: 「[Datadog って OpenTelemetry に対応しているの？](https://qiita.com/AoTo0330/items/b9a758555b7e049eca9f#%E3%83%87%E3%83%AB%E3%82%BF%E4%B8%80%E6%99%82%E6%80%A7%E3%83%A1%E3%83%88%E3%83%AA%E3%82%AF%E3%82%B9%E3%81%AE%E7%94%9F%E6%88%90)」でも詳しい解説をしています。

カウントが減少する可能性があったり、温度やリソースの使用量などの測定値を表す場合は、[Gauge 形式のメトリクス](https://prometheus.io/docs/concepts/metric_types/#gauge)を利用します。

この他に特徴的な形式として、[Histgram 形式のメトリクス](https://prometheus.io/docs/concepts/metric_types/#histogram)があります。この形式は、リクエストの持続時間や応答サイズなどの観測値をサンプリングし、設定可能なバケットでカウントと合計値を提供します。

そして最後に、Histgram 形式と似た [Summary 形式のメトリクス](https://prometheus.io/docs/concepts/metric_types/#summary)があります。これは、ベースのメトリクスの四分位数や合計値やカウントを記録します。

Histgram と Summary はどちらも単純な形式ではなく、元のメトリクスから計算された結果算出されるものですが、どちらもメトリクスの特性を表すために利用されます。これらの違いについては [Histgram and Summaries](https://prometheus.io/docs/practices/histograms/) のページで解説がありますが、対応するライブラリの制限があったり、計算を行う場所が異なる点が主な違いです。

Histgram 形式の場合、あらかじめサーバーで計算を行った上でメトリクスをエクスポートするため、アドホックな計算に時間がかかる場合があります。他にも違いがありますが、形式の特性を理解した上で利用することが重要となります。

## Prometheus のデータモデル
先ほど既にデータの形式については触れてしまいましたが、データモデルはこれらの全ての形式に共通するデータのスキーマです。

Prometheus は基本的に全てのデータを時系列で保存します。これはメトリクスデータの基本的な形式で、時系列データにラベル付けを行いこれによって同じメトリクスを複数の監視対象から収集し、区別することができます。

ラベル付けによってメトリクスが区別できるようにすることで、Prometheus は次元データモデルを利用して同一名のメトリクスを複数持つことができるようになります。

時系列データを形成する要素はサンプルと呼ばれ、`float64`形式のデータとミリ秒精度のタイムスタンプ([Unix タイムスタンプ](https://www.unixtimestamp.com/))のセットで記録されます。

これをメトリクス名とラベルのセットの表記法で、メトリクスを指定できます。この記法は [OpenTSDB](http://opentsdb.net/)と同一の表記法です。

```
<metric name>{<label name>=<label value>, ...}
```

## PromQL
これらのデータを収集しクエリする言語として、Prometheus は [**PromQL(Prometheus Query)**](https://prometheus.io/docs/prometheus/latest/querying/basics/)と呼ばれる独自の関数型クエリ言語を提供しています。このクエリ言語を利用して、ユーザ＝は表示したいデータの表示したい時間枠を指定してクエリを行い、グラフの表示や表形式での表示、[**Grafana**](https://grafana.com/) でのダッシュボードの作成に利用できます。

細かい仕様はさておき、クエリを行う際は前述の「メトリクス名とラベルのセットの表記法」でメトリクスをクエリすることができます。
```
<metric name>{<label name>=<label value>, ...}
```

ここに、[範囲ベクトルセレクター](https://prometheus.io/docs/prometheus/latest/querying/basics/#range-vector-selectors)(`[time]`)を加えることで、特定の範囲を指定してクエリを行えます。
```
<metric name>{<label name>=<label value>, ...}[time]
```

基本的なクエリはこれで理解ができたと思います。あとは[オフセット修飾子](https://prometheus.io/docs/prometheus/latest/querying/basics/#offset-modifier)を利用したり、[@ 修飾子](https://prometheus.io/docs/prometheus/latest/querying/basics/#modifier)を利用したり、[サブクエリ](https://prometheus.io/docs/prometheus/latest/querying/basics/#subquery)なども利用できます。

Prometheus では、有効でなくなった・削除された時系列データを扱うために [**Staleness(陳腐化)**](https://prometheus.io/docs/prometheus/latest/querying/basics/#staleness)という概念が使用されます。これにより、時系列にターゲットが存在しなくなったか、削除された場合に **stale** とマークされます。

タイムスタンプが含まれる時系列の場合、5分間サンプルが確認できなかった時にのみ**stale** とマークされます。

さらに PromQL を実行する際は、その対象のデータ量にも考慮する必要があります。大量のデータに対してクエリを行うことで、グラフ化のタイムアウトやサーバー・ブラウザへ高負荷がかかる可能性があります。

特に多くのラベルを持つメトリクスをクエリする際は、結果はシンプルでもクエリ対象のデータ量が多くなります。こうした問題を避けるために、表形式ビューでクエリを作成しながらデータポイントを数百程度に抑えてから、グラフモードに切り替えることが推奨されています。

## おわりに
Prometheus は Pull 型のアーキテクチャやサービスティスカバリにより大規模な環境の監視を容易にし、単純なバイナリと設定ファイルで動作する上さまざまなクライアントライブラリが用意されています。これに加えて、柔軟でチューリング完全なクエリ言語である PromQL を利用でき、データの保存もそれぞれのサーバーで自律的に行えるのが魅力です。

そして業界で標準的に利用されるようになり、様々な Exporter が用意されることで様々なメトリクスが同一の方法で取得し監視を行うことができるようになっています。

こうした魅力の根本にある「Prometheus 形式のメトリクス」に注目することで、Prometheus 全体のエコシステムの基盤を知ることができたように思います。