2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

OpenTelemetry SDK/Collector/Operatorのアーキテクチャ

2
Posted at

著者: 伊藤 雅博, 株式会社日立製作所

はじめに

本稿ではOpenTelemetryの主要コンポーネントであるSDK、Collector、Operatorのアーキテクチャを紹介します。また、複数サービスをまたがるトランザクションにおけるコンテキスト伝搬(Context Propagation)について説明します。

記事一覧:

  1. オブザーバビリティの標準化を実現するOpenTelemetryの概要
  2. OpenTelemetry SDK/Collector/Operatorのアーキテクチャ(本稿)

OpenTelemetryの構成

OpenTelemetryは以下のようなテレメトリーの生成と伝送の標準仕様を提供しています。

  • Trace/Metric/Logのデータモデルと属性名の標準規格(Semantic Conventions)
  • テレメトリーの標準伝送プロトコル(OTLP: OpenTelemetry Protocol)

また、上記の仕様を実装した以下のコンポーネントを提供しています。本稿ではこれらのコンポーネントの内部構造を紹介します。

  • OpenTelemetry SDK
    • アプリケーションに組み込んでテレメトリーを作成・伝送する各言語用のSDKを提供
    • 一部の言語では、計装コードの埋め込み無しで最低限のテレメトリーを生成可能なエージェントも提供
  • OpenTelemetry Collector
    • SDKが作成・伝送したテレメトリーを加工・集約・転送するCollectorを提供
  • OpenTelemetry Operator
    • Kubernetes上でOpenTelemetryを運用・管理するためのOperatorを提供

otel_overview.png

参考: OpenTelemetryを構成する主なコンポーネント

OpenTelemetry SDKのアーキテクチャ

OpenTelemetry SDKのコンポーネント

SDKはTrace/Metric/Logを処理して伝送する以下のProviderで構成され、ユーザアプリケーションからAPIを呼び出して使用します。

Provider 役割
TracerProvider TraceのSpanを間引き、蓄積してからCollectorに転送
MeterProvider Metricを集約して定期的にCollectorへ転送
LoggerProvider Logを蓄積してからCollectorに転送

以下にJava用のOpenTelemetry SDKの構成例を示します。

otel_sdk_architecture_zerocode.png

ビジネスロジックや計装済ライブラリ(ネイティブ計装)はOpenTelemetry APIを使用して計装しておき、実行時にJavaエージェントでOpenTelemetry SDKと設定を追加することで動作します。

SDKを追加しない場合は、APIが呼び出されるだけでテレメトリの収集は行いません(No-Op)。この仕組みにより、計装済みのコードはOpenTelemetryを使用しない環境でも問題なく動作します。

SDKの内部コンポーネント名や設定のデフォルト値は、言語・バージョンにより異なる場合があります。また、各Providerのコンポーネントの組み合わせは変更可能です。例えば、MetricをPrometheusに直接提供することもできます。

OpenTelemetry SDKの設定例

SDKの設定は環境変数とソースコードで実施します。基本的な設定は環境変数のみで完結しますが、内部コンポーネントの組み合わせを変更したい場合はソースコードで設定します。ゼロコード計装の場合は環境変数のみで設定することになります。

なお、設定可能な項目は言語ごとのSDKにより若干異なります。

環境変数による設定

Java SDKにおける環境変数の設定例を示します。

# 基本設定
OTEL_SERVICE_NAME="my-service"
OTEL_RESOURCE_ATTRIBUTES="deployment.environment=prod,region=ap-northeast-1"

# Traceの設定
OTEL_TRACES_SAMPLER="parentbased_always_on"
OTEL_BSP_SCHEDULE_DELAY=5000
OTEL_TRACES_EXPORTER="otlp"

# Metricの設定
OTEL_METRIC_EXPORT_INTERVAL="60000"
OTEL_METRICS_EXEMPLAR_FILTER="TRACE_BASED"
OTEL_METRICS_EXPORTER="otlp"

# Logの設定
OTEL_BLRP_SCHEDULE_DELAY=1000
OTEL_LOGS_EXPORTER="otlp"

# OTLP Exporterの設定(Trace, Metric, Log共通)
OTEL_EXPORTER_OTLP_ENDPOINT=https://otel.example.com:4317
OTEL_EXPORTER_OTLP_PROTOCOL=grpc
OTEL_EXPORTER_OTLP_TIMEOUT=10000

ソースコードによる設定

内部コンポーネントの組み合わせを変更したい場合はソースコードで設定します。設定方法は言語ごとに異なるため、ソースコードによる設定例は下記ドキュメントを参照してください。

OpenTelemetry Collectorのアーキテクチャ

OpenTelemetry Collectorのコンポーネント

Collectorは下記5種類のコンポーネントで構成され、これらを組み合わせて独自のCollectorを作成します。

コンポーネント 役割(概要)
Receiver データの受信または収集を行う
Processor データの加工や変換を行う
Exporter データの送信を行う
Connector パイプライン(Receiver-Processor-Exporter)間でデータを転送
Extension 拡張機能

Receiver→Processor→Exporterのパイプラインを複数定義して、Connectorでパイプライン間を接続できます。以下にCollectorの構成例を示します。

otel_collector_architecture.png

Collectorコンポーネントの一覧は以下をご確認ください。

また、Collectorには公式では5種類のディストリビューションがあり、含まれるコンポーネントが異なります。検証時は多数のコンポーネントを含むOpenTelemetry Collector Contrib Distroを使用することが多いです。

本番環境で実行する場合は、OpenTelemetry Collector Builderで必要なコンポーネントのみを含むCollectorを作成することを推奨します。これによりCollectorのバイナリサイズが縮小され、デプロイ時間を短縮でき、攻撃対象領域を減らすことでセキュリティが向上します。

OpenTelemetry Collectorの設定例

Collectorの設定はYAML形式のファイルで行います。以下にCollectorの設定例を示します。

otel-collector-config.yaml
####################
# Receiver: データの受信または収集を行う
receivers:
  # OTLP Receiver: SDKからOTLPでTrace/Metric/Logを受信
  otlp/fromsdk:
    protocols:
      grpc: {}
      http: {}

  # Host Metrics Receiver: ホストのCPU/Memory/Disk/FilesystemのMetricsを受信
  hostmetrics/system:
    collection_interval: 10s
    scrapers:
      cpu: {}
      memory: {}
      disk: {}
      filesystem: {}

  # File Log Receiver: ホスト上のログファイルを読み取り
  filelog/myapp:
    include:
      - /var/log/myapp/*.log
    storage: file_storage

####################
# Processor: データの加工や変換を行う
processors:
  # Span Processor: Span名を属性から生成・変更する
  span:
    name:
      from_attributes:
        - http.method
        - http.route
      separator: "::"

  # Attributes Processor: イベントレコード単位(Span, Metric, Log)の属性を変更する
  attributes/dataset:
    actions:
      # data_stream.dataset の値を event.dataset の値として追加・上書き
      - key: event.dataset
        from_attribute: data_stream.dataset
        action: upsert

  # Resource Processor: リソース単位(プロセス, Pod, VM, サービス)の属性を変更する
  resource/process:
    # process.executable.path を削除
    attributes:
      - key: process.executable.path
        action: delete

  # Filter Processor: 条件に一致するテレメトリーを破棄する
  filter:
    logs:
      log_record:
        - 'IsMatch(body, ".*password.*")'

####################
# Exporter: データの送信を行う
exporters:
  # OTLP gRPC Exporter
  otlp_grpc/jaeger:
    endpoint: jaeger:4317
    tls:
      insecure: true

  # Elasticsearch Exporter
  elasticsearch/otel:
    endpoints:
      - http://elasticsearch:9200/
    mapping:
      mode: otel

  # Kafka exporter
  kafka:
    brokers:
      - kafka:9092

####################
# Connector: パイプライン間を接続
connectors:
  # Span Metrics Connector: SpanからRED metrics(Request, Error, Duration)を集計する
  spanmetrics: {}

  # Forward Connector: 同じタイプのパイプラインをマージまたはフォークする
  forward: {}

####################
# Extensions: 拡張機能
extensions:
  # File Storage: 状態をローカルファイルに保存する
  file_storage:
    directory: /data/otelcol

  # Health Check: http://localhost:13133/ でステータスを提供する
  health_check: {}

  # Performance Profiler: http://localhost:1777/ でパフォーマンスプロファイルを提供する
  pprof: {}

####################
# 上記で定義したコンポーネントの利用設定
service:
  # 拡張機能の起動順序を定義: 停止は逆順で実施
  extensions:
    - file_storage
    - health_check
    - pprof

  # パイプラインを定義:
  # Receiver、Processor、Exporterを組み合わせてパイプラインを定義し、Connectorでパイプライン間を接続
  pipelines:
    traces/fromsdk:
      receivers:
        - otlp/fromsdk
      processors:
        - span
      exporters:
        - otlp_grpc/jaeger
        - elasticsearch/otel
        - spanmetrics

    metrics/fromsdktrace:
      receivers:
        - spanmetrics
      processors: []
      exporters:
        - elasticsearch/otel

    metrics/fromsdk:
      receivers:
        - otlp/fromsdk
      processors:
        - attributes/dataset
        - resource/process
      exporters:
        - elasticsearch/otel

    metrics/hostmetrics:
      receivers:
        - hostmetrics/system
      processors: []
      exporters:
        - elasticsearch/otel

    logs/fromsdk:
      receivers:
        - otlp/fromsdk
      processors: []
      exporters:
        - elasticsearch/otel

    logs/myapp:
      receivers:
        - filelog/myapp
      processors:
        - attributes/dataset
      exporters:
        - elasticsearch/otel
        - forward

    logs/aggregated-logs:
      receivers:
        - forward
      processors:
        - filter
      exporters:
        - kafka

OpenTelemetry Operatorのアーキテクチャ

オブザーバビリティはコンテナ環境のような分散システムで特に重要となるため、Kubernetes上でOpenTelemetryを運用・管理するためのOperatorが提供されています。OpenTelemetry Operatorは以下の機能を提供します。

  • OpenTelemetry Collectorのデプロイ
  • ユーザアプリケーションPodにエージェント(OpenTelemetry SDKを含む)を注入

参考: Kubernetes用のOpenTelemetryオペレーター

OpenTelemetry Collectorのデプロイ

OpenTelemetry CollectorのPodを任意の方式(Deployment/StatefulSet/DaemonSet/Sidecar)でデプロイします。DaemonSetで全Workerノードにデプロイする例を以下に示します。

otel_operator_collector.png

  1. OpenTelemetryCollectorカスタムリソース(CR)で、OpenTelemetry Collectorの設定とデプロイ方式(Deployment/StatefulSet/DaemonSet/Sidecar)などを指定します。
  2. OpenTelemetry CollectorのPodを指定した設定でWorkerノードにデプロイします。

OpenTelemetryCollector CRの記載例を以下に示します。

otel-collector.yaml
---
# OpenTelemetry Collectorの定義
apiVersion: opentelemetry.io/v1beta1
kind: OpenTelemetryCollector
metadata:
  name: otel-collector
  namespace: otel-test
spec:
  # Collectorのログレベル
  logLevel: info

  # Collectorのデプロイ方式
  mode: daemonset # 全ノードにデプロイ

  # OpenTelemetry Collectorの設定
  config:
    receivers:
      otlp:
        protocols:
          grpc: {}
          http: {}
    processors: {}
    exporters:
      debug:
        verbosity: detailed
    connectors: {}
    extensions:
      health_check:
    service:
      extensions: [health_check]
      pipelines:
        traces:
          receivers: [otlp]
          processors: []
          exporters: [debug]
        metrics:
          receivers: [otlp]
          processors: []
          exporters: [debug]
        logs:
          receivers: [otlp]
          processors: []
          exporters: [debug]

自動計装の注入

ユーザアプリケーションのPodにエージェント(OpenTelemetry SDK含む)とその設定を注入します。

  • OpenTelemetry Operator v0.145.0 時点の対応言語・ソフトウェア:
    • Go, Java, .NET, Node.js, Python, Apache HTTPD, Nginx

最新の対応状況は以下をご確認ください。

自動計装の注入の流れを以下に示します。

otel_operator_instrument.png

  1. Instrumentation CRでエージェントとSDKの設定(環境変数)を指定します。
  2. エージェントを注入したいユーザアプリケーションのリソース(PodDeploymentなど)にアノテーションを付与します。
  3. アプリケーションPodの適用時にMutating Admission Webhookが介入して、以下のようにエージェントと環境変数を追加します。
    1. initContainersでエージェントが格納されたイメージを取得して空ボリュームに展開し、アプリケーションPodにマウント
    2. エージェントを利用するための環境変数などを設定
  4. アプリケーションの起動時にエージェントを取り込み、テレメトリーの収集を開始

Instrumentation CRの記載例を以下に示します。

otel-instrumentation.yaml
---
# OpenTelemetry 計装設定
apiVersion: opentelemetry.io/v1alpha1
kind: Instrumentation
metadata:
  name: otel-instrumentation
  # 注入先のアプリケーションPodが存在するNamespaceを指定
  namespace: kafka-cluster
spec:
  exporter:
    # デフォルトのプロトコル: `otlp/grpc`
    endpoint: http://otel-collector-collector.otel-test.svc.cluster.local:4317

  propagators:
    - tracecontext
    - baggage

  sampler:
    type: parentbased_traceidratio
    argument: "1"

  # OpenTelemetry SDKの環境変数をPodに注入
  env:
    - name: OTEL_EXPORTER_OTLP_TIMEOUT
      value: "5000"

  java:
    #Java言語のOpenTelemetry SDKの環境変数をPodに注入
    env:
      # JavaのOpenTelemetry SDKの伝送プロトコル: `otlp/grpc`
      - name: OTEL_EXPORTER_OTLP_ENDPOINT
        value: http://otel-collector-collector.otel-test.svc.cluster.local:4317
      - name: OTEL_EXPORTER_OTLP_PROTOCOL
        value: grpc

JavaアプリケーションPodのリソースファイルにアノテーションを追加した例を以下に示します。

user-app.yaml
---
# ユーザアプリケーションのPod
apiVersion: v1
kind: Pod
metadata:
  name: kafka-client
  namespace: kafka-cluster
  annotations:
    # OpenTelemetryのJava自動計装の注入をトリガー
    instrumentation.opentelemetry.io/inject-java: "true"
spec:
  containers:
    - name: app
      # <省略>

OpenTelemetryのコンテキスト伝搬

コンテキスト伝搬の流れ

最後に、複数のアプリケーション・サービスにまたがるトランザクション(Trace)におけるコンテキスト伝搬(Context Propagation)の流れを説明します。

TraceのSpanはサービスごとに収集されるため、これらのSpanを1つのTraceに紐づけるためには、コンテキスト(Trace IDや親Span IDなど)をサービス間で伝搬する必要があります。OpenTelemetryの初期設定では、コンテキストを W3C Trace Context という標準フォーマットで伝搬します。

各言語のOpenTelemetry SDKには、サービス間の通信にコンテキストをW3C Trace Context形式で注入(Inject)および抽出(Extract)するためのPropagators APIが付属します。例えばサービス間通信がHTTPの場合は、コンテキストをHTTPヘッダに注入して送信します。gRPC(HTTP/2上で動くRPC)の場合は、コンテキストをgRPCのMetadata(HTTP/2ヘッダー)に注入して送信します。

主要な通信ライブラリはネイティブ計装されていることが多いため、基本的にはゼロコード計装(自動計装)でもコンテキスト伝搬が可能です。

以下にコンテキスト伝搬の流れを示します。

context_propagation.png

この仕組みにより、分散トレーシングにおいてサービス境界を越えた性能分析や障害解析が可能になります。

参考:

W3C Trace Contextの概要

W3C Trace Contextでは主に以下の情報を伝搬します。

情報 役割 主な伝達情報
Traceparent
(必須)
基本的なコンテキストを伝搬 - W3C Trace Contextのバージョン
- Trace ID
- Parent ID(親Span ID)
- Trace flags(サンプリング有無)
Tracestate
(任意)
ベンダ固有の追加情報を伝搬 - APM固有情報
- サンプリング/ルーティング関連情報

W3C Trace Contextは基本的にリクエストでコンテキストを伝搬します。レスポンスでも伝搬は可能ですが基本的には使用されません。

W3C Trace Contextと合わせて、任意のキーバリューを伝搬するためのW3C Baggageもよく併用されます。

Traceparentの例

HTTPリクエストのTraceparentヘッダに含まれるコンテキスト情報の例を以下に示します。

Traceparent: [00-c7b686e41d949a0197f84fead81c2939-87477e0dd199e384-01]

W3C Trace ContextのTraceparentヘッダの定義を以下に示します。

[version]-[trace-id]-[parent-id]-[trace-flags]

よって、Traceparentヘッダの各値は以下になります。

フィールド 説明
version 00 W3C Trace Contextのバージョン
trace-id c7b686e41d949a0197f84fead81c2939 Trace ID(32桁 / 128-bit)
parent-id 87477e0dd199e384 親となるSpan ID(16桁 / 64-bit)
trace-flags 01 サンプリングの記録対象であれば01

おわりに

本稿ではOpenTelemetryの主要コンポーネントであるSDK、Collector、Operatorのアーキテクチャと、サービス間のコンテキスト伝搬の仕組みを紹介しました。

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?