2026年8月31日
2026年8月31日
Grafana Tempoで分散トレーシングを設定する方法(Docker Composeでの構築とGrafanaでの確認)
Grafana Tempo は、アプリケーションから送られてきたトレース(1件のリクエストが、どの処理にどれだけ時間をかけたかの記録)を保存し、Grafana から検索・表示できるようにするバックエンドです。Tempo を立ててトレースの受け口(OTLP)を開け、Grafana にデータソースとして登録すれば、遅いリクエストの内訳を画面で追えるようになります。この記事では、Docker Compose で Tempo と Grafana を1台に構築する手順、保存期間と保存先の設定、TraceQL での検索、そして公開範囲の注意点までを説明します。
WordPress などのアプリ側でトレースを作って送る「計装」の手順は、OpenTelemetry の導入記事で扱っています。本記事は受け取る側(Tempo)の構築に絞ります。
Tempo の位置づけと全体像
分散トレーシングの構成は、大きく3つの役割に分かれます。
- 計装(アプリ側):OpenTelemetry の SDK や自動計装で、リクエストごとのスパン(処理の区間)を作る
- 収集・転送:OpenTelemetry Collector や Grafana Alloy が受け取り、加工してバックエンドに送る(アプリから直接 Tempo に送ることもできる)
- 保存・検索(Tempo):トレースを保存し、Grafana からの検索に応える
Tempo の特長は、トレースの本体をオブジェクトストレージ(S3 互換ストレージなど)やローカルディスクに置き、検索用の大きなデータベースを別に持たない設計にあります。そのため運用の部品が少なく、保存コストを抑えやすい一方、検索は「トレースID」「サービス名」「所要時間」「属性」などの条件を TraceQL で指定する形になります。
受け口は OTLP(OpenTelemetry の標準プロトコル)が基本で、gRPC は 4317 番、HTTP は 4318 番が慣例のポートです。Jaeger や Zipkin の形式も受信設定を追加すれば受け取れます。
Docker Compose で Tempo と Grafana を起動する
学習や小規模な用途なら、Tempo を「単一バイナリ(monolithic)」で動かすのが簡単です。作業用ディレクトリに次のファイルを用意します。イメージのバージョンは latest に頼らず、公式のリリースページで確認した版を .env に書いて固定します。版が変わると設定項目が変わることがあるためです。
mkdir -p ~/tracing/grafana/provisioning/datasources
cd ~/tracing
cat > .env <<'EOF'
# x.y.z はリリースページで確認した版に置き換える
TEMPO_VERSION=x.y.z
GRAFANA_VERSION=x.y.z
EOF
# ~/tracing/compose.yaml
services:
# Tempo のコンテナは root 以外のユーザー(UID 10001)で動くため、
# 保存用ボリュームの所有者を先に合わせておく
tempo-init:
image: grafana/tempo:${TEMPO_VERSION}
user: root
entrypoint: ["chown", "10001:10001", "/var/tempo"]
volumes:
- tempo-data:/var/tempo
tempo:
image: grafana/tempo:${TEMPO_VERSION}
command: ["-config.file=/etc/tempo.yaml"]
depends_on:
tempo-init:
condition: service_completed_successfully
volumes:
- ./tempo.yaml:/etc/tempo.yaml:ro
- tempo-data:/var/tempo
ports:
- "127.0.0.1:3200:3200" # 検索 API
- "127.0.0.1:4317:4317" # OTLP gRPC
- "127.0.0.1:4318:4318" # OTLP HTTP
restart: unless-stopped
grafana:
image: grafana/grafana:${GRAFANA_VERSION}
volumes:
- ./grafana/provisioning:/etc/grafana/provisioning:ro
- grafana-data:/var/lib/grafana
ports:
- "127.0.0.1:3000:3000"
restart: unless-stopped
volumes:
tempo-data:
grafana-data:
tempo-init は保存ディレクトリの所有者を変えるだけの使い捨てコンテナです。これを省くと、Tempo が /var/tempo に書き込めず「permission denied」で起動に失敗することがあります。公式リポジトリの Docker Compose の例でも同じ方法が使われていますが、イメージの実行ユーザーは版で変わる可能性があるので、起動に失敗したらまずログで権限エラーかどうかを見てください。
ポートを 127.0.0.1: 付きで公開しているのは、Tempo と Grafana をインターネットから直接触れないようにするためです。理由は後の「公開範囲」の節で説明します。
Tempo の設定ファイルを書く
# ~/tracing/tempo.yaml
stream_over_http_enabled: true
server:
http_listen_port: 3200
log_level: info
distributor:
receivers:
otlp:
protocols:
grpc:
endpoint: "0.0.0.0:4317"
http:
endpoint: "0.0.0.0:4318"
compactor:
compaction:
block_retention: 168h # 7日分を保持
storage:
trace:
backend: local
wal:
path: /var/tempo/wal
local:
path: /var/tempo/blocks
それぞれの意味とつまずきやすい点は次のとおりです。
- receivers の endpoint:近年の版では、受信ポートの既定の待ち受けアドレスが
localhostに変わっています。コンテナ内でlocalhostだけを待ち受けると、ポートを公開していても外のコンテナやホストから届きません。コンテナで動かすときは0.0.0.0を明示し、外部への公開範囲は Docker のポート設定やファイアウォールで絞ります。 - block_retention:トレースを保持する期間です。期間を過ぎたデータはコンパクターが削除します。ディスク容量と、どれくらい前までさかのぼって調べたいかで決めます。
- wal と local:受け取った直後のデータは WAL(先行書き込みログ)に置かれ、一定時間ごとにブロックとしてまとめられます。どちらも永続ボリューム上に置かないと、コンテナの再作成で直近のトレースが消えます。
- stream_over_http_enabled:Grafana から検索したとき、結果を段階的に受け取るための設定です。
設定キーは版によって追加・変更されます。起動時にエラーで止まったら、ログに出る未知のキー名を手がかりに、使っている版の設定リファレンスを確認してください(2026年10月現在)。
Grafana にデータソースを登録する
Grafana の画面からも追加できますが、設定ファイルで登録しておくとコンテナを作り直しても消えません。
# ~/tracing/grafana/provisioning/datasources/tempo.yaml
apiVersion: 1
datasources:
- name: Tempo
type: tempo
uid: tempo
access: proxy
url: http://tempo:3200
URL の tempo は Compose のサービス名で、Grafana のコンテナから見た Tempo の宛先です。ブラウザから見たアドレスではない点に注意します。起動して疎通を確かめます。
docker compose up -d
docker compose logs tempo --tail 50
curl -s http://127.0.0.1:3200/ready # ready と返れば受け付け可能
Tempo は起動直後しばらく ready を返さないことがあるので、少し待ってから再確認します。Grafana には http://127.0.0.1:3000 で入り、初回ログイン後すぐに管理者パスワードを変更します。リモートのサーバーで動かしている場合は、SSH のポート転送(ssh -L 3000:127.0.0.1:3000 user@server)で手元のブラウザから開きます。
テスト用のトレースを送って確認する
アプリの計装を始める前に、Tempo 自体が受け取れるかを確かめておくと、問題の切り分けが楽になります。OpenTelemetry Collector のプロジェクトが配布している telemetrygen を使うと、ダミーのトレースを送れます。
docker run --rm --network tracing_default \
ghcr.io/open-telemetry/opentelemetry-collector-contrib/telemetrygen:latest \
traces --otlp-endpoint tempo:4317 --otlp-insecure --traces 10
--network には Compose が作ったネットワーク名を指定します(docker network ls で確認できます。ディレクトリ名に _default が付いた名前が一般的です)。送ったあと Grafana の Explore で Tempo データソースを選び、検索するとトレースが表示されます。表示されないときは、Tempo のログに受信エラーが出ていないか、時間範囲の指定が直近になっているかを確認します。
TraceQL でトレースを探す
Tempo の検索は TraceQL というクエリ言語で行います。波かっこの中にスパンの条件を書くのが基本形です。
{ resource.service.name = "wordpress" }
{ resource.service.name = "wordpress" && duration > 2s }
{ resource.service.name = "wordpress" && status = error }
1行目はサービス名で絞る例、2行目は2秒を超えた処理を探す例、3行目はエラーになったスパンを探す例です。resource. はサービス全体に付く属性、span. は個々のスパンに付く属性を表します。HTTP のパスやデータベースの種類などの属性名は、計装ライブラリやセマンティック規約の版によって異なるため、まず1件のトレースを開いて、実際に付いている属性名を確認してから条件を書くと手戻りが少なくなります。
遅いページを調べるときは、「時間がかかったトレースを探す → スパンの一覧で一番長い区間を見る → それが DB クエリなのか外部 API 呼び出しなのかを特定する」という順で追います。ログを Loki に集めている場合は、Tempo データソースの設定でログとの関連付け(トレースからログへ移動する機能)を有効にすると、同じリクエストのログにすぐ移れます。画面上の設定名は Grafana の版で変わることがあるので、データソース設定画面の項目を確認してください。
保存先と公開範囲の注意点
保存先をオブジェクトストレージにする
ローカルディスクは手軽ですが、ディスクがいっぱいになると受信が止まり、サーバーが壊れればトレースも失われます。長く運用するなら S3 互換のオブジェクトストレージを使います。
storage:
trace:
backend: s3
s3:
bucket: your-tempo-bucket
endpoint: s3.ap-northeast-1.amazonaws.com
region: ap-northeast-1
wal:
path: /var/tempo/wal
認証情報は設定ファイルに書かず、可能であれば IAM ロールを使い、使えない環境では環境変数で渡します。バケットにはライフサイクルの設定もできますが、Tempo の block_retention と食い違うと、Tempo が把握していないデータが残ったり、逆に早く消えたりします。保持期間はどちらで管理するかを決めておきます。
Tempo を外部に公開しない
Tempo 単体には、ログイン画面のような利用者認証の仕組みがありません。4317・4318・3200 番をインターネットに開けると、誰でもトレースを送り込めたり、保存されたトレースを読めたりする状態になります。アプリと Tempo が別のサーバーにある場合は、VPN やプライベートネットワークを使うか、認証付きのリバースプロキシや Collector を前段に置きます。
また、トレースには URL のクエリ文字列やユーザーID、SQL 文の一部などが含まれることがあります。個人情報や秘密情報が入りそうな属性は、Collector の段階で削除・マスクしてから Tempo に送ると安全です。
まとめ
- Grafana Tempo はトレースを保存・検索するバックエンドで、OTLP(gRPC 4317・HTTP 4318)で受け取り、Grafana で表示します。
- Docker で動かすときは、保存ボリュームの所有者と、受信アドレスを
0.0.0.0にする設定でつまずきやすいので先に対処します。 block_retentionで保持期間を決め、長期運用ではオブジェクトストレージに保存します。- まず telemetrygen でダミーのトレースを送り、Tempo 単体で受信できることを確かめてからアプリの計装に進みます。
- Tempo には利用者認証がないため、ポートは外部に公開せず、個人情報を含む属性は送る前に取り除きます。