- はじめに
- OpenTelemetry Collector アーキテクチャ
- Receiver の設定
- Processor パイプライン
- Exporter の設定
- Agent と Gateway のデプロイパターン
- Kubernetes 環境へのデプロイ
- メモリ管理とバックプレッシャー
- トラブルシューティングガイド
- 運用チェックリスト
- 障害事例と復旧
- 設定検証とテスト
- 高度な運用テクニック
- 参考資料

はじめに
分散システムの複雑度が増すほど、可観測性 (Observability) の重要性は指数関数的に大きくなる。数百のマイクロサービスが相互作用する環境で、Traces、Metrics、Logs という三つのテレメトリシグナルを統合的に収集し、加工し、適切なバックエンドへルーティングするパイプラインの設計は、プラットフォームエンジニアリングの中核的な能力である。
OpenTelemetry Collector は CNCF プロジェクトとして、ベンダーニュートラルなテレメトリパイプラインを提供する。特定のモニタリングソリューションに縛られることなく、Receiver でさまざまなフォーマットのデータを収集し、Processor で加工とフィルタリングを行ったうえで、Exporter で任意のバックエンドへ送信する柔軟なアーキテクチャを備えている。2026 年現在、Collector は v0.120 以上へと成熟し、プロダクション環境での安定性は十分に検証されている。
この記事では、OpenTelemetry Collector の内部アーキテクチャから、Receiver、Processor、Exporter の実践的な設定、Agent/Gateway のデプロイパターン、Kubernetes 環境における DaemonSet/Deployment マニフェスト、Tail Sampling 戦略、メモリ管理とバックプレッシャーのメカニズム、トラブルシューティングガイド、そして障害復旧手順まで、運用の観点で必要になる内容をすべて扱う。
OpenTelemetry Collector アーキテクチャ
主要コンポーネント構成
OpenTelemetry Collector のアーキテクチャは四つの主要コンポーネントで構成される。Receiver が外部ソースからテレメトリデータを受信し、Processor がデータを加工し、Exporter が最終的な宛先へ送信する。さらに Extension が付加的な機能 (ヘルスチェック、認証、zPages など) を提供する。
[Application / Infrastructure]
|
v
+-------------------+
| Receivers | <-- OTLP, Prometheus, Filelog, Kafka, etc.
+-------------------+
|
v
+-------------------+
| Processors | <-- Memory Limiter, Batch, Attributes, Tail Sampling
+-------------------+
|
v
+-------------------+
| Exporters | <-- OTLP, Prometheus Remote Write, Loki, Kafka, etc.
+-------------------+
+-------------------+
| Extensions | <-- Health Check, zPages, pprof, Bearer Token Auth
+-------------------+
一つの Collector インスタンスの中に複数のパイプラインを定義できる。各パイプラインは一つのシグナルタイプ (traces、metrics、logs) を処理し、それぞれ異なる Receiver、Processor、Exporter の組み合わせを持てる。この設計のおかげで、トレースは Tempo へ、メトリクスは Mimir へ、ログは Loki へそれぞれルーティングする構成が、単一の Collector 設定ファイルの中で実現できる。
Core と Contrib のディストリビューション
OpenTelemetry Collector は二つのディストリビューションを提供する。
| 項目 | Core | Contrib |
|---|---|---|
| 含まれるコンポーネント | 主要な Receiver/Processor/Exporter のみ | コミュニティ提供コンポーネントを多数含む |
| バイナリサイズ | 約 50MB | 約 200MB+ |
| セキュリティ表面積 | 小さい | 広い |
| 更新周期 | 隔週 | 隔週 |
| プロダクション推奨 | Custom Build 推奨 | テスト/PoC に適する |
| 主なユースケース | 最小限のコンポーネントのみ必要な場合 | 多様なソース/宛先の連携が必要な場合 |
プロダクション環境では、OpenTelemetry Collector Builder (OCB) を使って必要なコンポーネントだけを含むカスタムバイナリをビルドすることが、セキュリティと性能の両面から推奨される。
データモデルとシグナルタイプ
Collector の内部でデータは pdata (Pipeline Data) 形式で表現される。この内部データモデルは OTLP (OpenTelemetry Protocol) のプロトコルバッファ定義を基礎としており、三つのシグナルタイプをサポートする。
- Traces: 分散トランザクションの実行経路を表す Span の集合。TraceID、SpanID、ParentSpanID で因果関係を表現する。
- Metrics: 時系列データで、Gauge、Sum、Histogram、ExponentialHistogram、Summary タイプをサポートする。
- Logs: タイムスタンプベースのイベントレコード。SeverityNumber、Body、Attributes、Resource を含む。
Receiver の設定
Receiver はテレメトリデータの入口である。Push 方式 (OTLP、Kafka など) と Pull 方式 (Prometheus、hostmetrics など) の両方をサポートし、同じタイプの Receiver を名前を変えて複数定義できる。
OTLP Receiver
OTLP は OpenTelemetry のネイティブプロトコルで、gRPC と HTTP/protobuf の二つの転送方式を提供する。ほとんどの OpenTelemetry SDK は OTLP Exporter をデフォルトで使うため、この Receiver はほぼすべての Collector 構成に含まれる。
receivers:
otlp:
protocols:
grpc:
endpoint: 0.0.0.0:4317
max_recv_msg_size_mib: 8 # 大容量バッチを許可
max_concurrent_streams: 256 # 同時ストリーム数
keepalive:
server_parameters:
max_connection_idle: 30s
max_connection_age: 60s
max_connection_age_grace: 10s
enforcement_policy:
min_time: 10s
permit_without_stream: true
http:
endpoint: 0.0.0.0:4318
cors:
allowed_origins:
- 'https://*.company.com'
allowed_headers:
- 'Content-Type'
- 'X-Custom-Header'
max_age: 600
gRPC 転送は HTTP/2 ベースでバイナリシリアライズと多重化をサポートし、大容量テレメトリに効率的だ。HTTP 転送はブラウザベースの計測 (Web SDK) やファイアウォール制約のある環境で使う。
Prometheus Receiver
Prometheus Receiver は既存の Prometheus エコシステムとの互換性を提供する。Prometheus の scrape_configs 文法をそのまま使えるため、これまで Prometheus で収集していたメトリクスを Collector 経由で別のバックエンドへルーティングできる。
receivers:
prometheus:
config:
scrape_configs:
- job_name: 'kubernetes-pods'
scrape_interval: 30s
scrape_timeout: 10s
kubernetes_sd_configs:
- role: pod
relabel_configs:
# prometheus.io/scrape アノテーションを持つ Pod のみスクレイピング
- source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_scrape]
action: keep
regex: true
# カスタムポートの指定
- source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_port]
action: replace
target_label: __address__
regex: (.+)
replacement: '$$1'
# ネームスペースラベルを追加
- source_labels: [__meta_kubernetes_namespace]
action: replace
target_label: namespace
# Pod 名ラベルを追加
- source_labels: [__meta_kubernetes_pod_name]
action: replace
target_label: pod
- job_name: 'node-exporter'
scrape_interval: 15s
static_configs:
- targets: ['node-exporter.monitoring.svc:9100']
Filelog Receiver
Filelog Receiver はファイルシステム上のログファイルをリアルタイムに収集する。Kubernetes 環境でコンテナログを収集する中核コンポーネントであり、オペレーターチェーンを通じてログのパース、フィルタリング、変換を行う。
receivers:
filelog:
include:
- /var/log/pods/*/*/*.log
exclude:
- /var/log/pods/*/otel-collector*/*.log
- /var/log/pods/kube-system_*/*/*.log
start_at: end # 新規ログのみ収集 (beginning なら既存ログから)
include_file_path: true
include_file_name: false
retry_on_failure:
enabled: true
initial_interval: 1s
max_interval: 30s
operators:
# CRI ログフォーマットのパース (containerd)
- type: regex_parser
id: parser-cri
regex: '^(?P<time>[^ Z]+Z) (?P<stream>stdout|stderr) (?P<logtag>[^ ]*) ?(?P<log>.*)$'
timestamp:
parse_from: attributes.time
layout: '%Y-%m-%dT%H:%M:%S.%fZ'
# JSON ログ本文のパース
- type: json_parser
id: parser-json
parse_from: attributes.log
parse_to: body
on_error: send_quiet # パース失敗時は原本を保持
# 重大度のマッピング
- type: severity_parser
parse_from: attributes.level
mapping:
fatal: [FATAL, fatal, F]
error: [ERROR, error, E]
warn: [WARN, warn, W]
info: [INFO, info, I]
debug: [DEBUG, debug, D]
Receiver タイプ比較表
| Receiver タイプ | 方式 | シグナル | 主な用途 |
|---|---|---|---|
| otlp | Push | Traces, Metrics, Logs | OTel SDK で計測したアプリケーション |
| prometheus | Pull | Metrics | Prometheus 互換メトリクスのスクレイピング |
| filelog | Pull | Logs | コンテナ/ファイルログの収集 |
| hostmetrics | Pull | Metrics | CPU、Memory、Disk、Network のホストメトリクス |
| k8s_events | Pull | Logs | Kubernetes イベントの収集 |
| kafka | Push | Traces, Metrics, Logs | Kafka トピックからのテレメトリ消費 |
| zipkin | Push | Traces | Zipkin フォーマットのトレース受信 |
| jaeger | Push | Traces | Jaeger フォーマットのトレース受信 |
Processor パイプライン
Processor は Receiver と Exporter の間でデータを加工する中間レイヤーである。順序が重要で、パイプラインに定義された順にチェーンされて実行される。一般的に推奨される Processor の順序は以下の通りだ。
memory_limiter -> k8sattributes -> resourcedetection -> attributes -> filter -> tail_sampling -> batch
Memory Limiter Processor
Memory Limiter は Collector の OOM (Out of Memory) を防ぐ安全装置だ。必ず Processor チェーンの最初に配置する必要があり、メモリ使用量がしきい値に達するとデータを拒否してプロセスを保護する。
processors:
memory_limiter:
check_interval: 1s
limit_mib: 1800 # ハードリミット (コンテナ limit の 80%)
spike_limit_mib: 500 # 突発的なスパイクの許容量
# limit_percentage: 80 # または比率ベースの設定 (cgroup 認識)
# spike_limit_percentage: 25
limit_mib はコンテナのメモリ limit のおよそ 80% 程度に設定する。例えばコンテナの limit が 2Gi なら limit_mib を 1600-1800 に設定する。残りの 20% は Go ランタイムとその他の内部バッファのための余裕である。spike_limit_mib は瞬間的なトラフィックバーストを許容しつつ limit_mib を超えないようにする緩衝の役割を果たす。
Batch Processor
Batch Processor は個々のテレメトリレコードをまとめてバッチ単位で Exporter に渡す。このバッチングはネットワークリクエスト数を減らし圧縮効率を高めるため、パイプライン全体のスループットを大きく向上させる。
processors:
batch:
timeout: 5s # 最大待機時間
send_batch_size: 8192 # バッチサイズ (レコード数)
send_batch_max_size: 16384 # 最大バッチサイズ (このサイズを超えると分割)
# Gateway ではより大きなバッチ
batch/gateway:
timeout: 10s
send_batch_size: 16384
send_batch_max_size: 32768
timeout に先に到達するか send_batch_size に先に到達するか、いずれかの条件を満たした時点でバッチが送信される。トラフィックが少ない環境では timeout が、トラフィックが多い環境では send_batch_size が主に働く。
Attributes Processor
Attributes Processor はテレメトリデータの属性 (Attribute) を追加、修正、削除する。機微情報の除去、環境情報のタグ付け、ラベルの正規化などに使う。
processors:
attributes/security:
actions:
# 機微な HTTP ヘッダーを削除
- key: http.request.header.authorization
action: delete
- key: http.request.header.cookie
action: delete
# DB クエリのハッシュ化 (機微データの保護)
- key: db.statement
action: hash
# 環境タグを追加
- key: deployment.environment
action: upsert
value: production
# IP アドレスのマスキング
- key: net.peer.ip
action: extract
pattern: '^(?P<subnet>\d+\.\d+\.\d+)\.\d+$'
- key: net.peer.ip
action: delete
- key: net.peer.subnet
from_attribute: subnet
action: upsert
Tail Sampling Processor
Tail Sampling はトレース全体の Span がすべて収集されたあとにサンプリングの判断を下す方式だ。Head Sampling と違い、エラーが発生したトレースやレスポンスが遅いトレースを漏れなく保存できるため、プロダクション環境でデバッグ能力とコスト削減を同時に達成できる。
processors:
tail_sampling:
decision_wait: 30s # トレース完了の待機時間
num_traces: 200000 # メモリに保持する最大トレース数
expected_new_traces_per_sec: 5000
policies:
# ポリシー 1: エラーを含むトレースは 100% 保存
- name: error-traces
type: status_code
status_code:
status_codes: [ERROR]
# ポリシー 2: 2 秒以上かかったトレースは 100% 保存
- name: high-latency
type: latency
latency:
threshold_ms: 2000
upper_threshold_ms: 0 # 0 なら上限なし
# ポリシー 3: 重要サービスは 50% 保存
- name: critical-services
type: and
and:
and_sub_policy:
- name: service-match
type: string_attribute
string_attribute:
key: service.name
values:
- payment-service
- auth-service
- order-service
- name: sample-half
type: probabilistic
probabilistic:
sampling_percentage: 50
# ポリシー 4: 特定の HTTP パスを除外 (health check など)
- name: drop-health-checks
type: string_attribute
string_attribute:
key: http.route
values:
- /healthz
- /readyz
- /livez
invert_match: true
# ポリシー 5: 残りのトラフィックは 5% のみサンプリング
- name: default-sampling
type: probabilistic
probabilistic:
sampling_percentage: 5
Tail Sampling の最も重要な注意点は、同一 TraceID のすべての Span が同一の Collector インスタンスに到達しなければならないことだ。Gateway が複数台ある場合は、必ず TraceID ベースの一貫性ハッシュ (ロードバランサーの consistent hashing) が必要になる。
Processor タイプ比較表
| Processor | 役割 | 必須かどうか | 推奨する配置位置 |
|---|---|---|---|
| memory_limiter | OOM 防止 | 必須 | 最優先 (最初) |
| k8sattributes | K8s メタデータの注入 | 推奨 | memory_limiter の次 |
| resourcedetection | クラウド/ホスト情報の注入 | 推奨 | k8sattributes の次 |
| attributes | 属性の追加/修正/削除 | 任意 | 中間 |
| filter | 不要データのドロップ | 任意 | サンプリングの前 |
| tail_sampling | トレース単位のサンプリング | 任意 (traces) | batch の前 |
| transform | OTTL ベースの変換 | 任意 | 状況に応じて |
| batch | バッチ処理 | 必須 | 最後 |
Exporter の設定
Exporter は加工済みのテレメトリデータを最終的な宛先へ送信する役割を担う。同じ Exporter タイプを名前を変えて複数のバックエンドへ同時に送信でき、retry と queue の設定で送信の安定性を確保する。
OTLP Exporter
OTLP Exporter は別の Collector (Gateway) や OTLP をネイティブにサポートするバックエンド (Tempo、Jaeger、SigNoz など) へデータを送信する。
exporters:
# Traces -> Grafana Tempo
otlp/tempo:
endpoint: tempo-distributor.observability.svc:4317
tls:
insecure: true # クラスタ内部通信
retry_on_failure:
enabled: true
initial_interval: 5s
max_interval: 30s
max_elapsed_time: 300s
sending_queue:
enabled: true
num_consumers: 10
queue_size: 5000
timeout: 30s
# Traces -> Jaeger (OTLP ネイティブ対応)
otlp/jaeger:
endpoint: jaeger-collector.observability.svc:4317
tls:
cert_file: /certs/client.crt
key_file: /certs/client.key
ca_file: /certs/ca.crt
Prometheus Remote Write Exporter
Prometheus Remote Write Exporter はメトリクスを Prometheus 互換のバックエンド (Mimir、Thanos、Cortex、VictoriaMetrics) へ送信する。
exporters:
prometheusremotewrite/mimir:
endpoint: https://mimir.observability.svc:9009/api/v1/push
tls:
insecure: false
cert_file: /certs/client.crt
key_file: /certs/client.key
headers:
X-Scope-OrgID: 'tenant-production'
resource_to_telemetry_conversion:
enabled: true # Resource 属性をメトリックラベルに変換
external_labels:
cluster: 'prod-ap-northeast-2'
region: 'ap-northeast-2'
retry_on_failure:
enabled: true
initial_interval: 5s
max_interval: 60s
sending_queue:
enabled: true
num_consumers: 5
queue_size: 10000
Loki Exporter
Loki Exporter はログデータを Grafana Loki へ送信する。ラベルマッピングの設定が重要で、過度なラベルカーディナリティは Loki の性能を低下させるため注意が必要だ。
exporters:
loki:
endpoint: https://loki-gateway.observability.svc:3100/loki/api/v1/push
headers:
X-Scope-OrgID: 'tenant-production'
default_labels_enabled:
exporter: false
job: true
instance: true
level: true
retry_on_failure:
enabled: true
initial_interval: 5s
max_interval: 30s
sending_queue:
enabled: true
num_consumers: 5
queue_size: 5000
主要バックエンド比較表
| バックエンド | シグナル | プロトコル | 主な特徴 | Exporter タイプ |
|---|---|---|---|---|
| Grafana Tempo | Traces | OTLP gRPC | オブジェクトストレージ、コスト効率 | otlp |
| Grafana Mimir | Metrics | Prometheus Remote Write | Prometheus 互換、マルチテナント | prometheusremotewrite |
| Grafana Loki | Logs | HTTP Push | ラベルベースのインデックス、低コスト | loki |
| Jaeger | Traces | OTLP gRPC | トレース専用、UI 内蔵 | otlp |
| Elasticsearch | Logs | HTTP | 全文検索に強い | elasticsearch |
| SigNoz | All | OTLP gRPC | オールインワン、ClickHouse ベース | otlp |
| Datadog | All | HTTP | SaaS、豊富な統合 | datadog |
Agent と Gateway のデプロイパターン
OpenTelemetry Collector をデプロイする方式は、大きく Agent パターン、Gateway パターン、そして両者を組み合わせたハイブリッドパターンに分かれる。プロダクション環境では Agent + Gateway の組み合わせが最も広く使われており、それぞれのパターンの長所と短所を理解したうえで、トラフィック規模に合わせて選択する必要がある。
パターン別比較
| 特性 | Agent (DaemonSet) | Gateway (Deployment) | Agent + Gateway |
|---|---|---|---|
| デプロイ方式 | 各ノードに 1 つ | クラスタ内の独立サービス | 二層の組み合わせ |
| 収集範囲 | ローカルノード | クラスタ全体 | ローカル収集 + 中央処理 |
| Tail Sampling | 不可 (トレースが分散) | 可能 (中央集中) | Gateway で実施 |
| リソース使用 | ノード数だけ分散 | 集中 | 分散 + 集中 |
| 障害の影響範囲 | 該当ノードのみ | パイプライン全体 | 隔離可能 |
| スケーリング | ノード追加時に自動 | HPA で水平拡張 | 独立したスケーリング |
| 複雑度 | 低い | 中間 | 高い |
| 推奨トラフィック | 小規模 | 中規模 | 中~大規模 |
Agent + Gateway ハイブリッドアーキテクチャ
[Node 1] [Node 2] [Node N]
+----------+ +----------+ +----------+
| App Pods | | App Pods | | App Pods |
+----+-----+ +----+-----+ +----+-----+
| | |
+----+-----+ +----+-----+ +----+-----+
| OTel | | OTel | | OTel |
| Agent | | Agent | | Agent |
| (DaemonSet) | (DaemonSet) | (DaemonSet)
+----+-----+ +----+-----+ +----+-----+
| | |
+------------+-------------+-----------+--------------+
| |
+------+------+ +------+------+
| OTel Gateway | | OTel Gateway |
| (Deployment) | | (Deployment) |
+------+------+ +------+------+
| |
+------------+-------------------------+
| | |
+----+----+ +----+----+ +-----+-----+
| Tempo | | Mimir | | Loki |
+---------+ +---------+ +-----------+
Agent では軽量な処理 (メモリ制限、基本的なバッチング、K8s メタデータの注入) だけを行い、Gateway が Tail Sampling、高度なフィルタリング、最終的なバックエンドへのルーティングを担当する。この分離によって Agent のリソース使用量を最小限に抑えつつ、Gateway で緻密なデータ処理を行える。
Kubernetes 環境へのデプロイ
Agent DaemonSet マニフェスト
apiVersion: opentelemetry.io/v1beta1
kind: OpenTelemetryCollector
metadata:
name: otel-agent
namespace: observability
spec:
mode: daemonset
image: otel/opentelemetry-collector-contrib:0.120.0
serviceAccount: otel-collector-agent
env:
- name: K8S_NODE_NAME
valueFrom:
fieldRef:
fieldPath: spec.nodeName
- name: K8S_POD_IP
valueFrom:
fieldRef:
fieldPath: status.podIP
resources:
requests:
cpu: 200m
memory: 256Mi
limits:
cpu: 500m
memory: 512Mi
volumeMounts:
- name: varlogpods
mountPath: /var/log/pods
readOnly: true
- name: varlibdockercontainers
mountPath: /var/lib/docker/containers
readOnly: true
volumes:
- name: varlogpods
hostPath:
path: /var/log/pods
- name: varlibdockercontainers
hostPath:
path: /var/lib/docker/containers
tolerations:
- operator: Exists
config:
receivers:
otlp:
protocols:
grpc:
endpoint: 0.0.0.0:4317
http:
endpoint: 0.0.0.0:4318
filelog:
include:
- /var/log/pods/*/*/*.log
exclude:
- /var/log/pods/observability_otel-*/*/*.log
start_at: end
include_file_path: true
operators:
- type: regex_parser
id: parser-cri
regex: '^(?P<time>[^ Z]+Z) (?P<stream>stdout|stderr) (?P<logtag>[^ ]*) ?(?P<log>.*)$'
timestamp:
parse_from: attributes.time
layout: '%Y-%m-%dT%H:%M:%S.%fZ'
hostmetrics:
collection_interval: 30s
scrapers:
cpu: {}
memory: {}
disk: {}
network: {}
load: {}
filesystem:
exclude_mount_points:
mount_points: ['/dev/*', '/proc/*', '/sys/*']
match_type: regexp
processors:
memory_limiter:
check_interval: 1s
limit_mib: 400
spike_limit_mib: 100
k8sattributes:
auth_type: serviceAccount
passthrough: false
extract:
metadata:
- k8s.namespace.name
- k8s.deployment.name
- k8s.statefulset.name
- k8s.daemonset.name
- k8s.pod.name
- k8s.pod.uid
- k8s.node.name
- k8s.container.name
labels:
- tag_name: app.label.team
key: team
from: pod
pod_association:
- sources:
- from: resource_attribute
name: k8s.pod.ip
- sources:
- from: connection
batch:
timeout: 5s
send_batch_size: 4096
exporters:
otlp/gateway:
endpoint: otel-gateway.observability.svc.cluster.local:4317
tls:
insecure: true
retry_on_failure:
enabled: true
initial_interval: 5s
max_interval: 30s
sending_queue:
enabled: true
queue_size: 2000
extensions:
health_check:
endpoint: 0.0.0.0:13133
service:
extensions: [health_check]
pipelines:
traces:
receivers: [otlp]
processors: [memory_limiter, k8sattributes, batch]
exporters: [otlp/gateway]
metrics:
receivers: [otlp, hostmetrics]
processors: [memory_limiter, k8sattributes, batch]
exporters: [otlp/gateway]
logs:
receivers: [otlp, filelog]
processors: [memory_limiter, k8sattributes, batch]
exporters: [otlp/gateway]
telemetry:
logs:
level: warn
metrics:
address: 0.0.0.0:8888
Gateway Deployment マニフェスト
apiVersion: opentelemetry.io/v1beta1
kind: OpenTelemetryCollector
metadata:
name: otel-gateway
namespace: observability
spec:
mode: deployment
replicas: 3
image: otel/opentelemetry-collector-contrib:0.120.0
serviceAccount: otel-collector-gateway
resources:
requests:
cpu: '1'
memory: 2Gi
limits:
cpu: '2'
memory: 4Gi
autoscaler:
minReplicas: 3
maxReplicas: 10
targetCPUUtilization: 70
targetMemoryUtilization: 80
podDisruptionBudget:
minAvailable: 2
config:
receivers:
otlp:
protocols:
grpc:
endpoint: 0.0.0.0:4317
max_recv_msg_size_mib: 16
processors:
memory_limiter:
check_interval: 1s
limit_mib: 3200
spike_limit_mib: 800
resourcedetection:
detectors: [env, system, gcp, aws, azure]
timeout: 5s
override: false
attributes/security:
actions:
- key: http.request.header.authorization
action: delete
- key: db.statement
action: hash
filter/metrics:
metrics:
exclude:
match_type: regexp
metric_names:
- 'go_.*'
- 'process_.*'
- 'promhttp_.*'
tail_sampling:
decision_wait: 30s
num_traces: 200000
expected_new_traces_per_sec: 5000
policies:
- name: error-traces
type: status_code
status_code:
status_codes: [ERROR]
- name: high-latency
type: latency
latency:
threshold_ms: 2000
- name: default-sampling
type: probabilistic
probabilistic:
sampling_percentage: 10
batch:
timeout: 10s
send_batch_size: 16384
send_batch_max_size: 32768
exporters:
otlp/tempo:
endpoint: tempo-distributor.observability.svc:4317
tls:
insecure: true
sending_queue:
enabled: true
num_consumers: 10
queue_size: 10000
prometheusremotewrite/mimir:
endpoint: http://mimir-distributor.observability.svc:8080/api/v1/push
headers:
X-Scope-OrgID: 'production'
resource_to_telemetry_conversion:
enabled: true
sending_queue:
enabled: true
num_consumers: 5
queue_size: 10000
loki:
endpoint: http://loki-gateway.observability.svc:3100/loki/api/v1/push
headers:
X-Scope-OrgID: 'production'
sending_queue:
enabled: true
queue_size: 5000
extensions:
health_check:
endpoint: 0.0.0.0:13133
zpages:
endpoint: 0.0.0.0:55679
pprof:
endpoint: 0.0.0.0:1777
service:
extensions: [health_check, zpages, pprof]
pipelines:
traces:
receivers: [otlp]
processors: [memory_limiter, resourcedetection, attributes/security, tail_sampling, batch]
exporters: [otlp/tempo]
metrics:
receivers: [otlp]
processors: [memory_limiter, resourcedetection, filter/metrics, batch]
exporters: [prometheusremotewrite/mimir]
logs:
receivers: [otlp]
processors: [memory_limiter, resourcedetection, attributes/security, batch]
exporters: [loki]
telemetry:
logs:
level: info
metrics:
address: 0.0.0.0:8888
Gateway のロードバランシングと TraceID ベースのルーティング
Tail Sampling を使う Gateway が複数台ある場合、同一 TraceID の Span が異なる Gateway インスタンスへ分散すると、サンプリングの判断が不完全になる。この問題を解決するため、Agent から Gateway へ送信する際に loadbalancing Exporter を使う。
# Agent 側の Exporter 設定 (Gateway へ送信する場合)
exporters:
loadbalancing:
protocol:
otlp:
tls:
insecure: true
timeout: 10s
resolver:
dns:
hostname: otel-gateway-headless.observability.svc.cluster.local
port: 4317
# または Kubernetes resolver を使用
# k8s:
# service: otel-gateway
# ports:
# - 4317
routing_key: traceID # TraceID ベースの一貫性ハッシュ
この設定を使えば、同一 TraceID を持つすべての Span が同じ Gateway Pod へルーティングされ、Tail Sampling の正確性が保証される。
メモリ管理とバックプレッシャー
OpenTelemetry Collector の運用で最も頻繁に発生する問題はメモリ関連の問題だ。テレメトリトラフィックの急増、Tail Sampling の待機バッファ、sending queue の蓄積などが複合的にメモリ使用量を押し上げる。
メモリ使用量の算定式
Collector のメモリ使用量を見積もる際は、次の要素を考慮する必要がある。
総メモリ = Go ランタイム基本 (約 50MB)
+ Receiver バッファ (接続数 x メッセージサイズ)
+ Processor バッファ
- Batch: send_batch_max_size x レコード平均サイズ
- Tail Sampling: num_traces x トレース平均サイズ
+ Exporter キュー: queue_size x バッチサイズ x レコード平均サイズ
+ 内部オーバーヘッド (約 10-20%)
Tail Sampling が最大のメモリ消費要因だ。decision_wait の時間だけトレースをメモリに保持するため、decision_wait が 30 秒で毎秒 5,000 件の新しいトレースが流入すると、およそ 150,000 件のトレースが同時にメモリ上に存在することになる。
バックプレッシャーのメカニズム
Collector のバックプレッシャー (Backpressure) は三段階で動作する。
第 1 段階では、Exporter の sending queue がいっぱいになると Exporter が Processor に圧力を伝える。第 2 段階では、memory_limiter がメモリ使用量が limit_mib に達したことを検知し、Receiver にデータ拒否のシグナルを送る。第 3 段階では、Receiver がデータを拒否するとクライアント (SDK または Agent) にエラーを返し、クライアントの retry ロジックが動作する。
このバックプレッシャーの連鎖が正しく動作するには、memory_limiter が必ず Processor チェーンの最初に位置していなければならない。そうでなければ、メモリ制限が働く前に別の Processor がメモリを使い果たし、OOM が発生しうる。
GOGC チューニング
Go ランタイムのガベージコレクタのチューニングもメモリ管理では重要だ。GOGC のデフォルト値は 100 で、これはヒープサイズが前回の GC サイクル比で 100% 増加すると GC をトリガーすることを意味する。メモリの余裕が少ない環境では GOGC を下げて、より頻繁な GC を促すことができる。
env:
- name: GOGC
value: '80' # デフォルト値 100 から 80 に下げる
- name: GOMEMLIMIT
value: '3600MiB' # ソフトメモリ上限 (limit の 90%)
トラブルシューティングガイド
自己テレメトリメトリクスによる診断
Collector は自身のテレメトリメトリクスをデフォルトポート 8888 で提供する。このメトリクスを Prometheus でスクレイピングし、Grafana ダッシュボードで監視することが必須だ。
# 受信メトリクス
otelcol_receiver_accepted_spans # Receiver が受け入れた Span 数
otelcol_receiver_refused_spans # Receiver が拒否した Span 数 (バックプレッシャー)
otelcol_receiver_accepted_metric_points # 受け入れたメトリックポイント数
otelcol_receiver_accepted_log_records # 受け入れたログレコード数
# Processor メトリクス
otelcol_processor_dropped_spans # Processor でドロップされた Span 数
otelcol_processor_batch_batch_send_size # 実際に送信されたバッチサイズ
# Exporter メトリクス
otelcol_exporter_sent_spans # Exporter が送信に成功した Span 数
otelcol_exporter_send_failed_spans # 送信に失敗した Span 数
otelcol_exporter_queue_size # 現在キューで待機中の項目数
otelcol_exporter_queue_capacity # キューの最大容量
主要なアラートの式は以下の通りだ。
- データ欠損アラート:
rate(otelcol_receiver_refused_spans[5m]) > 0-- Receiver がデータを拒否しているならバックプレッシャーが動作中であり、アップストリームが再送しないデータは失われる可能性がある。 - Exporter 障害アラート:
rate(otelcol_exporter_send_failed_spans[5m]) > 0-- バックエンドへの接続失敗を意味するため、ネットワークまたはバックエンドの状態を点検する必要がある。 - キュー飽和アラート:
otelcol_exporter_queue_size / otelcol_exporter_queue_capacity > 0.8-- キューが 80% 以上埋まると、まもなくデータのドロップが発生しうる。
zPages を活用したリアルタイムデバッグ
zPages 拡張を有効にすると、ブラウザから Collector の内部状態をリアルタイムで確認できる。
/debug/servicez-- Collector のサービス情報/debug/pipelinez-- パイプラインの構成と状態/debug/extensionz-- 拡張の状態/debug/tracez-- 直近に処理されたトレースのサンプル
よくある問題と解決法
問題 1: Collector が OOM で再起動を繰り返す
原因の多くは、Tail Sampling の num_traces が大きすぎるか decision_wait が長すぎて、メモリに過剰なトレースが蓄積されるケースだ。num_traces を減らすか decision_wait を 10-15 秒に短縮し、memory_limiter の limit_mib をコンテナ limit の 75% まで下げる。
問題 2: Exporter で "context deadline exceeded" エラー
バックエンドの応答が timeout 内に返ってこない場合に発生する。Exporter の timeout 値を増やすか、sending_queue の num_consumers を増やして同時送信数を上げる。根本的にはバックエンドの処理能力をスケールアップする必要がある。
問題 3: トレースで Span が欠落する
Tail Sampling の Gateway が複数台あるのに TraceID ベースのルーティングが設定されていない場合に発生する。Agent で loadbalancing Exporter を使い、TraceID ベースの一貫性ハッシュを適用する。
問題 4: Prometheus Receiver で "context canceled" エラー
scrape_timeout が scrape_interval 以上の場合に発生する。scrape_timeout を scrape_interval の 50-80% 程度に設定する。
運用チェックリスト
プロダクション環境で OpenTelemetry Collector を安定して運用するためのチェックリストだ。
デプロイ前チェックリスト
- memory_limiter がすべてのパイプラインの最初の Processor になっているか確認
- batch Processor がすべてのパイプラインの最後の Processor になっているか確認
- memory_limiter の limit_mib がコンテナのメモリ limit の 75-80% 以下か確認
- Exporter の sending_queue が有効で、適切なサイズになっているか確認
- Exporter の retry_on_failure が有効になっているか確認
- health_check Extension が設定され、liveness/readiness probe が接続されているか確認
- TLS 設定が必要な Exporter に証明書がマウントされているか確認
- RBAC 権限が正しく設定されているか確認 (k8sattributes を使う場合は ClusterRole が必要)
モニタリング設定
- Collector 自体のメトリクス (ポート 8888) に対する Prometheus scrape 設定が完了している
- Grafana ダッシュボードの構成 (受信/ドロップ/送信 Span 数、キュー使用率、メモリ使用量)
- 主要なアラートルールの設定 (refused spans、send failed、queue saturation、OOM)
- zPages または pprof Extension の有効化 (トラブルシューティング用)
スケーリング戦略
- Agent DaemonSet に適切な resource requests/limits を設定
- Gateway Deployment に HPA を設定 (CPU 70%、Memory 80% ターゲット)
- Gateway に PodDisruptionBudget を設定 (minAvailable または maxUnavailable)
- Tail Sampling を使う場合は Gateway の headless Service と loadbalancing Exporter を設定
- Pod Anti-Affinity で Gateway Pod が異なるノードへ分散配置されるか確認
セキュリティチェック
- 機微な属性 (Authorization ヘッダー、DB クエリなど) が attributes Processor で削除/ハッシュ化されているか確認
- Collector のエンドポイントが不必要に外部へ公開されていないか確認
- ServiceAccount に最小権限の原則が適用されているか確認
- 設定ファイルにハードコードされたシークレットがないか確認 (環境変数または Secret を使用)
障害事例と復旧
事例 1: Tail Sampling のメモリ暴走による連鎖障害
状況: Gateway 3 台で Tail Sampling を運用中、ブラックフライデーのトラフィック急増でトレースの流入量が平常時の 5 倍に増えた。decision_wait が 30 秒、num_traces が 500,000 に設定されていたが、実際にメモリへ保持されたトレース数が num_traces を超え、メモリ使用量が急騰した。
症状: Gateway Pod が順番に OOM Kill され、再起動を繰り返した。再起動した Pod にトラフィックが集中し、連鎖的に OOM が発生するドミノ現象が起きた。
復旧手順:
- 緊急対応として Tail Sampling を無効化し、probabilistic head sampling (10%) へ切り替えてトレースの流入量を即座に減らした。
- Gateway のメモリ limit を 4Gi から 8Gi へ増設し、replicas を 3 から 6 へ拡張した。
- decision_wait を 30 秒から 15 秒へ短縮し、num_traces を 200,000 に調整したうえで Tail Sampling を再有効化した。
教訓: Tail Sampling のメモリ使用量はトラフィックに線形に比例する。最大トラフィックのシナリオに対する負荷テストを必ず実施し、num_traces と decision_wait は保守的に設定する必要がある。
事例 2: Exporter キューの飽和によるデータ欠損
状況: Tempo バックエンドの Ingester がディスクフルによって応答が遅くなった。Collector の otlp/tempo Exporter で timeout エラーが発生し、sending queue が急速に埋まった。
症状: Exporter queue がいっぱいになり、新しいトレースデータがドロップされ始めた。otelcol_exporter_send_failed_spans メトリクスが急騰し、otelcol_exporter_queue_size が queue_capacity に達した。
復旧手順:
- Tempo Ingester のディスクを拡張し、問題となった Ingester Pod を再起動した。
- Collector の sending_queue サイズを 5,000 から 20,000 へ一時的に増やし、バッファリングの余裕を確保した。
- retry_on_failure の max_elapsed_time を 600 秒へ延ばし、バックエンドの復旧時間の間リトライを維持した。
教訓: sending_queue はバックエンドの一時的な障害に対する緩衝の役割しか果たさない。長時間のバックエンド障害ではキューが必ず飽和するため、バックエンドの監視と迅速な障害対応が根本的な解決策になる。重要なデータについては、Kafka を中間バッファとして使い永続性を保証するアーキテクチャも検討すべきだ。
事例 3: K8s Attributes Processor の権限不足で Pod メタデータが注入されない
状況: k8sattributes Processor を設定したが、namespace や pod name などのメタデータがテレメトリに注入されなかった。
症状: トレースとログで k8s.namespace.name、k8s.pod.name などの属性が空だった。Collector のログに "error": "forbidden" というメッセージが出力されていた。
復旧: Collector の ServiceAccount に、Pods、Namespaces、ReplicaSets に対する get、list、watch 権限を持つ ClusterRole をバインドして解決した。
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: otel-collector
rules:
- apiGroups: ['']
resources: ['pods', 'namespaces', 'nodes']
verbs: ['get', 'list', 'watch']
- apiGroups: ['apps']
resources: ['replicasets', 'deployments', 'statefulsets', 'daemonsets']
verbs: ['get', 'list', 'watch']
- apiGroups: ['batch']
resources: ['jobs', 'cronjobs']
verbs: ['get', 'list', 'watch']
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: otel-collector
subjects:
- kind: ServiceAccount
name: otel-collector-agent
namespace: observability
roleRef:
kind: ClusterRole
name: otel-collector
apiGroup: rbac.authorization.k8s.io
設定検証とテスト
プロダクションへデプロイする前に Collector 設定ファイルの妥当性を検証することが重要だ。設定ミスによる Collector の起動失敗は、テレメトリパイプライン全体の停止につながる。
# 設定ファイルの構文検証
otelcol validate --config=config.yaml
# ドライランモードでの起動テスト
otelcol --config=config.yaml --dry-run
# Docker を使ったローカルテスト
docker run --rm \
-v $(pwd)/config.yaml:/etc/otelcol/config.yaml \
otel/opentelemetry-collector-contrib:0.120.0 \
validate --config=/etc/otelcol/config.yaml
CI/CD パイプラインに設定検証のステップを組み込めば、誤った設定がプロダクションへデプロイされることを事前に防げる。Helm Chart を使う場合は、helm template でレンダリングしたあとに生成される ConfigMap の設定ファイルに対して validate を実行する。
高度な運用テクニック
マルチテナント環境でのルーティング
複数のチームが共有する Collector で、テナントごとにデータを分離して異なるバックエンドへ送信する必要がある場合は、routing Connector を活用する。
connectors:
routing:
table:
- statement: route() where attributes["team"] == "platform"
pipelines: [traces/platform]
- statement: route() where attributes["team"] == "payments"
pipelines: [traces/payments]
default_pipelines: [traces/default]
service:
pipelines:
traces/ingress:
receivers: [otlp]
processors: [memory_limiter, batch]
exporters: [routing]
traces/platform:
receivers: [routing]
processors: [batch]
exporters: [otlp/tempo-platform]
traces/payments:
receivers: [routing]
processors: [batch]
exporters: [otlp/tempo-payments]
traces/default:
receivers: [routing]
processors: [batch]
exporters: [otlp/tempo-default]
Collector 自体の監視ダッシュボードで押さえるべきパネル
運用向けの Grafana ダッシュボードに必ず含めるべき主要パネルの一覧だ。
- スループットパネル: 毎秒の受信/送信 Span、Metric Point、Log Record 数
- ドロップ率パネル: refused + dropped / accepted の比率 (0% が正常)
- Exporter キューパネル: 各 Exporter の queue_size と capacity の比率
- メモリパネル: プロセスの RSS メモリと memory_limiter のしきい値
- Exporter 遅延パネル: 送信所要時間の P50、P95、P99
- リトライパネル: retry 回数と失敗率
参考資料
- OpenTelemetry Collector Configuration -- 公式の設定リファレンス。Receiver、Processor、Exporter のすべてのオプションを参照できる。
- OpenTelemetry Collector Architecture -- 公式アーキテクチャドキュメント。内部のデータフローとコンポーネント間の相互作用を説明している。
- OpenTelemetry Collector Complete Guide - SigNoz -- SigNoz が提供する実践ガイド。さまざまな設定例とベンチマークを含む。
- OpenTelemetry Collector Guide - Dash0 -- Dash0 が提供する Collector 運用ガイド。デプロイパターンとプロダクション向けのヒントを扱う。
- Build Logs-to-Metrics Pipelines - Last9 -- ログからメトリクスを生成するパイプライン構築ガイド。Connector を活用したシグナル変換を説明する。