telemetrygenでOpenTelemetry Collectorの性能を分析する
こんにちは。Splunk Observability Cloudの導入支援を行っている kntr_nkgm です。
OpenTelemetry Advent Calendar 2025の記事です。
はじめに
OpenTelemetry Collectorを運用していると、パフォーマンス調査やキャパシティプランニングが必要になるケースがあります。本番環境に導入する前に「どれぐらいの負荷に耐えられるのか」を把握したい、「今の設定でボトルネックはないか」を確認したいといった場面です。
今回は、OpenTelemetryプロジェクト公式の負荷生成ツール telemetrygen を使って、OTel Collectorに負荷をかけ、その挙動を確認してみます。
telemetrygen とは
telemetrygen は、OpenTelemetryプロジェクト公式の負荷生成ツールです。Traces、Metrics、Logsの3つのシグナルタイプのテストデータを生成できます。
主な用途としては以下が挙げられます:
- OTel Collectorのパフォーマンステスト: 本番環境に近い負荷をかけて性能を検証
- 開発・検証環境でのテストデータ生成: ダッシュボードやアラートの動作確認
- デモ環境の構築: 継続的にデータを流し続けることでリアルな環境を再現
OTLPプロトコル(gRPCまたはHTTP)を使ってデータを送信するため、OpenTelemetry Collectorはもちろん、OTLPをサポートする任意のバックエンドに対してデータを送信して処理を確認することもできます。
インストール
telemetrygenのインストールは非常にシンプルです。Goがインストール済みの環境であれば、以下のコマンドでインストールできます。
go install github.com/open-telemetry/opentelemetry-collector-contrib/cmd/telemetrygen@latest
インストール後、ヘルプを確認してみましょう。
telemetrygen --help
オプションは以下のように表示されます。
Telemetrygen simulates a client generating traces, metrics, and logs
Usage:
telemetrygen [command]
Examples:
telemetrygen traces
telemetrygen metrics
telemetrygen logs
Available Commands:
help Help about any command
logs Simulates a client generating metrics. (Stability level: development)
metrics Simulates a client generating metrics. (Stability level: development)
traces Simulates a client generating traces. (Stability level: alpha)
Flags:
-h, --help help for telemetrygen
Use "telemetrygen [command] --help" for more information about a command.
Dockerを使う場合は、コンテナイメージも提供されています。
docker pull ghcr.io/open-telemetry/opentelemetry-collector-contrib/telemetrygen:latest
基本的な使い方
telemetrygenは、サブコマンドで生成するシグナルタイプを指定します。
主なオプション
よく使うオプションをまとめておきます。
| オプション | 説明 |
|---|---|
--otlp-endpoint |
OTel Collectorのエンドポイント(デフォルト: localhost:4317) |
--otlp-insecure |
TLSなしで接続(開発環境用) |
--otlp-http |
HTTPエクスポーターを使用(デフォルトはgRPC) |
--rate |
1秒あたりの生成数 |
--duration |
実行時間(例: 60s, 5m, 1h)。infで無限に実行 |
--traces / --metrics / --logs
|
生成する総数(durationと併用しない) |
--workers |
並列ワーカー数(デフォルト: 1) |
Tracesの生成
最もシンプルな例として、10個のトレースを生成してみます。
telemetrygen traces --otlp-endpoint localhost:4317 --otlp-insecure --traces 10
ターミナル上には以下のようなログが出力されます。
2025-12-25T07:45:27.878Z INFO traces/traces.go:53 starting gRPC exporter
2025-12-25T07:45:27.878Z INFO grpclog/component.go:69 [core] original dial target is: "localhost:4317" {"grpc_log": true}
2025-12-25T07:45:27.878Z INFO channelz/trace.go:200 [core] [Channel #1] Channel created for target "localhost:4317" {"grpc_log": true}
2025-12-25T07:45:27.878Z INFO channelz/trace.go:200 [core] [Channel #1] parsed dial target is: resolver.Target{URL:url.URL{Scheme:"dns", Opaque:"", User:(*url.Userinfo)(nil), Host:"", Path:"/localhost:4317", RawPath:"", OmitHost:false, ForceQuery:false, RawQuery:"", Fragment:"", RawFragment:""}} {"grpc_log": true}
2025-12-25T07:45:27.878Z INFO channelz/trace.go:200 [core] [Channel #1] Channel authority set to "localhost:4317" {"grpc_log": true}
2025-12-25T07:45:27.878Z INFO traces/traces.go:119 generation of traces is limited {"per-second": 1}
2025-12-25T07:45:29.879Z INFO channelz/trace.go:200 [core] [Channel #1] Channel exiting idle mode {"grpc_log": true}
2025-12-25T07:45:29.885Z INFO channelz/trace.go:200 [core] [Channel #1] Resolver state updated: {
"Addresses": [
{
"Addr": "127.0.0.1:4317",
"ServerName": "",
"Attributes": null,
"BalancerAttributes": null,
"Metadata": null
}
],
"Endpoints": [
{
"Addresses": [
{
"Addr": "127.0.0.1:4317",
"ServerName": "",
"Attributes": null,
"BalancerAttributes": null,
"Metadata": null
}
],
"Attributes": null
}
],
"ServiceConfig": null,
"Attributes": null
} (resolver returned new addresses) {"grpc_log": true}
2025-12-25T07:45:29.885Z INFO channelz/trace.go:200 [core] [Channel #1] Channel Connectivity change to CONNECTING {"grpc_log": true}
2025-12-25T07:45:29.885Z INFO channelz/trace.go:200 [core] [Channel #1 SubChannel #2] Subchannel created {"grpc_log": true}
2025-12-25T07:45:29.885Z INFO channelz/trace.go:200 [core] [Channel #1 SubChannel #2] Subchannel Connectivity change to CONNECTING {"grpc_log": true}
2025-12-25T07:45:29.885Z INFO channelz/trace.go:200 [core] [Channel #1 SubChannel #2] Subchannel picks a new address "127.0.0.1:4317" to connect {"grpc_log": true}
2025-12-25T07:45:29.886Z INFO channelz/trace.go:200 [core] [Channel #1 SubChannel #2] Subchannel Connectivity change to READY {"grpc_log": true}
2025-12-25T07:45:29.886Z INFO grpclog/prefix_logger.go:42 [pick-first-leaf-lb] [pick-first-leaf-lb 0xc000426000] SubConn 0xc0000ee500 reported connectivity state READY and the health listener is disabled. Transitioning SubConn to READY. {"grpc_log": true}
2025-12-25T07:45:29.886Z INFO channelz/trace.go:200 [core] [Channel #1] Channel Connectivity change to READY {"grpc_log": true}
2025-12-25T07:45:46.879Z INFO traces/worker.go:180 traces generated {"worker": 0, "traces": 10}
2025-12-25T07:45:46.879Z INFO traces/traces.go:75 stop the batch span processor
2025-12-25T07:45:46.880Z INFO channelz/trace.go:200 [core] [Channel #1] Channel Connectivity change to SHUTDOWN {"grpc_log": true}
2025-12-25T07:45:46.880Z INFO channelz/trace.go:200 [core] [Channel #1] Closing the name resolver {"grpc_log": true}
2025-12-25T07:45:46.880Z INFO channelz/trace.go:200 [core] [Channel #1] ccBalancerWrapper: closing {"grpc_log": true}
2025-12-25T07:45:46.880Z INFO channelz/trace.go:200 [core] [Channel #1 SubChannel #2] Subchannel Connectivity change to SHUTDOWN {"grpc_log": true}
2025-12-25T07:45:46.880Z INFO channelz/trace.go:200 [core] [Channel #1 SubChannel #2] Subchannel deleted {"grpc_log": true}
2025-12-25T07:45:46.880Z INFO grpclog/prefix_logger.go:42 [transport] [client-transport 0xc0001f0908] Closing: rpc error: code = Canceled desc = grpc: the client connection is closing {"grpc_log": true}
2025-12-25T07:45:46.880Z INFO grpclog/prefix_logger.go:42 [transport] [client-transport 0xc0001f0908] loopyWriter exiting with error: rpc error: code = Canceled desc = grpc: the client connection is closing {"grpc_log": true}
2025-12-25T07:45:46.880Z INFO channelz/trace.go:200 [core] [Channel #1] Channel deleted {"grpc_log": true}
2025-12-25T07:45:46.880Z INFO traces/traces.go:65 stopping the exporter
継続的に負荷をかける場合は、--rateと--durationオプションを使います。以下の例では、1秒間に10トレースを60秒間生成し続けます。
telemetrygen traces --otlp-endpoint localhost:4317 --otlp-insecure --rate 10 --duration 60s
メトリクスやログも同様です。
telemetrygen metrics --otlp-endpoint localhost:4317 --otlp-insecure --rate 100 --duration 60s
telemetrygen logs --otlp-endpoint localhost:4317 --otlp-insecure --rate 50 --duration 60s
実際に負荷をかけてみる
それでは実際に、telemetrygenを使ってOTel Collectorに負荷をかけ、その挙動を確認してみましょう。
今回は検証用のLinuxサーバにSplunk Distro of OpenTelemetry Collectorを導入します。Splunk Distro of OTel Collectorは、デフォルトでOTel Collectorの内部メトリクスをSplunk Observability Cloudに送信するようになっています。
telemetrygenで負荷をかける
それでは、telemetrygenを使ってOTel Collectorに負荷をかけてみます。今回は1秒間に1000トレースを生成してみましょう。
telemetrygen traces --otlp-endpoint localhost:4317 --otlp-insecure --rate 1000 --duration 300s
Splunk Observability Cloudには、OTel Collectorのパフォーマンスを監視するためのデフォルトダッシュボードが用意されています。
「Dashboards」から「OpenTelemetry Collector」を選択すると、各Collectorのメトリクスを確認できます。

負荷をかけてみると、OTel CollectorのOTLPレシーバーが約1000 spans/secでトレースを受け入れ、Exporterがotlphttpとsignalfxのエンドポイントにそれぞれ送信していることが確認できます。

ドロップされたスパンも確認しましょう。Sending queue dropped spansとDropped spans per processorのグラフを見ると、どちらも0で推移しており、データロスが発生していないことがわかります。

Secondary monitoringセクションでは、Sending queue lengthを確認できます。キューの長さは0で安定しており、Collectorが余裕を持って処理できていることを示しています。

エラー関連のメトリクスも確認します。Exporter - Send failed rate(送信失敗率)とReceivers - Refusal rate(受信拒否率)のどちらも0で推移しており、正常に動作していることが確認できます。

複数のワーカーを使って並列に送信することで、より高い負荷をかけることもできます。
例えばトレースを秒間10000件、10のWorkerから送信してみます。
telemetrygen traces --otlp-endpoint localhost:4317 --otlp-insecure --rate 10000 --duration 300s --workers 10
すると、Sending Queue Lengthのチャートが1を記録するタイミングが出てきましたね。キューにデータが積まれたものの、その後すぐに送信されているように見えます。

※右のチャートは別のテレメトリに関するデータの残りなのでここでは無視してください!
まとめ
今回は、OpenTelemetry公式の負荷生成ツールtelemetrygenを使って、OTel Collectorのパフォーマンスを分析する方法を紹介しました。
telemetrygenは非常にシンプルなツールですが、OTel Collectorの性能検証やキャパシティプランニングには十分な機能を備えています。負荷を変えながら内部メトリクスを観察することで、ボトルネックの特定やチューニングのポイントを見つけることができます。
それでは、良い年末を!
Discussion