著者: 伊藤 雅博, 株式会社日立製作所
はじめに
本稿ではOpenTelemetryの主要コンポーネントであるSDK、Collector、Operatorのアーキテクチャを紹介します。また、複数サービスをまたがるトランザクションにおけるコンテキスト伝搬(Context Propagation)について説明します。
記事一覧:
- オブザーバビリティの標準化を実現するOpenTelemetryの概要
- 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を提供
参考: 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の構成例を示します。
ビジネスロジックや計装済ライブラリ(ネイティブ計装)はOpenTelemetry APIを使用して計装しておき、実行時にJavaエージェントでOpenTelemetry SDKと設定を追加することで動作します。
SDKを追加しない場合は、APIが呼び出されるだけでテレメトリの収集は行いません(No-Op)。この仕組みにより、計装済みのコードはOpenTelemetryを使用しない環境でも問題なく動作します。
SDKの内部コンポーネント名や設定のデフォルト値は、言語・バージョンにより異なる場合があります。また、各Providerのコンポーネントの組み合わせは変更可能です。例えば、MetricをPrometheusに直接提供することもできます。
- 参考:言語API & SDK
OpenTelemetry SDKの設定例
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
- 参考: Java SDKの設定
ソースコードによる設定
内部コンポーネントの組み合わせを変更したい場合はソースコードで設定します。設定方法は言語ごとに異なるため、ソースコードによる設定例は下記ドキュメントを参照してください。
OpenTelemetry Collectorのアーキテクチャ
OpenTelemetry Collectorのコンポーネント
Collectorは下記5種類のコンポーネントで構成され、これらを組み合わせて独自のCollectorを作成します。
| コンポーネント | 役割(概要) |
|---|---|
| Receiver | データの受信または収集を行う |
| Processor | データの加工や変換を行う |
| Exporter | データの送信を行う |
| Connector | パイプライン(Receiver-Processor-Exporter)間でデータを転送 |
| Extension | 拡張機能 |
Receiver→Processor→Exporterのパイプラインを複数定義して、Connectorでパイプライン間を接続できます。以下にCollectorの構成例を示します。
Collectorコンポーネントの一覧は以下をご確認ください。
また、Collectorには公式では5種類のディストリビューションがあり、含まれるコンポーネントが異なります。検証時は多数のコンポーネントを含むOpenTelemetry Collector Contrib Distroを使用することが多いです。
本番環境で実行する場合は、OpenTelemetry Collector Builderで必要なコンポーネントのみを含むCollectorを作成することを推奨します。これによりCollectorのバイナリサイズが縮小され、デプロイ時間を短縮でき、攻撃対象領域を減らすことでセキュリティが向上します。
OpenTelemetry Collectorの設定例
Collectorの設定はYAML形式のファイルで行います。以下にCollectorの設定例を示します。
####################
# 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ノードにデプロイする例を以下に示します。
-
OpenTelemetryCollectorカスタムリソース(CR)で、OpenTelemetry Collectorの設定とデプロイ方式(Deployment/StatefulSet/DaemonSet/Sidecar)などを指定します。 - OpenTelemetry CollectorのPodを指定した設定でWorkerノードにデプロイします。
OpenTelemetryCollector CRの記載例を以下に示します。
---
# 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
最新の対応状況は以下をご確認ください。
自動計装の注入の流れを以下に示します。
-
InstrumentationCRでエージェントとSDKの設定(環境変数)を指定します。 - エージェントを注入したいユーザアプリケーションのリソース(
Pod、Deploymentなど)にアノテーションを付与します。 - アプリケーションPodの適用時にMutating Admission Webhookが介入して、以下のようにエージェントと環境変数を追加します。
- initContainersでエージェントが格納されたイメージを取得して空ボリュームに展開し、アプリケーションPodにマウント
- エージェントを利用するための環境変数などを設定
- アプリケーションの起動時にエージェントを取り込み、テレメトリーの収集を開始
Instrumentation CRの記載例を以下に示します。
---
# 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のリソースファイルにアノテーションを追加した例を以下に示します。
---
# ユーザアプリケーションの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ヘッダー)に注入して送信します。
主要な通信ライブラリはネイティブ計装されていることが多いため、基本的にはゼロコード計装(自動計装)でもコンテキスト伝搬が可能です。
以下にコンテキスト伝搬の流れを示します。
この仕組みにより、分散トレーシングにおいてサービス境界を越えた性能分析や障害解析が可能になります。
参考:
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のアーキテクチャと、サービス間のコンテキスト伝搬の仕組みを紹介しました。





