OpenTelemetry と Prometheus

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

この記事は **[<font color="#4F62AE">OpenTelemetry Advent Calendar 2023</font>](https://qiita.com/advent-calendar/2023/otel) 22日目**の記事です。

昨日は、[逆井（さかさい）](https://qiita.com/k6s4i53rx)さん([@k6s4i53rx](https://twitter.com/k6s4i53rx))の『[**OpenTelemetry Collector の Span Metrics Connector を使ってメトリクスを生成してみる**](https://zenn.dev/k6s4i53rx/articles/2023-advent-calender-otel)』でした！

昨日の記事の最後には…

> 明日は [@AoToLog_](https://twitter.com/AoToLog_) の 「OpenTelemetry と〇〇」です。〇〇 に何が入るかは 当日のお楽しみということで楽しみです！

ということで、実ははじめからこの〇〇に入れる内容は決めていました。**Prometheus** については[個人のアドベントカレンダー](https://qiita.com/advent-calendar/2023/aoto)で入門してきたので、今回は OpenTelemetry との関係性を深掘りしていきます。

https://qiita.com/AoTo0330/items/a1340d1b906bc39411bc

## どういう関係なの？
まずはそれぞれのプロジェクトの概要から押さえておきましょう。
両ページの What is 〇〇 の章から考察していきます。

### OpenTelemetry
OpenTelemetry は**トレース・メトリクス・ログ**などのテレメトリデータを**作成および管理**するために設計された **Observability フレームワーク及びツールキット**です。OTel はベンダーやツールに依存せず、Jaeger や **Prometheus** などのオープンソースツール・商用製品などあらゆる Observability バックエンドで使用できます。

2019年5月7日から [Cloud Native Computing Foundation(CNCF)](https://www.cncf.io/projects/opentelemetry/) に参加しており、2021年8月26日に Incubationg 成熟度レベルに移行しています。

![OTel_TopPage.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2363567/c19fd6c0-9866-85c2-0794-6129cb029291.jpeg)
「High-quality, ubiquitous, and portable telemetry to enable effective observability」

主なコンポーネントは以下の通りです。
 - すべてのコンポーネントの**仕様**
 - テレメトリ データの形式を定義する**標準プロトコル**
 - 一般的なテレメトリ データ型の標準命名スキームを定義する**セマンティック規則**
 - テレメトリ データの生成方法を定義する **API**
 - 仕様、API、テレメトリ データのエクスポートを実装する**言語 SDK**
 - 共通のライブラリとフレームワークのインストルメンテーションを実装する**ライブラリエコシステム**
 - コード変更を必要とせずにテレメトリ データを生成する**自動計測コンポーネント**
 - OTel Collector、テレメトリ データを受信、処理、エクスポートする**プロキシ**
 - Kubernetes 用の OTel Operator、 OTel Helm Charts、 FaaS 用のコミュニティ アセットなど、**その他のさまざまなツール**

### Prometheus
Prometheus は元々 SoundCloud が構築したもので、2012年に開始され現在はスタンドアロンのオープンソースプロジェクトです。**メトリクス**をラベルと呼ばれるオプションのキーと値のペアと共に、記録されたタイムスタンプと合わせて、時系列データとして**収集して保存**します。

2016年5月9日に [Cloud Native Computing Foundation(CNCF)](https://www.cncf.io/projects/prometheus/) に参加しており、2018年8月9日に Graduated 成熟度モデルに到達しています。

![Prometheus_TopPage.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2363567/028c037a-8f89-8fe9-32c7-3b1f0d8cffe0.jpeg)
「From metrics to insight」

主なコンポーネントは以下の通りです。
 - 時系列データを収集して保存するメインのPrometheus **サーバー**
 - アプリケーション コードをインストルメントするための**クライアントライブラリ**
 - 短期間のジョブをサポートするための**プッシュゲートウェイ**
 - HAProxy、StatsD、Graphite などのサービスの**専用エクスポーター**
 - アラートを処理する**アラートマネージャー**
 - **各種サポートツール**

### 関係性(relationship)
互いに [Cloud Native Computing Foundation(CNCF) プロジェクト](https://www.cncf.io/projects/) の1つであり、Prometheus の方が先に参加し現在は卒業しており、つまり**先輩**です。

お互いの概要ページにはそれぞれのプロジェクトを説明する内容が書かれていますが、OpenTelemetry のページには Prometheus が登場し逆のページには何もなく、つまり OTel の**片思い**です。

以上をまとめると両者の関係性は、<INS>OpenTelemetry は卒業した憧れの Prometheus を一方的に慕っている後輩</INS>だと言うことがわかります。[^1]
[^1]:この場合、Jaeger も同様の立場となります。三角関係…

### 違い(difference)
冗談はこのくらいにして、両者には根本的な違いが何点かあります。

 - 記録する対象のテレメトリデータ
 - 収集する先のバックエンドと独立性

OpenTelemetry は**トレース・メトリクス・ログ**の3つを主なテレメトリとして対象としている反面、Prometheus は**メトリクスだけ**にフォーカスをしたプロジェクトです。OpenTelemetry vs Prometheus のように語られることもありますが、両者は根本的に性質の異なるにて非なるプロジェクトであることを押さえておきましょう。

さらに、OpenTelemetry は [Observability のバックエンドではない](https://opentelemetry.io/docs/what-is-opentelemetry/#what-opentelemetry-is-not)ことを明言しており、Prometheus を含む既存のバックエンドのデータの保存と可視化を委任しています。これが OpenTelemetry が Prometheus と並行して利用される理由です。

## OpenTelemetry Collector
ここからは OpenTelemetry のエコシステムを中心に、Prometheus への統合を見ていきましょう。OpenTelemetry はベンダーに依存しない形でデータの受信・処理・送信を行うため OpenTelemetry Collector を提供しています。

![OTel_Collector.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2363567/fd2cce40-9701-3425-8113-b124463ff7c3.png)

ここで見られるように、Prometheus から OTel Collector へ、OTel Collector から Prometheus へデータを受け渡すことができるようになっています。これは、前述の通り「多次元データモデルのメトリクスを生成する Prometheus」と「分散ストレージに依存しない自律的なバックエンドの Prometheus」の両面として、OpenTelemetry と互いの長所を活かしながらツールの活用ができるということです。

OTel Collector は [Receiver](https://opentelemetry.io/docs/collector/configuration/#receivers), [Processor](https://opentelemetry.io/docs/collector/configuration/#processors), [Exporter](https://opentelemetry.io/docs/collector/configuration/#exporters), [Connector](https://opentelemetry.io/docs/collector/configuration/#connectors) の4つの分類の構成からなります。それぞれの構成について、<INS>Prometheus とそのメトリクスに関連してできること</INS>を確認していきましょう。

### Receiver
Receiver は1つ以上のソースからテレメトリをプッシュ・プル形式で収集する OTel Collector のコンポーネントです。

![Prometheus_Receiver.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2363567/efd4b58d-901d-bb2b-8499-e45fec609c7c.jpeg)

[**Prometheus Receiver**](https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/main/receiver/prometheusreceiver/README.md) は Prometheus 形式でメトリクスデータを収集できます。Prometheus の完全互換を目指していますが、現在でも一部の機能をサポートしていません。[^2]
[^2]: `alert_config.alertmanagers`, `alert_config.relabel_configs`, `remote_read`, `remote_write`, `rule_files` をサポートしておらず、Receiver にこれらの設定を含めるとエラーを返します。

Prometheus 形式の一般的な構成の通り、**Prometheus Receiver** はプル型でメトリクスをターゲットにスクレイピングします。**Prometheus Reviever** の `yaml` ファイルは `receivers: prometheus: config` で構成することができ、[GitHub](https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/main/receiver/prometheusreceiver/README.md) では一例として以下のサンプル構成ファイルが記載されています。

```config.yaml
receivers:
    prometheus:
      config:
        scrape_configs:
          - job_name: 'otel-collector'
            scrape_interval: 5s
            static_configs:
              - targets: ['0.0.0.0:8888']
```

この構成は、5秒毎に　`otel-collector` ジョブを実行し `0.0.0.0:8888` に対しメトリクスの取得を行います。この他にも Prometheus でのメトリクス収集と同様に、Kubernetes のリソースメトリクスをラベルを付与しながら収集することも可能です。

**Prometheus Receiver** の利点は、従来 Prometheus で行ってきた高機能なメトリクス生成・収集を変更することなく、ログ・トレースを受信・処理・送信できる OTel Collector に統合できることです。これにより、OTel がサポートしていないメトリクス収集を Prometheus のエコシステムの恩恵を受けながら補完することができます。

昨年の [<font color="#4F62AE">OpenTelemetry Advent Calendar 2022</font>](https://qiita.com/advent-calendar/2022/opentelemetry) でも解説されている記事があるので、是非ご参照ください🔭

https://qiita.com/symmr/items/148db3e2f0776ecc3d04

### Processor
Processor は Receiver によって収集されたデータを取得し、Exporter へ送信する前に処理を行いデータを変換する OTel Collector のコンポーネントです。

![Span_Metrics_Processor.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2363567/cdafd2d4-deb3-0518-a9b9-6a53b65d9ae7.jpeg)

**Span Metrics Processor** は OpenTelemetry によって計装されたアプリケーションのトレース内の処理単位であるスパンをもとに、R.E.D メトリクスを生成します。このメトリクスの形式として、Prometheus 形式を選択できます。

:::note warn
このコンポーネントは [Processor から Connector を利用する変更](https://github.com/open-telemetry/opentelemetry-collector-contrib/issues/403)により、現在**非推奨**です。スパンからメトリクスを生成する場合は、後述する **Span Metrics Connector** をご利用ください、
:::

[**Span Metrics Processor**](https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/main/processor/spanmetricsprocessor/README.md) によって生成されるメトリクスは、少なくとも以下の次元(dimentions)を持ちます。

 - Service name
 - Operation
 - Span kind
 - Status code

これと同時に、元のトレースを処理せずにパイプラインに流すことができます。これらの構成は以下のように記載可能です。

```config.yaml
processors:
  batch:
  spanmetrics:
    metrics_exporter: otlp/spanmetrics
    latency_histogram_buckets: [100us, 1ms, 2ms, 6ms, 10ms, 100ms, 250ms]
    dimensions:
      - name: http.method
        default: GET
      - name: http.status_code
    dimensions_cache_size: 1000
    aggregation_temporality: "AGGREGATION_TEMPORALITY_CUMULATIVE"     
    metrics_flush_interval: 15s
```

### Exporter
Exporter は1つ以上のバックエンドにデータをプッシュ・プル形式で送信するコンポーネントです。

![Prometheus_Exporter.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2363567/d08921f8-130b-d022-90f8-22f7edba34fe.jpeg)

[**Prometheus Exporter**](https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/exporter/prometheusexporter) は Prometheus バックエンドに Prometheus 形式でデータを送信するために、スクレイピングのエンドポイントを公開します。このエンドポイントには Prometheus サーバー以外にもアクセスでき、メトリクスを問い合わせて取得できます。

```config.yaml
exporters:
  prometheus:
    endpoint: "1.2.3.4:1234"
    tls:
      ca_file: "/path/to/ca.pem"
      cert_file: "/path/to/cert.pem"
      key_file: "/path/to/key.pem"
    namespace: test-space
    const_labels:
      label1: value1
      "another label": spaced value
    send_timestamps: true
    metric_expiration: 180m
    enable_open_metrics: true
    add_metric_suffixes: false
    resource_to_telemetry_conversion:
      enabled: true
```

**Prometheus Exporter** は主要な Exporter として、OpenTelemetry の公式ドキュメントにもその仕様について説明があります。

https://opentelemetry.io/docs/specs/otel/metrics/sdk_exporters/prometheus/

細かいですが、メトリクスに付与するリソース属性の仕様はデフォルトでは設定されないため、必要な際は構成に追加する必要があります。

### Connector
Connector は2つのパイプラインを結合し、それぞれの Exporter, Receiver としての機能を持ちます。Connector で扱うデータ型は同一のままでも、変換することも可能なため、データの要約・変換・複製・ルーティングに使用できます。

Connector は OTel Collector における[新しいアーキテクチャ](https://opentelemetry.io/docs/collector/building/connector/#new-architecture-using-a-connector)として誕生しました。OTel Collector のパイプラインは1種類のデータのみを対象とするにも関わらず、従来の Processor のうちデータ型を変換するものは変換後のデータを直接エクスポートしていました。

このような場合に、本来の OTel Collector のパイプラインと概念に合致するように2種類のデータパイプラインを接続するようなアーキテクチャを実現するコンポーネントとして Connector が誕生し、前述のデータ型を変換する Processor は非推奨となりました。

![Span_Metrics_Connector.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2363567/15a9b3a5-e348-5784-0857-9c99927004ae.jpeg)

[Span Metric Connector](https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/main/connector/spanmetricsconnector/README.md) は OTel を始めとするトレースからメトリクスを生成出来ます。

詳しい解説は、ちょうど前日の素晴らしい記事でまとめられています🔭

https://zenn.dev/k6s4i53rx/articles/2023-advent-calender-otel

## おわりに
OpenTelemetry のコンポーネントから各1つずつ取り上げていきましたが、この他にも Prometheus 形式でのメトリクスを扱う技術は OTel Collector の中に含まれています。

是非、OpenTelemetry × Prometheus で Cloud Native な Observability を実践してみてください🐶