---
title: OBI による分散トレース
linkTitle: 分散トレース
description: OBI の分散トレースサポートについて学びます。
weight: 22
default_lang_commit: 84d7cf19e9f7f44ea889f8e148b37bc71116ef31
cSpell:ignore: asyncio chanrecv chansend HPACK uvloop
---

## はじめに {#introduction}

OBI はアプリケーションの分散トレースをサポートしますが、いくつかの制限とカーネルバージョンの制約があります。

分散トレーシングは [W3C `traceparent`](https://www.w3.org/TR/trace-context/) ヘッダー値の伝搬を通じて実装されています。
OBI は受信コンテキストを自動的に読み取ります。
送信側のネットワークレベルでのコンテキスト伝搬はデフォルトで無効になっており、以下の説明にしたがって有効にする必要があります。

適用される伝搬モードを有効にすると、OBI は受信トレースコンテキストを読み取り、プログラムの実行を追跡し、送信される HTTP または gRPC リクエストに `traceparent` を追加します。
アプリケーションがすでに `traceparent` を追加している場合、OBI は自前で生成したコンテキストではなく、その値を使用します。
受信コンテキストが見つからない場合、OBI は W3C 仕様にしたがって新たに生成します。

スレッド、goroutine、タスク、イベントループをまたいで処理が移動する際に OBI がどのように親リクエストを選択するかについては、[トレースコンテキストの関連付け](../context-propagation/)を参照してください。

## 互換性 {#compatibility}

OBI は以下の構成で分散トレーシングとコンテキスト伝搬をサポートします。

| 分野                                  | サポートされるバージョンや環境                                          | 備考                                                                                                                                                                               |
| :------------------------------------ | :---------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| ネットワークレベル HTTP/1 伝搬        | [OBI 互換性要件](/docs/zero-code/obi/#compatibility)を満たす Linux 環境 | プログラミング言語を問わず動作します。HTTPS の場合、伝搬は他の OBI で計装されたサービス間に限定され、プロキシや L7 ロードバランサーによって途切れる可能性があります。              |
| ネットワークレベル gRPC 伝搬          | HTTP/2 上の gRPC `1.0+`                                                 | 言語を問わずストリームごとの HPACK `traceparent` ヘッダーを使用します。Go 以外の場合、OBI の起動前に確立された永続的な接続は認識されないことがあります。                           |
| Go ライブラリレベルのコンテキスト伝搬 | Go `1.18+`                                                              | 最大 6 階層のネストされた goroutine までコンテキスト伝搬をサポートします。この分散トレーシング機能は、一般的な Go ライブラリレベル計装よりも最小バージョン要件が高くなっています。 |
| Node.js の async hooks                | Node.js `8.0+`                                                          | `SIGUSR1` のカスタムハンドリングがコンテキスト伝搬を妨げる可能性があります。                                                                                                       |
| Ruby Puma                             | Puma `5.0+` で提供される Ruby アプリケーション                          | コンテキスト伝搬サポートには Puma サーバーが必要です。                                                                                                                             |
| Java スレッドプール                   | JDK `8+`                                                                | 文書化された追加のランタイム制約はありません。                                                                                                                                     |
| Python asyncio                        | `uvloop` 付きの Python `3.9+`                                           | コンテキスト伝搬サポートには `uvloop` イベントループが必要です。                                                                                                                   |

ここに記載のバージョンは、OBI が分散トレース機能で明示的にサポートするバージョンです。
これ以外のバージョンでも動作する可能性はありますが、特に明記されない限り、文書化されたサポート範囲には含まれません。
特に、ここでの Go `1.18+` 要件は分散トレーシングとコンテキスト伝搬に適用されるものであり、他の OBI Go ライブラリレベル計装の最小バージョンはこれより低い値です。

## 実装 {#implementation}

トレースコンテキストの伝搬は、2 つの異なる方法で実装されています。

1. 送信ヘッダー情報をネットワークレベルで書き込む
2. Go の場合は、ヘッダー情報をライブラリレベルで書き込む

サービスのプログラミング言語に応じて、OBI はこれらのコンテキスト伝搬アプローチのいずれか、または両方を使用します。
eBPF でメモリに書き込むことはカーネル設定や OBI に付与された Linux システムケーパビリティに依存するため、複数のアプローチを使ってコンテキスト伝搬を実装しています。
このトピックの詳細については、KubeCon NA 2024 のトーク [So You Want to Write Memory with eBPF?](https://www.youtube.com/watch?v=TUiVX-44S9s) を参照してください。

**ネットワークレベル**のコンテキスト伝搬はデフォルトで**無効**になっており、環境変数 `OTEL_EBPF_BPF_CONTEXT_PROPAGATION=all` を設定するか、OBI 設定ファイルを次のように変更することで有効化できます。

```yaml
ebpf:
  context_propagation: 'all'
```

### ネットワークレベルでのコンテキスト伝搬 {#context-propagation-at-network-level}

ネットワークレベルでのコンテキスト伝搬は、送信される HTTP ヘッダーおよび TCP/IP パケットレベルにトレースコンテキスト情報を書き込むことで実装されています。
HTTP のコンテキスト伝搬は、他の OpenTelemetry ベースのトレーシングライブラリと完全に互換性があります。
つまり、OBI で計装されたサービスは、OpenTelemetry SDK で計装されたサービスとの間で送受信されるトレース情報を正しく伝搬できます。
ネットワークパケットの調整には [Linux Traffic Control (TC)](<https://en.wikipedia.org/wiki/Tc_(Linux)>) を使用しているため、Linux Traffic Control を使う他の eBPF プログラムが OBI と適切にチェーンする必要があります。
Cilium CNI に関する特別な考慮事項については、[Cilium 互換性](../cilium-compatibility/)ガイドを参照してください。

TLS で暗号化されたトラフィック (HTTPS) では、OBI は送信される HTTP ヘッダーにトレース情報を注入できないため、かわりに TCP/IP パケットレベルに情報を注入します。
この制限により、OBI は他の OBI で計装されたサービスにのみトレース情報を送信できます。
L7 プロキシやロードバランサーは元のパケットを破棄して下流で再送するため、TCP/IP のコンテキスト伝搬を妨げます。
OpenTelemetry SDK で計装されたサービスからの受信トレースコンテキスト情報の解析は引き続き機能します。

gRPC の場合、OBI はストリームごとに `traceparent` HPACK ヘッダーを注入します。
これはプログラミング言語を問わず動作し、同時に利用される HTTP/2 ストリームごとに異なるトレースコンテキストを保持します。
OBI は gRPC に TCP オプションを使用しません。TCP オプションはコネクション単位のスコープであり、多重化されたストリームを表現できないためです。
gRPC 以外の汎用的な HTTP/2 コンテキスト伝搬は、Go のライブラリレベル計装に限定されます。

さらに細かい制御が必要な場合、`context_propagation` には `headers`、`tcp`、`headers,tcp` も指定できます。
以前存在した `http` エイリアスは削除されました。
非推奨の `ip` 値は効果がありません。

この種類のコンテキスト伝搬は任意のプログラミング言語で機能し、OBI を `privileged` モードで実行したり `CAP_SYS_ADMIN` を付与したりする必要はありません。
詳細については、[分散トレースとコンテキスト伝搬](../configure/metrics-traces-attributes/)の設定セクションを参照してください。

#### Kubernetes での設定 {#kubernetes-configuration}

ネットワークレベルの分散トレーシングをサポートする状態で OBI を Kubernetes 上にデプロイする推奨方法は、`DaemonSet` としてデプロイすることです。

次の `Kubernetes` 設定を使用する必要があります。

- OBI はホストネットワークアクセス (`hostNetwork: true`) 付きの `DaemonSet` としてデプロイする必要があります。
- ホストの `/sys/fs/cgroup` パスをローカルの `/sys/fs/cgroup` パスとしてボリュームマウントする必要があります。
- OBI コンテナに `CAP_NET_ADMIN` ケーパビリティを付与する必要があります。

次の YAML スニペットは OBI デプロイ設定の例です。

```yaml
spec:
  serviceAccount: obi
  hostPID: true # <-- 重要。DaemonSet モードでは OBI がすべての監視対象プロセスを検出するために必要
  hostNetwork: true # <-- 重要。DaemonSet モードでは OBI がすべてのネットワークパケットを参照するために必要
  dnsPolicy: ClusterFirstWithHostNet
  containers:
    - name: obi
      resources:
        limits:
          memory: 120Mi
      terminationMessagePolicy: FallbackToLogsOnError
      image: 'docker.io/otel/ebpf-instrument:main'
      imagePullPolicy: 'Always'
      env:
        - name: OTEL_EXPORTER_OTLP_ENDPOINT
          value: 'http://otelcol:4318'
        - name: OTEL_EBPF_KUBE_METADATA_ENABLE
          value: 'autodetect'
        - name: OTEL_EBPF_CONFIG_PATH
          value: '/config/obi-config.yml'
      securityContext:
        runAsUser: 0
        readOnlyRootFilesystem: true
        capabilities:
          add:
            - BPF # <-- 重要。ほとんどの eBPF プローブが正しく動作するために必要。
            - SYS_PTRACE # <-- 重要。OBI がコンテナの名前空間にアクセスし、実行可能ファイルを検査することを許可。
            - NET_RAW # <-- 重要。OBI が HTTP リクエスト用のソケットフィルターを使用することを許可。
            - CHECKPOINT_RESTORE # <-- 重要。OBI が ELF ファイルを開くことを許可。
            - DAC_READ_SEARCH # <-- 重要。OBI が ELF ファイルを開くことを許可。
            - PERFMON # <-- 重要。OBI が BPF プログラムをロードすることを許可。
            - NET_ADMIN # <-- 重要。OBI が HTTP および TCP のコンテキスト伝搬情報を注入することを許可。
      volumeMounts:
        - name: cgroup
          mountPath: /sys/fs/cgroup # <-- 重要。OBI が新規ソケットを監視して送信リクエストを追跡することを許可。
        - mountPath: /config
          name: obi-config
  tolerations:
    - effect: NoSchedule
      operator: Exists
    - effect: NoExecute
      operator: Exists
  volumes:
    - name: obi-config
      configMap:
        name: obi-config
    - name: cgroup
      hostPath:
        path: /sys/fs/cgroup
```

OBI の `DaemonSet` でローカルボリュームパスとして `/sys/fs/cgroup` がマウントされていない場合、一部のリクエストでコンテキスト伝搬が行われないことがあります。
このボリュームパスは、新しく作成されたソケットをリッスンするために使用されます。

#### カーネルバージョンの制限 {#kernel-version-limitations}

ネットワークレベルでのコンテキスト伝搬の受信ヘッダー解析は、BPF ループの追加と利用のため、通常はカーネル 5.17 以降を必要とします。

RHEL 9.2 などの一部のパッチ済みカーネルでは、この機能がバックポートされている場合があります。
`OTEL_EBPF_OVERRIDE_BPF_LOOP_ENABLED` を設定すると、お使いのカーネルがこの機能を含むものの 5.17 より低いバージョンである場合に、カーネルチェックをスキップできます。

### ライブラリレベルで計装する Go のコンテキスト伝搬 {#go-context-propagation-by-instrumenting-at-library-level}

この種類のコンテキスト伝搬は Go アプリケーションでのみサポートされており、eBPF のユーザーメモリ書き込みサポート (`bpf_probe_write_user`) を使用します。
このアプローチの利点は、HTTP および HTTPS で動作することです。
HTTP/2 と gRPC については、HTTPS を使用しない場合に新規接続でのみコンテキストを注入できます。
再利用された HTTP/2/gRPC 接続はまだサポートされていません。
`bpf_probe_write_user` の使用には OBI に `CAP_SYS_ADMIN` を付与するか、`privileged` コンテナとして実行するよう設定する必要があります。

#### Go の手動計装との統合 {#integration-with-go-manual-instrumentation}

OBI は [Auto SDK](/docs/zero-code/go/autosdk) を使用した手動スパンと自動的に統合されます。
詳細は Auto SDK のドキュメントを参照してください。

#### カーネル integrity モードの制限 {#kernel-integrity-mode-limitations}

送信される HTTP/gRPC リクエストヘッダーに `traceparent` 値を書き込むため、OBI は [**bpf_probe_write_user**](https://www.man7.org/linux/man-pages/man7/bpf-helpers.7.html) eBPF ヘルパーを使ってプロセスメモリに書き込む必要があります。
カーネル 5.14 以降 (5.10 系にもフィックスがバックポート) では、Linux カーネルが `integrity` **lockdown** モードで動作している場合、このヘルパーは保護され(BPF プログラムから利用不可)になります。
カーネル integrity モードは、カーネルで [**セキュアブート**](https://wiki.debian.org/SecureBoot)が有効になっている場合は通常デフォルトで有効になりますが、手動で有効化することもできます。

OBI は `bpf_probe_write_user` ヘルパーを利用できるかを自動的に確認し、カーネル設定が許可している場合にのみコンテキスト伝搬を有効にします。
Linux カーネルの **lockdown** モードは次のコマンドで確認できます。

```shell
cat /sys/kernel/security/lockdown
```

このファイルが存在し、モードが `[none]` 以外である場合、OBI はコンテキスト伝搬を実行できず、分散トレーシングは無効になります。

#### コンテナ化環境(Kubernetes を含む)における Go の分散トレーシング {#distributed-tracing-for-go-in-containerized-environments-including-kubernetes}

カーネル **lockdown** モードの制限により、**OBI の Docker コンテナ**ではホストシステムから `/sys/kernel/security/` ボリュームを Docker や Kubernetes の設定ファイルでマウントする必要があります。
これにより、OBI は Linux カーネルの **lockdown** モードを正しく判定できます。
以下に、OBI が **lockdown** モードを判定するのに十分な情報を持つようにする Docker Compose 設定の例を示します。

```yaml
services:
  ...
  obi:
    image: 'docker.io/otel/ebpf-instrument:main'
    environment:
      OTEL_EBPF_CONFIG_PATH: "/configs/obi-config.yml"
    volumes:
      - /sys/kernel/security:/sys/kernel/security
      - /sys/fs/cgroup:/sys/fs/cgroup
```

`/sys/kernel/security/` ボリュームがマウントされていない場合、OBI は Linux カーネルが integrity モードで動作していないものとみなします。

### Go チャネルスパンリンク {#go-channel-span-links}

OBI は、Go チャネルを介したサポート対象のワークハンドオフに対して、実験的なレシーバー側のスパンリンクを発行します。
送信側と受信側の両方に OBI が生成したアクティブなスパンがある場合、受信側のスパンは送信側のスパンにリンクされます。
OBI はトレース ID、親子関係、送信側のスパンを変更しません。

この動作は、OBI がターゲットバイナリのチャネルランタイムオフセットを解決できる場合、Go 固有のトレーシングとともに自動的に有効になります。
`runtime.chansend1`、`runtime.chanrecv1`、`runtime.chanrecv2` を通じたバッファなしおよびバッファありの直接的なハンドオフがサポートされます。
`select` を通じたチャネル操作はサポートされません。
これらのプローブを無効にするには Go 固有のトレーサーを無効にしてください。チャネルリンク専用のオプションはありません。

OBI は `OTEL_SPAN_LINK_COUNT_LIMIT` を尊重し、無効、重複、自己参照のリンクを破棄します。

### uvloop を使用した Python asyncio {#python-asyncio-with-uvloop}

OBI は v0.7.0 以降、[`uvloop`](https://github.com/MagicStack/uvloop) 上で動作する Python asyncio のワークロードに対するコンテキスト伝搬をサポートしています。
これにより、標準の `asyncio` サポートに加え、`uvloop` イベントループを使用する非同期 Python サービスの分散トレーシングが可能になります。

ネットワークレベルでのコンテキスト伝搬は `uvloop` 上で動作する Python アプリケーションにも適用され、OBI は非同期処理に対してトレースコンテキストを自動的に計装・伝搬できます。
コンテキスト伝搬を有効にすること以外に、追加の設定は不要です(詳細は[はじめに](#introduction)を参照)。

OBI を Python asyncio と `uvloop` で使うには、お使いの Python アプリケーションがイベントループ実装として `uvloop` を使用するように構成されていることを確認してください。
