newmo 技術ブログ

技術で地域をカラフルに

OpenTelemetry Collectorを使ったCloud Run to Datadogの実装パターン

newmoでは現在アプリケーションサーバーをCloud Runで動かし、Datadogを利用してサービスの監視をすることを考えています。 複数のCloud Runサービスからメトリクス、トレース、そしてログをDatadogへ送信する方法としていくつかのパターンが考えられます。 Datadogへメトリクスやトレース、ログを送る方法としてDatadog Agentを使う方法が一般的ですが将来のための柔軟性や拡張性を考えてOpenTelemetry Collectorを利用することを検討しました。この記事では、検討した構成案を紹介します。

はじめに

Datadog公式ドキュメントのOpenTelemetryのページに、OpenTelemetryのテレメトリデータをDatadogへ送信する方法を紹介しています。

docs.datadoghq.com

Datadog Agentを使う方法と、OpenTelemetry Collectorを使う方法があります。

実装パターン

Cloud RunからDatadogへテレメトリデータを送信する方法として、以下のような実装パターンがあります。

1. アプリケーションから直接送信する

Datadog のライブラリを使って、直接送信することができます。 この方式はインフラ観点で管理するリソースが少ないというメリットがあります。しかし、アプリケーションの実装と密結合になってしまう、Envoyなど自分たちが実装していないサービスを動かす際に利用できない、などの課題があります。また、将来的には OpenTelemetry Protocol(OTLP)でも直接送信することができるようになるかもしれませんが、おそらく今はできません。

2. 同じコンテナでDatadog Agentを動かす

Datadogが公式にサポートしている方式です。

Datadogのserverless-initがアプリケーションのプロセスをwrapすることでメトリクスやトレース、ログを自動的に収集するものです。 すべてのCloud Runサービスのコンテナに設定が必要となります。

3. サイドカーとして動かす

Cloud Runでサイドカーが使えるようになったので、サイドカーで収集してDatadogへ送信することができます。サイドカーにもいくつかやり方があります。

  1. Datadog Agentをサイドカーとして動かす
  2. OpenTelemetry Collectorをサイドカーとして動かす
  3. Cloud RunのためのDatadog サイドカーを使う(2024年10月7日時点ではPrivate Beta)

アプリケーションコンテナからサイドカーに送り、サイドカーからDatadogに送信するという方式です。アプリケーションと分けてCollectorやAgenetを管理することができます。 各Cloud Runサービスごとにサイドカーの設定をする必要があります。 ログについては、サイドカーとの共有Volumeを作ってそこに書くか、OTLP(HTTP)でサイドカーへ送信することでOpenTelemetry Collectorが受け取ることができます。

最後のDatadog サイドカーは少し前に発表されていましたが、まだ公開されていないので今回の比較には入れていません。参考: Automatically instrument your Google Cloud Run services with the Datadog sidecar

4. Cloud RunサービスとしてOpenTelemetry Collectorを動かす

OpenTelemetry Collectorを独立したCloud Runサービスとして動かすこともできます。 Cloud Runサービスが一つ増えることにはなりますが、各Cloud Runサービスで追加のリソースはなくOpenTelemetry CollectorへOTLPで送信するだけになります。

newmoでは、Cloud Runサービスごとに設定を持たず1箇所に集約してPlatformチームで管理できる点と、OpenTelemetry Collectorの拡張性などの利点を考慮して、上記4のOpenTelemetry CollectorをCloud Runサービスとして動かす方式を採用してみることにしました。

OpenTelemetry Collectorの設定

各Cloud RunサービスからOpenTelemetry CollectorへOTLP(gRPC)でメトリクスやトレースを送信します。 ログについては、サービスがOTLPでログを送信する以前にエラーを出して停止してしまう場合もログを拾いたい、またCloud Runのリクエストログも合わせて取得したいということで、少し工夫して Cloud RunがCloud Loggingに出すログをLog RouterでPub/Subへ送り、OpenTelemetry Collectorの Pub/Sub receiverを使って取得する、という方式にしました。

Logの収集

設定は以下を参考にしました。

実際の設定例

receivers:
  otlp:
    protocols:
      grpc:
  googlecloudpubsub:
    subscription: projects/${env:GOOGLE_CLOUD_PROJECT}/subscriptions/${env:LOGGING_SUBSCRIPTION}
    encoding: cloud_logging

processors:
  batch:
    # Datadog APM Intake limit is 3.2MB. Let's make sure the batches do not
    # go over that.
    send_batch_max_size: 1000
    send_batch_size: 100
    timeout: 10s
  memory_limiter:
    # drop metrics if memory usage gets too high
    check_interval: 1s
    limit_percentage: 65
    spike_limit_percentage: 20

connectors:
  datadog/connector: # DatadogのAPM Trace metricsのための設定

exporters:
  datadog/exporter:
    api:
      key: ${env:DD_API_KEY}
      site: api.datadoghq.com

service:
  pipelines:
    traces:
      receivers: [otlp]
      processors: [batch]
      exporters: [datadog/connector, datadog/exporter]
    metrics:
      receivers: [datadog/connector, otlp]
      processors: [batch, memory_limiter]
      exporters: [datadog/exporter]
    logs:
      receivers: [googlecloudpubsub]
      processors: [batch]
      exporters: [datadog/exporter]

こんな感じで設定して、メトリクス、トレース、ログを収集してDatadogへ送信することができていました。 長くなるので省略しますがtrace_idのフィールドがRequestログとApplicationログでズレていたりするのを揃えたり、など細かい調整も入れています。

OpenTelemetry Collectorのビルド

OpenTelemetry Collector は otel/opentelemetry-collector-contrib という全部入りのImageを使うこともできるのですが、必要なコンポーネントだけ入れたものを OpenTelemetry Collector Builder (ocb) を使ってビルドすることが推奨されています。 現在 googlecloudpubsub receiverで encoding: cloud_logging でログを読むとときどきクラッシュする問題があり、修正のPRがマージされないままCloseされてしまっていたので、とりあえず修正を手元で当ててビルドすることで回避しています。

ocbに渡す設定例

dist:
  name: otelcol
  description: Custom OpenTelemetry Collector
  output_path: /app

receivers:
  - gomod: go.opentelemetry.io/collector/receiver/otlpreceiver v0.106.1
  - gomod: github.com/open-telemetry/opentelemetry-collector-contrib/receiver/googlecloudpubsubreceiver v0.106.1
    path: ./googlecloudpubsubreceiver
processors:
  - gomod: go.opentelemetry.io/collector/processor/batchprocessor v0.106.1
  - gomod: go.opentelemetry.io/collector/processor/memorylimiterprocessor v0.106.1
exporters:
  - gomod: github.com/open-telemetry/opentelemetry-collector-contrib/exporter/datadogexporter v0.106.1
connectors:
  - gomod: github.com/open-telemetry/opentelemetry-collector-contrib/connector/datadogconnector v0.106.1

これで必要なものを入れたImageを作成できます。

ハマったところ

ログ収集まわりのデバッグでログのフィールドの確認のためにOpenTelemetry Collectorのデバッグログを数行を出したところ、そのログをOpenTelemetry Collectorが拾って1行のログに対して数行ログが出るループができてログが爆発しました。ログ周りのデバッグ時は、OpenTelemetry Collector自身のログをExcludeする設定をしておいたほうが安全かもしれません。

まとめ

newmoでは、今後のOpenTelemetry Collectorの拡張性への期待から、OpenTelemetry CollectorをCloud Runサービスとして動かしてDatadogへテレメトリデータを送信しています。 まだ使い始めたばかりで送っているデータも多くないためパフォーマンスやコストは問題となっていませんが、今後もっと利用が増えていくと見直しが必要になる部分もあるかもしれません。 OpenTelemetry Collectorを使うのは初めてなので、改善できる部分や他の事例などがありましたら気軽に教えてもらえると助かります。

書いた人: tjun