OpenTelemetry CollectorでPrometheus Remote Storageへ直接メトリクスを書き込む

# OpenTelemetryとは

![](https://www.appdynamics.com/wp-content/uploads/2020/10/17869_AWS_announce_OT_img1_C2.jpg)
> 画像引用元: https://www.appdynamics.com/blog/product/what-is-opentelemetry/

[OpenTelemetry](https://opentelemetry.io/)は[OpenTracing](https://opentracing.io/)と[OpenCensus](https://opencensus.io/)がマージされできたプロジェクトでこれら二つの次のメジャーバージョンとして提供されます。
トレース、メトリクス、ログのようなテレメトリーデータの作成・管理をするためにデザインされたAPI・SDK・ツールのセットを提供され、OpenTelemetryを利用することでテレメトリデータを計測・生成・収集・エクスポートができるようになります。これ自体はストレージやビュー等を提供するものではないため、トレーシングであればJaegerやZipkin、メトリクスであればPrometheusなどオープンソースまたはクラウドプロバイダにより提供されている商用のバックエンドに対してテレメトリデータをエクスポートすることになります。

OpenTelemetryを用いてアプリケーションからテレメトリデータを収集する場合、言語毎に実装されたAPI・SDKを用いて直接インストルメンテーションしたり、Prometheusのexporterのようにアプリケーションの外部からテレメトリデータを収集し、直接バックエンドに対してエクスポートするかまたは後述のOpenTelemetry Collectorに対してテレメトリデータをエクスポートすることになるかと思います。

現在(2020/11/25時点)で多くのコンポーネントがすでにbetaの状態でGAに向けて作業中のようです。ログに関しては初期の段階ではサポートしていませんが、後ほど組み込むことを目指しているようです。

## OpenTelemetry Collector

[OpenTelemetry Collector](https://opentelemetry.io/docs/collector/)はOpenCensusの[OpenCensus Service](https://opencensus.io/service/)のような構成を一つのコンポーネントで実現できるものとして理解しています。OpenTelemetry CollectorはOpenTelemetryで提供されているものの一つでこれ自体が単体のアプリケーションとして起動するものであり、テレメトリデータを受信・処理・エクスポートする方法をベンダーに依存しない実装で提供します。

OpenTelemetry Collectorでは[レシーバー](https://github.com/open-telemetry/opentelemetry-collector/tree/master/receiver)で実装され対応しているフォーマットのテレメトリデータ(PrometheusやJaegerなど)を受け取り、[エクスポーター](https://github.com/open-telemetry/opentelemetry-collector/tree/master/exporter)で実装されているバックエンドに対してテレメトリデータをエクスポートするためのものです。


受信したテレメトリデータのためのバッチ処理、リトライ、フィルター、加工などが可能であり、アプリケーション側からこれらの処理をCollectorへオフロードできます。

> ![スクリーンショット 2020-12-01 14.50.33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/224576/df12f892-45d0-fee8-9a7f-95862f79bbe6.png)
> 画像引用元: https://kccncna20.sched.com/event/eoEq

また複数のバックエンドに出力することが可能で、これにより様々なバックエンドのためのエージェント・コレクターをOpenTelemetry Collectorへまとめることが可能です。OpenTelemetryで提供されている特定の言語向けSDKでは実装されておらずエクスポートできないモニタリングサービスバックエンドがあった場合でも、Collector側のエクスポーターで実装されていればCollectorを介してエクスポートできます。OpenTelemetry Collectorから別のOpenTelemetry Collectorへエクスポートも可能であり、アプリケーションの近く(例えばPodのサイドカーや同じノード上)に起動して、さらに別のCollectorで集約するといった構成も可能です。

> ![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/224576/6172c27b-f190-9fed-4f71-393218efad34.png "https://kccncna20.sched.com/event/eoEq")
> 画像引用元: https://kccncna20.sched.com/event/eoEq

OpenTelemetry Collectorとしては以下の2種類のものが公開されています。

- [opentelemetry-collector](https://github.com/open-telemetry/opentelemetry-collector)
  - コアリポジトリであり主要な機能とメジャーなオープンソースのレシーバーとエクスポーターを含むもの
- [opentelemetry-collector-contrib](https://github.com/open-telemetry/opentelemetry-collector-contrib)
  - opentelemetry-collectorのスーパーセットであり主要な機能でないものや、その他のオープンソースや商用のバックエンドのための実装を含むもの

Collectorは自作することも可能のようで、社内に独自のモニタリングサービスがありそれらに対応したCollectorのディストリビューションを開発したい場合には以下が参考になるかもしれません。私も時間を見つけて遊んでみようと思います。

- [Building your own OpenTelemetry Collector distribution](https://medium.com/p/42337e994b63)
- [OpenTelemetry Collector Builder](https://github.com/observatorium/opentelemetry-collector-builder)

# OpenTelemetry Collectorを試す

OpenTelemetry Collectorを利用してKubernetes上に稼働しているアプリケーションが出力するPrometheus形式で出力されたメトリクスを収集し、直接[PrometheusのRemote Storage](https://prometheus.io/docs/prometheus/latest/storage/#remote-storage-integrations)へエクスポートしてみました。OpenTelemetry Collector、Thanos、アプリケーションは全て同じKubernetesクラスタ上で動かします。
Prometheus形式のメトリクスの取得と、PrometheusのRemote Storageへの書き込みは[opentelemetry-collector](https://github.com/open-telemetry/opentelemetry-collector)に含まれているため、今回はこちらのCollectorを利用します。

![スクリーンショット 2020-11-25 15.40.20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/224576/61a9d2c8-0630-5693-2a91-cc80ae829e6e.png)

Remote Storageには[Thanos](https://thanos.io/)を利用しました。Thanosは[Receiver](https://thanos.io/v0.17/components/receive.md/)というコンポーネントを利用することでPrometheusのRemote Writeのリクエストを受け付けることができます。
Thanos自体はRemote ReadのAPIは利用できませんが、PrometheusのAPIを直接話すことができるQueryというコンポーネントがあるため、PrometheusがなくともGrafanaからThanosへ保存されたデータを参照することができます。つまりOpenTelemetry CollectorとThanosによりPrometheusが存在しない構成がとれます。

### バージョン情報

- OpenTelemetry Collector v0.15.0
- Thanos v0.17.0
- Kubernetes v1.19.4

## OpenTelemetry Collectorの設定

OpenTelemetry Collectorに必要な設定は大きく以下の三つです。

- exporters
  - テレメトリデータのエクポートに関する設定
- processors
  - テレメトリデータの加工、フィルター、リトライ、バッチ処理等の設定
- receivers:
  - どういうフォーマットでどのようにテレメトリデータを受信するかという設定

どのような設定が全体として必要かは[公式ドキュメント](https://opentelemetry.io/docs/collector/configuration/)をご参照ください。
ここでは今回利用した設定・マニフェスト等は[こちら](https://gist.githubusercontent.com/uesyn/a7ec18c98b6e5ae822f8433c2d4aea2a/raw/58b8682113c725ce6b5884f81ecff19d2f40f00f/demo-otel-collector-and-thanos.yaml)です。この中からOpenTelemetry Collectorの設定でexportersとreceiversに関連する部分だけ少しピックアップします。

### exporters

今回はPrometheusのRemote Writeへ出力します。今回はTLSを利用しているわけではないため、PrometheusのRemote Writeに関する設定はシンプルに以下だけです。

```yaml
exporters:
  prometheusremotewrite:
    endpoint: "http://thanos-receive:19291/api/v1/receive"
```

設定可能な全ての項目は[こちら](https://github.com/open-telemetry/opentelemetry-collector/tree/master/exporter/prometheusremotewriteexporter#prometheus-remote-write-exporter)をご参照ください。

### receivers

Prometheus形式のメトリクスを取得する設定にはPrometheusのconfigと同じコンフィグのフォーマットが利用できるようです。
そのため[scrape_config](https://prometheus.io/docs/prometheus/latest/configuration/configuration/#scrape_config)が利用可能であり、スクレイプ対象のターゲットやリラレベルに関する設定にPrometheusの設定をそのまま利用できるのは嬉しいですね。
今回はPrometheusのgithubリポジトリで管理されている[こちらの設定ファイル](https://github.com/prometheus/prometheus/blob/v2.22.2/documentation/examples/prometheus-kubernetes.yml)をベースとして利用しています。

```yaml
receivers:
  prometheus:
    config:
      global:
        scrape_interval: 1m
        scrape_timeout: 30s
      scrape_configs:
        ...(省略)
```

設定可能な項目は[こちら](https://github.com/open-telemetry/opentelemetry-collector/tree/master/receiver/prometheusreceiver#prometheus-receiver)をご参照ください。

Prometheusと同様にターゲットのディスカバリにはKubernetesのAPIを利用します。そのためRBACを有効にしている場合は、OpenTelemetry Collectorに対して適切なロールの割り当てが必要です。現在利用しているPrometheusと同じClusterRole, RoleをCollectorが利用するサービスアカウントに対して割り当てれば十分なはずです。例えば[このようなClusterRole](https://github.com/prometheus/prometheus/blob/v2.22.2/documentation/examples/rbac-setup.yml#L8-L27)のようなものが利用できます。

## 実際に動かす

Kubernetesクラスタが用意できる方は以下のコマンドで試すことができます。

```sh
$ kubectl apply -f https://gist.githubusercontent.com/uesyn/a7ec18c98b6e5ae822f8433c2d4aea2a/raw/58b8682113c725ce6b5884f81ecff19d2f40f00f/demo-otel-collector-and-thanos.yaml
```

上記でThanosとOpenTelemetry Collectorがデプロイされます。
メトリクスを出力するサンプルアプリもデプロイします。

```sh
$ kubectl apply -f https://gist.githubusercontent.com/uesyn/f9d8c352d4f57f857be9abd09a136706/raw/5d4d56c379785aa0d0bf7e5b2afcbe13c61046ff/random-metrics.yaml
```

下記のコマンドでThanosのUIへport-forwardし、上記のアプリケーションのメトリクスを確認すると、メトリクスが収集されていることが確認できます。

```sh
$ kubectl -n otel port-forward svc/thanos-query 10902:10902
```

![スクリーンショット 2020-12-01 11.52.26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/224576/49b11f01-6144-5326-3671-98e3e4403b23.png)

## 最後に

上記以外のアプリケーションのメトリクスを収集していて気づいたのですが、
同様の設定のPrometheusと比べた場合に、いくつかの種類のメトリクスが現状exportされません。
またいくつかのターゲットが漏れていることがわかりました。前者については[現状のexporterの制約](https://github.com/open-telemetry/opentelemetry-collector/tree/v0.15.0/exporter/prometheusremotewriteexporter)のように見えますが、後者については軽く確認した限りメトリクスを収集するポートやパスをrelabelにより変更した場合にうまく取得できていないようです。同じ設定でPrometheusを動かした場合は正常に動作したため、receiverのバグだと思うのですが引き続き調べてみようと思います。
個人的に気になっているプロジェクトなので今後も動向を追っていこうと思います！
またメトリクスに関して言えば[VictriaMetricsのvmagent](https://victoriametrics.github.io/vmagent.html)で今回と似たようなことができそうなため、こちらも試していきたいです！

## 参考

- [Using the OpenTelemetry Collector to empower end users](https://kccncna20.sched.com/event/eoEq)
- [OpenTelemetry公式ページ](https://opentelemetry.io/)
- [Thanos公式ページ](https://thanos.io/)
- [OpenTelemetry Collector](https://github.com/open-telemetry/opentelemetry-collector)