コンテンツにスキップ

OpenTelemetry Collector internals

チェック

  • [ ] 本文を確認した
  • [ ] 概要を確認した
  • [ ] タグを確認した
  • [ ] inbox/ 直下へ移行した

概要

OpenTelemetry Collector の内部構造を、コードベースを読むための入口として説明するスライド。 Receiver、Processor、Exporter、Connector、Extension の役割、内部データモデル pdata、パイプラインのグラフ構築、Consumer chain によるデータ受け渡しが中心。 Collector にコントリビュートするため、どこから読めばよいかを示す資料。

本文

この資料のゴールは、OpenTelemetry Collector の利用者を、内部を読んでコントリビュートできる人に近づけること。 対象にしている Collector のコードは、資料時点で v1.34.0 付近のリポジトリ。

OpenTelemetry は、計装、生成、送信、収集、変換、エクスポートまでを含む観測データの標準化プロジェクト。 Collector はその中で、アプリケーションやエージェントから受け取った telemetry を処理し、別のバックエンドへ送る中継・処理基盤になる。

Collector の主要コンポーネント

Collector のパイプラインは、主に次のコンポーネントで構成される。

Receiver は外部からデータを受け取る。 OTLP、Jaeger、Zipkin、Prometheus scrape など、外部形式やプロトコルから Collector 内部のデータへ変換する入口。

Processor は Collector 内部のデータを処理する。 batch、memory_limiter、attributes、resource、tail_sampling など、データの加工、制限、集約、サンプリングを行う。

Exporter は Collector 内部のデータを外部へ送る。 OTLP exporter、logging/debug exporter、Prometheus exporter、各種 SaaS 向け exporter などがある。

Connector は、あるパイプラインの出力を別のパイプラインの入力へつなぐ。 たとえば traces から metrics を作るようなケースで使われる。

Extension は、パイプライン外の補助機能。 認証、ヘルスチェック、pprof、zpages など、Collector 全体の補助的な機能を担う。

内部データモデル pdata

Collector 内部では、telemetry データは pdata として扱われる。 pdata は OTLP protobuf を Go で扱いやすくするラッパーで、生成コードと追加の状態を持つ。

主なパッケージは次の通り。

  • ptrace: trace データ
  • pmetric: metric データ
  • plog: log データ
  • pprofile: profile データ
  • pcommon: 共通型

pcommon には、Value、slice、map、TimestampSpanIDTraceIDTraceStateResource など、telemetry データで共通して使う型が入る。

Trace の階層は、おおむね次のような形。

Traces
  ResourceSpans
    Resource
    ScopeSpans
      InstrumentationScope
      Span
        Status
        Event
        Link

Metric の階層は次のような形。

Metrics
  ResourceMetrics
    Resource
    ScopeMetrics
      InstrumentationScope
      Metric
        Gauge / Sum / Histogram ...
          DataPoint
          Exemplar

Log や Profile も同じように、Resource、Scope、個々のデータという階層を持つ。

pdata を使った trace 構築例

Collector コンポーネントは、外部から受け取ったデータを pdata に変換し、次のコンポーネントへ渡す。 trace を組み立てるイメージは次のようになる。

traces := ptrace.NewTraces()

rs := traces.ResourceSpans().AppendEmpty()
rs.Resource().Attributes().PutStr("service.name", "service-a")

ss := rs.ScopeSpans().AppendEmpty()
scope := ss.Scope()
scope.SetName("go.opentelemetry.io/otel/instrumentation/net/http/otelhttp")
scope.SetVersion("0.46.0")
scope.Attributes().PutStr("instrumentation.type", "automatic")

span := ss.Spans().AppendEmpty()
span.SetName("HTTP GET /api/users")
span.SetKind(ptrace.SpanKindClient)
span.Attributes().PutStr("http.method", "GET")
span.Attributes().PutInt("http.status_code", 200)
span.Status().SetCode(ptrace.StatusCodeOk)

ここで重要なのは、Collector の中では OTLP の概念が Go の型として扱われていること。 Receiver は外部形式を pdata へ変換し、Processor は pdata を受け取って加工し、Exporter は pdata を外部形式へ変換して送る。

pdata を使った metric 構築例

metric も同じように pmetric で構築できる。

metrics := pmetric.NewMetrics()

rm := metrics.ResourceMetrics().AppendEmpty()
rm.Resource().Attributes().PutStr("service.name", "service-a")

sm := rm.ScopeMetrics().AppendEmpty()
metric := sm.Metrics().AppendEmpty()
metric.SetName("http_requests_total")
metric.SetDescription("Total number of HTTP requests")

sum := metric.SetEmptySum()
sum.SetAggregationTemporality(pmetric.AggregationTemporalityCumulative)
sum.SetIsMonotonic(true)

dp := sum.DataPoints().AppendEmpty()
dp.Attributes().PutStr("method", "GET")
dp.Attributes().PutStr("status", "200")
dp.SetIntValue(1500)

Trace、Metric、Log のいずれでも、Collector 内のデータ変換の中心は pdata。 資料では「すべての道は pdata に通じる」という整理になっている。

Collector の設定とパイプライン

Collector の設定は YAML で書く。 基本構造は receivers、processors、exporters、service pipelines。

receivers:
  otlp:
    protocols:
      grpc:

processors:
  memory_limiter:
  batch:

exporters:
  otlp:
    endpoint: "your-endpoint:4317"

service:
  pipelines:
    traces:
      receivers: [otlp]
      processors: [memory_limiter, batch]
      exporters: [otlp]

この設定から、Collector はパイプラインを構築する。 traces pipeline なら、OTLP receiver が受け取った trace を memory_limiter、batch processor に通し、OTLP exporter へ送る。

グラフ構築

Collector 内部では、設定ファイルからコンポーネントの依存グラフが作られる。 資料では service/internal/graph/graph.go や、その周辺の構造が参照されている。

パイプラインのノード構造には、receivers、processors、exporters だけでなく、内部的な仮想ノードも出てくる。 たとえば capabilities node や fan-out node。

type pipelineNodes struct {
  receivers        map[component.ID]*receiverNode
  capabilitiesNode *capabilitiesNode
  processors       []*processorNode
  fanOutNode       *fanOutNode
  exporters        map[component.ID]*exporterNode
}

fan-out node は、複数 exporter へデータを配るときに必要になる。 capabilities node は、下流の Consumer がデータを変更するかどうかなど、能力情報を扱うために使われる。

Consumer chain

Collector のデータ処理は Consumer chain としてつながる。 Receiver や Processor は、次に渡す相手を consumer.Tracesconsumer.Metricsconsumer.Logs のような interface として受け取る。

Processor の実装イメージは次のようになる。

type processor struct {
  next consumer.Traces
}

func (p *processor) ConsumeTraces(ctx context.Context, td ptrace.Traces) error {
  // td を検査または加工する
  return p.next.ConsumeTraces(ctx, td)
}

Exporter は終端なので、次の Consumer を持たない。 外部バックエンドへ送るだけ。

Receiver は外部プロトコルから受け取ったデータを pdata にし、次の Consumer へ渡す。 Processor は受け取った pdata を処理し、さらに次へ渡す。 この chain が、設定ファイルで定義した pipeline として構築される。

触れられていない領域

資料では、Collector 内部の全てを扱うわけではない。 実際の component creation、factory の詳細、mdatagen、Extension、Connector、feature gates などは、さらに深掘りする領域として残されている。

ただし、最初に読むべき軸としては、pdata、component、pipeline graph、consumer chain が分かれば、Collector のコードを追いやすくなる。

要点

  • Collector の主要部品は Receiver、Processor、Exporter、Connector、Extension。
  • Collector 内部の telemetry データは pdata として扱われる。
  • ptracepmetricplogpprofilepcommon が中心パッケージ。
  • Receiver は外部データを pdata にし、Processor は pdata を加工し、Exporter は外部へ送る。
  • 設定 YAML から pipeline graph が構築される。
  • データの流れは Consumer chain として表現される。
  • Collector にコントリビュートする入口は、pdata と pipeline 構築を理解すること。

タグ

opentelemetry #collector #go #observability #pdata