OpenTelemetryのKubernetes構成|Operator導入とCollector二層設計
KubernetesでOpenTelemetryを動かすとき、詰まるのはSDKの書き方ではなく置き場所の設計です。Collectorをノードごとに置くのか、クラスタに1本だけ置くのか。Pod名やNamespaceを誰が付け、自動計装をどこまで広げるのか。ここを決めずにマニフェストを当てると、トレースにk8s.pod.nameが付かないまま検索できないデータが溜まります。Operator導入とCRDの責務分担、計装の注入、agent層とgateway層の切り替え基準、RBACとリソース見積りを扱います。
まとめ|OpenTelemetryのKubernetes構成で先に決める三点
K8s上のOTel構成で最初に決めるのは三点です。①Operatorを入れるか素のマニフェスト管理で済ませるか、②Collectorをagent(DaemonSet)とgateway(Deployment)の二層にするか単層で足りるか、③自動計装をNamespace単位でどこまで広げるか。ここが決まればreceiverとprocessorの配置もRBACの範囲も自動的に決まります。
2026年8月時点の推奨形は、cert-manager導入済みのクラスタにOperator 0.15x系を入れ、DaemonSetのagentにOTLP・kubeletstats・filelogとk8sattributes processorを持たせ、Deploymentのgatewayにk8s_cluster receiverを単一レプリカで置く二層構成。テールサンプリングも送信先の振り分けも不要でノード数が十数台までなら単層で足ります。リソースはmemory_limiterを先頭へ置き、agent側とgateway側で別々に見積もる。共通値で配ると片方が必ず過剰になります。
OpenTelemetry OperatorのインストールとCRD三種の役割分担
Operatorは配布と計装の注入を宣言で扱えるようにするものです。何が増えるのかを把握してから入れます。
cert-manager前提とOperator v0.157系の導入手順
Operatorはadmission webhookを使うため、TLS証明書を発行するcert-managerがクラスタに入っていることが前提になります。入れずにマニフェストを当てると、Operator自体は起動してもCRの検証やPodへの注入が動かず、切り分けに時間を取られる。導入はkubectl apply -fで公式マニフェストを当てるのが最短です。GitHub Releases APIで実測した最新版はv0.157.0(2026年8月13日公開)、直前がv0.156.0(7月14日)とv0.155.0(7月13日)で、月1〜2回のペースでマイナーが刻まれています。latest指定のURLをCIに組み込むと更新のたび意図しない版へ上がるため、タグ固定にする。Operatorは配下のCollectorも自動アップグレードします。
三種のCRDが分けるCollector管理と計装注入の責務範囲
Operatorが持ち込むCRDは役割が分かれます。OpenTelemetryCollectorはCollectorの配置と設定、Instrumentationはアプリへ注入する自動計装の内容、OpAMPBridgeは遠隔設定管理をつなぐもの。日常的に触るのは前の2つです。この分離があるので、exporterの送信先を差し替えてもInstrumentationには触らず、サンプリング率だけ変えたいときはInstrumentationだけを更新できる。OTLPやシグナルの前提はOpenTelemetry(OTel)とは|3つのシグナル・OTLP・Collectorの違いで押さえてから戻ると読みやすくなります。
Instrumentation CRDでゼロコード自動計装を注入する手順と制約
自動計装はアノテーション1行で入りますが、動かない原因はほぼ順序と対応言語に集約されます。
言語別アノテーションとinit containerの注入の仕組み
Podに付けるアノテーションはinstrumentation.opentelemetry.io配下に言語ごとに分かれ、inject-java・inject-nodejs・inject-python・inject-dotnet・inject-go、SDKのみのinject-sdkの6種です。値は3通りで、"true"は同一Namespaceの既定名、名前だけならその名のリソース、スラッシュ区切りなら別Namespaceの参照になります。Podが起動するとwebhookがopentelemetry-auto-instrumentationという名前のinit containerを差し込み、エージェント本体をアプリコンテナへコピーする。ただしこの方式が使えるのはJava・Python・Node.js・.NET・SDKだけで、Go・Apache HTTPD・NGINXは非対応です。GoはeBPFエージェントをサイドカーで動かしOTEL_GO_AUTO_TARGET_EXEの設定が必須で、マルチコンテナPodにも対応しません。
Instrumentation リソースを先にデプロイする順序の制約
注入が起きない原因で最も多いのがデプロイ順です。Instrumentationリソースはアプリより先にクラスタへ入っている必要があり、逆順にするとinit containerが作られません。Podが起動する瞬間にwebhookが参照先を探す仕様のため、後から追加しても既存Podには遡って効かない。この制約はGitOps運用で表面化します。同一のApplicationにCRとDeploymentを同居させると適用順が保証されず、日によって注入が入ったり入らなかったりする。CRは先行のsync waveへ分けておきます。
sampler・propagators・exporterの指定と既定の落とし穴
Instrumentationのspecで実務上効くのは3フィールドです。exporter.endpointはCollectorのOTLP受け口を指し、CRD仕様ではhttpsスキーマを書いた場合にTLS設定の指定が必須になります。propagatorsの値はそのままOTEL_PROPAGATORS環境変数へ渡されるため、既存システムがB3を使っているのに既定のままにすると、境界でトレースが切れて別IDとして記録される。samplerはtypeとargumentの組で指定し、parentbased_traceidratioのargumentは0から1の数値です。0.25なら4分の1を採取します。どこで削るかの設計はトレースサンプリングの仕組みとサンプリング率の決め方で扱っています。
Collectorのagent層とgateway層で分けるreceiverの配置
CollectorのCRDはspec.modeにdeployment・daemonset・statefulset・sidecarの4値を取り、この選択がデータの流れ方を決めます。
agent層をDaemonSetで置く理由とノード局所の収集範囲
DaemonSetで置くagentは、そのノードのデータだけを扱います。ノード局所でしか取れないものがあるからこの層が要る。kubeletstats receiverはkubelet APIを叩くためDeploymentでは自分の載るノードしか取れず、RBACにはノードのstatsサブリソースへのget・watch・listが必須で、認証はauth_type: serviceAccountを使います。この権限を落とすと403で収集だけが静かに止まる。収集間隔の既定はcollection_interval: 10s、metric_groupsはnode・pod・containerから選びますが、containerはPod内のコンテナ数ぶんの系列を作るため容量計画目的なら外せます。ログの受け口の組み方はOpenTelemetryのログ実装|直接送信とfilelog receiverの選び分けで整理しています。
gateway層に寄せるk8s_cluster receiverと単一レプリカ
gatewayはクラスタ全体のデータを1か所へ集めてから外へ出す層です。置く理由は3つ。トレース全体が揃わないと判定できないテールサンプリングがagent層では成立しないこと、送信先の認証情報を1か所に閉じ込められること、レート制限に対して送出のキューと再送を集約して当たれることです。ノードの状態・Podのフェーズ・コンテナ再起動回数を集めるk8s_cluster receiverもこの層に置く。収集対象がクラスタ単位なので、DaemonSetに置いても複数レプリカにしても同じ値が重複して届きます。この受信を持つCollectorはレプリカ1本に固定し、k8sobjects receiverも単一に置く。
Collector単層で足りるクラスタ規模と二層へ切り替える目安
二層が常に正解ではありません。単層で足りる条件は明確です。テールサンプリングを使わず、送信先が1つで、ノード数が十数台まで。この範囲ならDaemonSet1層にk8s_cluster用の小さいDeploymentを足すだけで回ります。切り替えの目安は、送信先が2つ以上に増えたとき、テールサンプリングの要件が出たとき、APIキーをノードごとに配りたくなくなったときの3つ。判断軸は台数ではなく、集約が必要な処理があるかどうかです。
k8sattributes processorでPod属性を付与する設定とRBAC
公式は、アプリのデータを受け取るCollectorにk8sattributesを必ず入れるべきだと明記しています。無ければどのPodのトレースか分かりません。
pod_associationで送信元Podを特定する三通りの設定
このprocessorは、届いたデータの送信元IPやリソース属性からPodを逆引きしてメタデータを付けます。手がかりを決めるのがpod_associationで、送信元IP、k8s.pod.ip、k8s.pod.uidの3通りが実務で使われる。agent層をDaemonSetで置く構成なら送信元IPでほぼ確実に引けます。gateway層では途中でLoad Balancerを挟むと送信元IPが書き換わるため、Downward APIでPod IPを環境変数へ渡しOTEL_RESOURCE_ATTRIBUTES経由で属性として送る形にします。二層で両方に入れても属性は上書きされません。
ClusterRoleで与えるpods・namespacesの参照権限
k8sattributesはKubernetes APIを参照するため、ServiceAccountに読み取り権限が要ります。公式が挙げる必須権限は、podsとnamespacesに対するget・watch・list、replicasetsに対するget・list・watch。replicasetsが必要なのは、PodからDeployment名を辿るのにReplicaSetを1段挟むためで、ここを落とすとk8s.deployment.nameだけが付きません。Roleでも動きますが、他のNamespaceのPodを逆引きできず属性の欠けたデータが混ざる。欠落はダッシュボードでは気づきにくいので、導入直後にNamespaceをまたいだトレースを1本流して確認します。
既定で付与される属性と自前ラベルを足すextract節の設定値
設定を書かなくても付くのはk8s.namespace.name・k8s.pod.name・k8s.pod.uidの3つで、実務では足りません。チーム名やサービス階層をPodラベルで管理しているなら、extract節でラベルとアノテーションを追加属性として引き出します。注意するのは増やしすぎること。属性1つが取りうる値の数だけ時系列が増えるため、デプロイのたびに変わるハッシュ値のようなラベルを引き出すと課金が跳ねる。見積り方はOpenTelemetryのメトリクス実装|計器選定とカーディナリティ設計で扱っています。
Collectorのリソース消費見積りとmemory_limiterの設定値
Collectorが落ちる原因の大半はメモリです。既定を知らないまま入れると、保護が実質無効のまま動きます。
memory_limiterのlimit_percentageと投入順の決まり
memory_limiterの既定値は、check_intervalが0s、limit_mibが0、limit_percentageが0。つまり何も書かなければ制限は掛かりません。spike_limit_mibとspike_limit_percentageはそれぞれの20%が既定で、ソフトリミットはハードリミットからスパイク分を引いた値になります。limit_mib: 4000・spike_limit_mib: 800ならソフトリミットは3200MiBです。配置はパイプラインの先頭。先頭に置くとreceiverへバックプレッシャーが返り、データを捨てる前に受信側を止められる。あわせてGOMEMLIMITをハードリミットの80%に設定します。
agent側とgateway側で変わるCPUとメモリの見積り方
2つの層は負荷のかかり方が違うため、同じrequests/limitsを配ると片方が必ず過剰になります。
| 観点 | agent(DaemonSet) | gateway(Deployment) |
|---|---|---|
| 負荷の決まり方 | ノードあたりPod密度 | クラスタ総スループット |
| スケール方法 | ノード追加で自動増 | レプリカ数の調整 |
| メモリの主因 | filelogのバッファ | batchのキュー滞留 |
| 落ちたときの影響 | そのノードのみ欠測 | クラスタ全体が欠測 |
| 単一障害点 | ならない | なる |
gatewayが単一障害点になる以上、limitsを絞りすぎてOOMKilledを繰り返す状態が最も避けたい形です。初期値はメモリを広めに取り、memory_limiterで頭を押さえてから実測で下げます。
batch processorのsend_batch_sizeとtimeoutの調整
batch processorの既定はsend_batch_sizeが8192、timeoutが200ms、send_batch_max_sizeが0(上限なし)です。send_batch_sizeは送出のトリガーであって実サイズを制限しないため、上限を掛けるならsend_batch_max_sizeを別に指定する。バックエンドにサイズ上限があるなら、設定しないと大きなバッチが弾かれます。配置はmemory_limiterとサンプリング系processorの後ろ。timeoutを0にするとsend_batch_sizeが無視されるため、低遅延を狙って0にするとバッチがほぼ効かなくなる。遅延を詰めるなら50msなどの小さい値にします。
OpenTelemetry OperatorをK8sで採用する条件と見送る場面
採用可否を条件付きで言い切ります。外したほうが運用の軽くなるクラスタは確実にあります。
Operatorを入れるべきクラスタの条件とノード数のしきい値
入れる価値があるのは次の条件を満たすときです。計装対象のDeploymentが十数本を超え、そのうち複数がJava・Python・Node.js・.NETのいずれかで、Dockerfileを触らずに計装を入れたい。この3つが揃えば、アノテーション1行で計装を配れる効果がcert-managerとCRD保守のコストを上回ります。ノード数はしきい値になりません。3ノードでもDeploymentが30本ならOperatorは効き、50ノードでもアプリが2本ならHelm直管理で十分です。
Helmの直管理で十分な場面とOperatorが過剰になる条件
見送るべき場面をはっきり挙げます。主要言語がGoのクラスタでは、init container方式に非対応でマルチコンテナPodも扱えない以上、自動計装の恩恵がほぼ得られません。Collector管理だけのためにcert-managerとwebhookを抱えるのは割に合わないため、Helm直管理を選ぶ。もう1つは、変更を全てGitOpsで統制していて実行時のPod書き換えを避けたい場合です。webhookが挟まるとGitのマニフェストと実際に動くPodの定義が一致せず、差分検知が濁ります。第三に、アプリが数本の小規模構成ではCRD3種とwebhookの学習コストが導入効果を上回ります。
自動計装を全namespaceへ一括適用して失敗する典型パターン
やりがちな失敗は、Instrumentationを全Namespaceへ一括で当てて全アプリにアノテーションを配ることです。壊れ方は3通り。1つ目は起動時間の悪化で、Javaエージェントの読み込みが加わってPodの起動が数秒から十数秒遅くなり、readinessProbeのタイムアウトでCrashLoopBackOffに入ります。2つ目はデータ量の膨張で、バッチジョブやサイドカーまで計装されて想定の数倍のスパンが流れる。3つ目は既存のAPMエージェントとの二重計装です。対象Namespaceを1つ選んでDeployment単位で有効化し、起動時間とスパン量を実測してから増やす。kube-systemと監視系は最初から除外します。二重計測の整理はAPM(アプリケーション性能監視)とは|3種の監視データとOpenTelemetry計装にまとめました。載せ替えを外部と組んで進めるなら、保守運用・内製化支援が監視基盤の構築と内製化を扱っています。
OpenTelemetryのKubernetes構成に関するよくある質問
導入時に判断が分かれやすい点を、仕様の実測値に基づいて整理します。
OpenTelemetry Operatorは必須ですか?
必須ではありません。CollectorはHelmチャートや素のマニフェストでもデプロイでき、計装もSDKをアプリへ組み込めば動きます。Operatorが効くのは、アプリのイメージを触らずに自動計装を配りたい場合と、Collectorの設定を複数チームが独立して管理する場合の2つ。Goが主要言語のクラスタや小規模構成では保守コストのほうが上回ります。
CollectorはDaemonSetとDeploymentのどちらで置くべきですか?
両方置く二層構成が標準です。kubeletstatsとfilelogはノード局所でしか収集できないためDaemonSetのagentへ、クラスタ全体を対象とするk8s_clusterはDeploymentのgatewayへ単一レプリカで置きます。テールサンプリングが不要で送信先が1つ、ノード数が十数台までならagent1層に小さいDeploymentを1本足すだけで足ります。
自動計装が注入されないときは何を確認しますか?
順に3点を見ます。まずkubectl describe podでopentelemetry-auto-instrumentationという名前のinit containerが入っているかを確認する。入っていなければ注入自体が発火していません。次にInstrumentationリソースがアプリより先にデプロイされているかを確認します。逆順だとinit containerは作られず、Podを作り直すまで効かない。最後に対象言語です。Go・Apache HTTPD・NGINXは非対応です。
k8sattributes processorのRBACには何が必要ですか?
ServiceAccountに、podsとnamespacesへのget・watch・list、replicasetsへのget・list・watchを与えます。replicasetsはPodからDeployment名を辿るのに1段挟むために要るもので、落とすとk8s.deployment.nameだけが付きません。Namespaceをまたぐ逆引きにはClusterRoleが要ります。
Collectorのメモリはどう見積もればよいですか?
agent層とgateway層で別々に見積もります。agentはノードあたりのPod密度とfilelogのバッファで決まり、gatewayはクラスタ総スループットとbatchのキュー滞留で決まる。共通してmemory_limiterを先頭へ置き、limit_mibまたはlimit_percentageを明示します。既定はどちらも0で制限が掛からないためです。
関連記事
- OpenTelemetry(OTel)とは|3つのシグナル・OTLP・Collector・Prometheusとの違いを解説:前提となる定義とOTLPの位置づけ。
- AWSオブザーバビリティの実装|ADOT・CloudWatch・Container Insightsで計装する構成:AWS固有に寄せた場合の構成差。
- Jaegerとは?v2でOpenTelemetryを取り込んだ分散トレーシングの仕組み:gatewayの送信先となる基盤の候補。
- Grafana Tempoとは?分散トレーシングの仕組み・TraceQL・導入判断を実装者向けに解説:同じく保存先の選定材料。
- OpenTelemetryのメトリクス実装|計器選定とカーディナリティ設計:属性増加時の系列数の見積り。