Upgrade to Pro — share decks privately, control downloads, hide ads and more …

OpenTelemetry x Datadog APM 多言語環境での計装戦略と実践 (削減前...

Sponsored · Your Podcast. Everywhere. Effortlessly. Share. Educate. Inspire. Entertain. You do you. We'll handle the rest.
Avatar for mekka mekka
June 06, 2026
24

OpenTelemetry x Datadog APM 多言語環境での計装戦略と実践 (削減前)/ instrumentation-strategies-and-practices-in-multilingual-environment-bef

Avatar for mekka

mekka

June 06, 2026

More Decks by mekka

Transcript

  1. ⾃⼰紹介 ⾒形 親久 Chikahisa Mikata 株式会社ログラス クラウド基盤チーム SREリード SIer にてアプリケーションエンジニアとしてキャリアをスタート。

    ToBのSaaS企業でのSRE組織の⽴ち上げを経て、2024年10⽉より株式会 社ログラスに参画。 現在は共通基盤部にて、開発組織への SRE の推進およびプラットフォー ム開発に取り組んでおり、SRE を⽂化として根付かせることをテーマに 活動しています。
  2. 3

  3. 4

  4. 5

  5. • 4⾔語‧マルチサービスの計装を OTel で統⼀した話 ◦ Before / 転機: なぜ計装統⼀が必要になったか ◦

    判断基準: OTel 採⽤を決めた軸 ◦ After: OTel 統⼀アーキテクチャの全体像 ◦ 計装の実際 / ハマりどころ 今⽇お話しすること
  6. Before ECS(Fargate起動) 時代の構成 項⽬ Kotlin(メイン) Rust(⼀部) Tracing dd-trace-java + @Trace

    OTel SDK + DD exporter Logging Logback → Fluent Bit → DD カスタム JSON Layer 172⾏ 伝搬⽅式 Datadog 形式(⾃動) DatadogPropagator(⼿動)
  7. Before • DD SDK がない⾔語で無理に合わせた結果 ◦ 128bit TraceId → 64bit

    ⼿動変換 ◦ ログに dd.trace_id をハードコード ◦ Propagator も Datadog 独⾃形式 → アプリがバックエンドの仕様に依存 Rust で何が起きていたか
  8. Before • Rust のアプリ汚染も、ローカルで Trace が⾒えないことも耐えれていた ◦ 2⾔語‧数サービスの規模ならなんとかなる ◦ TraceもDatadog

    に送ればちゃんと⾒える ◦ 認知負荷はあるが、なにより動いていた → この均衡が崩れる⽇が来る それでも回っていた
  9. 転機 • マルチプロダクト展開に向けた共通基盤の構築 ◦ EKS ベースの Platform Engineering へ転換 ◦

    モノリス → マイクロサービスへ再設計 • ⾔語構成も⼤きく変化 ◦ Go: BFF + ユースケース層の中核として新規参⼊ ◦ TypeScript: LLM Gateway / MCP / エージェント等 → 2⾔語 → 4⾔語、サービス数も⼤幅に増加 マルチプロダクト戦略と基盤刷新
  10. 転機 • ⾔語ごとに SDK の有無がバラバラ ◦ 統⼀基準がないと破綻 • ローカルで Trace

    を事前テストしたい ◦ 多数のサービスを DD に送って確認は現実的でない • 実装を統⼀したい ◦ ⾔語ごとに DD 形式への対応がバラバラでは管理不能 ペインの爆発
  11. 転機 • ペインの根本原因 ◦ ベンダー固有の計装が⾔語ごとにバラバラ ◦ SDK があったりなかったり、API も形式も統⼀されていない •

    業界標準 OpenTelemetry なら統⼀できるのでは? ◦ 全⾔語で同じ計装モデル(Traces / Logs / Metrics) ◦ W3C TraceContext で⾔語をまたいだ伝搬が標準化 ◦ OTLP でバックエンドを選ばない出⼒ → 本当に採⽤できるか? 4つの軸で検証した 計装の標準化という選択肢
  12. 判断基準 4⾔語の現実: SDK 対応状況 ⾔語 Datadog SDK OTel SDK Go

    あり あり(最も充実) Kotlin (JVM) あり(成熟) あり(Java agent) Rust なし あり(発展途上) TypeScript (Node) あり あり TypeScript (Browser) RUM のみ 未成熟
  13. 判断基準 4つの判断軸で評価した 判断軸 Datadog SDK OpenTelemetry ベンダー依存 アプリ層が DD に依存

    アプリ層はベンダ⾮依存 多⾔語対応 ⾔語ごとに SDK 差異あり 全⾔語で同じモデル 標準準拠 Datadog 独⾃形式 W3C / OTLP ローカル開発 DD に送るまで不可 otel-tui で⼿元確認
  14. 判断基準 • Profiler との統合 ◦ Endpoint Profiling (Trace⇔Profile 紐付け) は不可

    ◦ → 無理に紐付けず、OTel 側の対応を待つ判断 • 変換レイヤーの存在 ◦ OTel → DD Agent → Datadog の変換で問題切り分けが増える • SDK 成熟度のばらつき ◦ ⾔語間で仕様追従速度が異なる(後述のハマりどころ) • Datadog 固有機能の⼀部制限 ◦ ASM 等の DD SDK 前提機能は利⽤不可 → いずれも許容可能と判断し、ADR に記録して合意した OTel 移⾏のトレードオフ — 許容した制約
  15. 判断基準 • 3案を7項⽬で評価し、対策案 A を採択 ◦ A: OTel SDK 純正

    — ベンダー⾮依存 + 全⾔語統⼀ ◦ B: OTel API + dd-trace — Profiler 統合可だが Rust ⾮対応 ◦ C: dd-trace 純正 — 完全ベンダーロックイン → トレードオフを明⽂化し、チームで合意 ADR: 3つの対策案を厳密に⽐較
  16. 判断基準 • ADR の決定を実装レベルに落とし込み ◦ 全体構成図(Trace / Log / Profiler

    の経路) ◦ Go / Kotlin / TS / Rust / Python の初期化コード ◦ ログ経路‧DD Agent 設定まで網羅 • 開発者はパターンに沿って実装するだけ ◦ OTel の詳細を知らなくても統⼀された計装を実現できる → この型をベースに全サービスの計装統⼀を実現した Design Doc: 4⾔語のサンプル実装で展開
  17. After システム構成(移⾏後): 4⾔語 × マルチサービス ⾔語 役割 備考 TypeScript (Browser)

    フロントエンド Next.js Go BFF + ユースケース層 API Gateway / サービス Kotlin (JVM) ドメインロジック コアサービス Rust クエリ‧演算‧ETL 3サービス TypeScript (Node.js) LLM Gateway / MCP / エージェント 4サービス
  18. After • OTel Browser SDK にはまだない機能を RUM が提供 ◦ セッションリプレイ

    / Core Web Vitals / ユーザーアクション追跡 • RUM は W3C TraceContext (traceparent) に対応 ◦ ブラウザ → バックエンドまで1本のトレースで接続 ◦ SDK v5+ ではデフォルトで有効 → 実際の APM Trace(右図: ブラウザから DB まで⼀貫) フロントエンドは Datadog RUM — それでもトレースは繋がる
  19. After • アプリは Datadog の存在を知らない ◦ OTLP エンドポイントを環境変数で指定するだけ ◦ サービス名‧環境名も

    OTel 標準の属性で設定 • 開発者の意識すべきこと ◦ 送り先の名前が datadog でも jaeger でも同じ設定 ◦ ベンダー固有の SDK は不要 — import も設定もゼロ → アプリ層はベンダーに依存しない アプリが意識すること: OTel の環境変数だけ
  20. After • Agent 側の設定: OTLP 受信を有効化するだけ ◦ otlp.receiver.protocols.grpc.enabled: true ◦

    → Traces / Logs / Metrics すべて受信可能に • Datadog がその先を担う ◦ APM ⇔ ログの1クリック遷移 ◦ K8s インフラメトリクスの⾃動収集 ◦ Error Tracking / 統合ダッシュボード → OTel はデータの出し⽅、Datadog はデータの使い⽅ Datadog Agent: OTLP を受けてその先を担う
  21. 計装の実際 ― Trace ⾃動計装: ⾔語ランタイムで⽅式が決まる 項⽬ Kotlin (JVM) TypeScript Go

    Rust ⽅式 バイトコード操作 モンキーパッチ contrib wrap Tower + Bridge アプリ変更 不要 不要 必要(定型) 必要(定型) 出⼒ OTLP gRPC OTLP gRPC OTLP gRPC OTLP gRPC
  22. 計装の実際 ― Trace 明⽰的スパン: Before → After ⾔語 Before After

    変化 Kotlin @Trace (DD 独⾃) @WithSpan (OTel 標準) ベンダー依存を排除 Rust instrument + DD exporter #[instrument] (OTel 直結) exporter 変換が不要に Go — (新規) tracer.Start(ctx, name) OTel 標準で新規構築 TypeScript — (新規) @Trace() デコレーター OTel 標準で新規構築
  23. 計装の実際 ― Log ログ転送: Before → After 転送経路: Fluent Bit

    サイドカー → stdout + OTLP の2経路へ ローカル確認: DD に送るまで不可 → kubectl logs で即確認 ⾔語 Before After 変化 Kotlin Logback → Fluent Bit → DD Logback → stdout / OTLP 並列 Agent が⾃動追加 Rust カスタム JSON Layer 172⾏ fmt::layer + TracingBridge DD 形式への依存排除 Go — (新規) slog → stdout / multiHandler OTel 標準で新規構築 TypeScript — (新規) Pino → stdout / Pino patch SDK 初期化で設定
  24. 計装の実際 ― Log • 2経路だと Datadog にログが重複する ◦ OTLP 経由で

    Datadog に送信済み ◦ Datadog Agent も stdout を⾃動収集 → 重複 • Pod アノテーションで stdout 収集を除外 ◦ ad.datadoghq.com/<name>.logs_exclude: "true" → OTel 経路のみで Datadog に到達する ログ⼆重取り込み防⽌: Pod 側の制御
  25. 計装の実際 ― Log • 開発者は logs_exclude を⼿で書かない ◦ 共通 Helm

    チャートが⾃動判定‧⾃動付与 • 仕組み ◦ 全コンテナにデフォルトで logs_exclude: true を付与 ◦ OTel ⾮対応のコンテナだけ stdoutContainers で例外宣⾔ ◦ 例外以外は⾃動で stdout 収集が除外される → 開発者は何も意識しない。安全側に倒す設計 共通 Helm チャートによる⾃動制御
  26. 計装の実際 ― Log OTLP 経路の追加⽅法も⾔語特性で異なる ⾔語 stdout 経路 OTLP 追加⽅法

    明⽰性 Kotlin Logback ConsoleAppender Agent が appender ⾃動追加 完全に暗黙 TypeScript Pino → stdout PinoInstrumentation がパッチ SDK 初期化で設定 Go slog.JSONHandler multiHandler に並列登録 コードで明⽰ Rust fmt::layer().json() TracingBridge を Registry に積む コードで明⽰
  27. ハマりどころ • 背景 ◦ Gradle の shadowJar プラグインで fat JAR

    を作成 ◦ クラスパス競合を避けるため grpc-netty-shaded を relocate • 何が起きたか ◦ OTel agent はクラス名のパターンで gRPC を検出して⾃動計装する ◦ relocate でパッケージ名が変わり、検出できず⾃動計装がスキップ ◦ gRPC の Trace が出ないがエラーも出ない事象が発⽣(サイレント) • 対処 ◦ gRPC interceptor を⼿動登録して⾃動計装を補完 shadowJar が Trace ⾃動計装を壊す (Kotlin)
  28. ハマりどころ • 背景 ◦ W3C TraceContext Level 2 で random

    flag (0x02) が追加(trace-id がランダム⽣成であることを⽰すフラグ) ◦ 仕様上、未知の flag は無視して伝搬すべき (MUST) • 何が起きたか ◦ Kotlin の OTel agent は Level 2 準拠 → random flag を付与 ◦ Rust の OTel SDK は Level 2 未対応 → 未知 flag として拒否 ◦ → Kotlin→Rust 間で Trace が切断される • 対処と学び ◦ upstream 修正版を patch.crates-io で適⽤ ◦ ⾔語ごとに OTel SDK の仕様追従速度が異なる。多⾔語では要注意 W3C TraceContext flags の⾮互換 (Kotlin→Rust)
  29. まとめ • OTel 統⼀を検討すべき状況 ◦ 複数⾔語で SDK 対応にばらつきがある ◦ ローカルで

    Trace / Log を事前確認したい • OTel が担う領域 ◦ Trace‧Log の計装をアプリ層で標準化 ◦ W3C TraceContext で全レイヤーを接続 • Datadog が担う領域 — OTel では代替できない価値 ◦ OTLP ネイティブ受信 → APM ⇔ Log の1クリック遷移 ◦ RUM: セッションリプレイ / Core Web Vitals / ユーザー⾏動追跡 ◦ K8s インフラメトリクス × APM の統合ダッシュボード → OTel で計装を標準化したからこそ、Datadog の分析⼒が最⼤限活きる まとめ