PrometheusのAgentモードを試してみた

## はじめに

新たに追加されたPrometheusのAgentモードを試してみました。
Agentモードに関して、Prometheus公式ブログの[Introducing Prometheus Agent Mode, an Efficient and Cloud-Native Way for Metric Forwarding](https://prometheus.io/blog/2021/11/16/agent/)がおそらく一番詳しい内容となっていますので、こちらも合わせてご参照いただくと良いかと思います。

## PrometheusのRemote Writeについて

Prometheusのローカルストレージには制限があり、スケーラビリティや長期の永続性が必要であれば、リモートストレージを利用する必要があります。
リモートストレージとのやりとりのためにPrometheusにはRemote Read/Writeというインターフェースがあります。
リモートストレージはRemote Read/Writeの両方の実装が必要なわけではありません。
それらに対応したリモートストレージとしてThanosやCortex、Google Cloud Managed Prometheus、Amazon Managed Service for Prometheusなどがあります。
Remote Storageについては少し昔の記事ですが@kkohtakaさんの[Prometheus の Remote Storage とは?](https://qiita.com/kkohtaka/items/48acd647155ee0602804)で紹介されているので、合わせてご参照いただくと良いかと思います。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/224576/613f2d3a-72cd-36a7-809d-d19e8ac0aa09.png)
画像: https://prometheus.io/docs/prometheus/latest/storage/#overview

## Remote Write利用時のPrometheus

前述の通りリモートストレージはRead/Writeの両方の実装が必要なわけではないため、例えばThanosであればRemote Writeのみが実装されています。
ではどうやって書き込まれたメトリクスデータを参照するかというと、PrometheusのQuery APIが実装されているThanos Queryと呼ばれるコンポーネントを通じて参照します。そのためThanosにはRemote ReadのAPIは実装されていなくとも、メトリクスデータをGrafanaなどからはThanosを通じて参照できるのです。

この場合、Prometheusはメトリクスのクエリーリクエストを受け付ける必要がなくなります。アラートも同様にThanosのコンポーネントで評価できるため、Prometheusでの評価は不要となります。またRemote Write利用時、そのデータはほぼほぼリアルタイムにThanosに対してメトリクスデータが送信されるため、PrometheusはStatelessアプリケーションのように扱うことが可能です。

上記のThanosのように、リモートストレージによってはそれを利用するユーザのPrometheusのクエリ機能やストレージの機能が不要となります。
おそらくThanos以外の他のリモートストレージも似たような状態になるのでしょう(かなり昔の記憶ですがCortexでもそうなったはず)。
Prometheusはそれなりにリソース消費が多くなりがちなので、今回のAgentモードはそういった利用用途での最適化です。
個人的に嬉しい機能追加でした :smile: 

## PrometheusのAgentモードとは？

AgentモードはPrometheusをターゲットのディスカバリ・メトリクスのスクレイプ・Remote Writeへ特化するPrometheusに組み込まれた新しい運用モードです。
Agentモードを有効化するとクエリーやアラート、ローカルストレージを無効化し、カスタマイズされたTSDB WALを利用します。
このカスタマイズされたTSDB WALではRemote Writeが成功するとそのデータを削除し、失敗時はリモートストレージが復帰するまでローカルストレージに一時的に保持する実装になっているようです(通常起動したPrometheusのバッファと同様に最大2時間までで、Agentモードでは今後この制限は今後なくなりそう？)。クエリ機能が無効化されるため、クエリのための完全なインデックスの構築も不要となり、そういった部分などでも最適化がされているようです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/224576/54cb4aa9-d796-aad7-bec8-abf811f5c5e2.png)
画像: https://prometheus.io/blog/2021/11/16/agent/

同様のことを実現できるものとして[Grafana Agent](https://github.com/grafana/agent)がありますが、そこで実装されたものを[Prometheusへ移植した]((https://grafana.com/blog/2021/11/16/why-we-created-a-prometheus-agent-mode-from-the-grafana-agent/))形になっているようです。
この機能のProposalは[こちら](https://docs.google.com/document/d/1cCcoFgjDFwU2n823tKuMvrIhzHty4UDyn0IcfUHiyyI/edit#heading=h.k685q93408c1)で、PRは[こちら](https://github.com/prometheus/prometheus/pull/8785)です。


## Agentモードで動かす

現時点(2021/12/1)で正式バージョンでAgentモードが動くものはまだリリースされていなかったため、`quay.io/prometheus/prometheus:v2.32.0-beta.0` で試しました。
PrometheusをAgentモードで動かすには `--enable-feature=agent` フラグを有効化することで利用できます。
スクレイピングのロジックやサービスディスカバリー、それらに関連するコンフィグなどは同様のようです。
しかしいくつかのAgentモードで利用しないPrometheusの設定が含まれていると起動しないようです。
今までの設定ファイルをそのまま利用しようとしたところ、いくつかAgentモードで利用しない設定項目が存在すると起動しないようです。
気づいたのものとしては以下の2つで、AlertやRuleに関する設定項目が存在するとエラーで起動しないようです(無視してほしいなぁ)。

```sh
ts=2021-12-01T09:23:35.070Z caller=main.go:195 level=info msg="Experimental agent mode enabled."
ts=2021-12-01T09:23:35.073Z caller=main.go:437 level=error msg="Error loading config (--config.file=/etc/prometheus/config/prometheus.yml)" err="field alerting is not allowed in agent mode"
```

```sh
ts=2021-12-01T09:25:06.150Z caller=main.go:195 level=info msg="Experimental agent mode enabled."
ts=2021-12-01T09:25:06.153Z caller=main.go:437 level=error msg="Error loading config (--config.file=/etc/prometheus/config/prometheus.yml)" err="field rule_files is not allowed in agent mode"
```

今回はKubernetes上で動作させ、以下の`args`の指定のみで特別Agentモードのその他のオプションは変更していません。

```
--config.file=/etc/prometheus/config/prometheus.yml
--web.external-url=XXXXXXXXXXXXXXXXXX
--web.enable-lifecycle
--web.enable-admin-api
--enable-feature=agent
```

ちなみにAgentモードが有効になったバージョンのprometheusバイナリのヘルプを確認すると、Agentモード専用のオプションには `Use with agent mode only.` と書かれ、今まで通りのモード(Serverモード)では、`Use with server mode only.` と書かれています。Agentモードではクエリーやアラートなどに関する機能がないため、そういったオプションがServerモードのみのオプションとなっているようです。それらが書かれていないフラグはAgentとServerの両方のモードで対応しているものです。

ServerモードとAgentモードでどの程度CPUとメモリの消費量が違うのか適当なクラスタでざっくりと比べてみました。
同じクラスタ上におおよそ同時に動かしはじめ、30分程度放置した結果が以下です。
計測開始時の`Number of Series`は126493で、そこからおそらく大きくは変化していないと思います。

**Serverモード**
![Serverモード](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/224576/ba048332-c1a9-c6d7-2dbb-f0387b3affae.png)

**Agentモード**
![Agentモード](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/224576/5de73d89-3806-c3fa-4999-80a9e9b88fb8.png)

AgentモードはServerモードと比べて確かにリソース消費量が抑えられており、特にメモリの消費量が小さくなっていました。
時系列データ数などの違いでどのようにこのリソース消費量に差が出てくるかまでは調べていませんが、少なくとも私の環境での最適化の効果は目に見えてありました :tada: 

## Prometheusの水平スケール

リモートストレージを利用せずにPrometheusを水平スケールしようと思うと、例えばrelabel_configでhashmodなどを利用しスクレイプターゲットを分散して、Promxyのような複数Prometheusのグローバルビューを用意すれば、水平スケールを実現できます。
しかしレプリカ数を減らす時、削除対象のPrometheusのローカルディスクのメトリクスデータをどうするか困るかと思います。
Agent モードでは ローカルディスクのメトリクスデータが参照されないため、PrometheusをStatelessなアプリケーションとして扱うことで、水平スケールも比較的簡単に実現できます。
(もちろんAgentモードでなくとも、単純にローカルのTSDBを参照しないようにPrometheusを利用すれば同じです)
Prometheusの水平スケールに関しては@watawuwu さんの[Prometheusでデータの水平分割を試みる](https://speakerdeck.com/watawuwu/lets-split-prometheus-data)でいくつか手法が紹介されていますので、合わせてご参照いただくと良いかもしれません。

## 最後に

PrometheusのAgentモードによりRemote Writeの最適化の効果が目に見えてありました。
このAgentモードの元となったGrafana Agentでは水平スケールのための設定があるようで、[全ノードにAgentを分散させる](https://grafana.com/docs/agent/latest/operation-guide/#host-filtering)ようなことも実現できそうです(試したことはないですが)。こういった機能も、もしかしたらGrafana Agentの成熟度をみてPrometheus側へ組み込まれていくかもしれません。
また[Prometheus OperatorでもAgentモードのサポート](https://github.com/prometheus-operator/prometheus-operator/issues/3989)が着々と進んでいるようです。
今後もPrometheusの進化が楽しみです！

## 参考
- [Introducing Prometheus Agent Mode, an Efficient and Cloud-Native Way for Metric Forwarding](https://prometheus.io/blog/2021/11/16/agent/)
- [Why we created a Prometheus Agent mode from the Grafana Agent](https://grafana.com/blog/2021/11/16/why-we-created-a-prometheus-agent-mode-from-the-grafana-agent/)