CLOVER🍀

That was when it all began.

FastAPI × OpenTelemetry Collector × Grafana Tempo × Prometheusでトレース、メトリクスを収集してみる

これは、なにをしたくて書いたもの?

前に、ひとまずGrafana Tempoを使ってみたというエントリーを書きました。

Grafana Tempo 2.8+MinIO+GrafanaをUbuntu Linux 24.04 LTSにインストールして、トレースデータを見てみる - CLOVER🍀

今度は、Grafana Tempoの前にOpenTelemetry Collectorを置く構成を試してみたいと思います。ついでにPrometheusも
つけてみます。

OpenTelemetry CollectorからGrafana Tempoにトレースシグナルを送信する

前回は、アプリケーションからGrafana Tempoに直接トレースシグナルを送信しました。

Grafana Tempo 2.8+MinIO+GrafanaをUbuntu Linux 24.04 LTSにインストールして、トレースデータを見てみる - CLOVER🍀

今回はOpenTelemetry Collector(正確にはOpenTelemetry Collector Contrib)経由で送信してみたいと思います。

つまり、こういう構成にしたいと思います。

flowchart LR
        A["Application/FastAPI"]
        A -- OTLP over http/protobuf --> B["OpenTelemetry Collector"]
        B -- OTLP over http/protobuf --> C["Grafana Tempo"]
        C -- read/write --> D[("MinIO")]
        B -- Remote Write --> E["Prometheus"]
        F["Grafana"] --> C
        F --> E

環境

今回の環境はこちら。

$ python3 --version
Python 3.12.3


$ uv --version
uv 0.8.11

OpenTelemetry Collector。172.19.0.2で動作しているものとします。

$ otelcol-contrib --version
otelcol-contrib version 0.132.0

Grafana Tempo。172.19.0.3で動作しているものとします。

$ tempo --version
tempo, version 2.8.2 (branch: HEAD, revision: 6b9261586)
  build user:
  build date:
  go version:       go1.24.5
  platform:         linux/amd64
  tags:             unknown

MinIO。172.19.0.4で動作しているものとします。

$ minio --version
minio version RELEASE.2025-07-23T15-54-02Z (commit-id=7ced9663e6a791fef9dc6be798ff24cda9c730ac)
Runtime: go1.24.5 linux/amd64
License: GNU AGPLv3 - https://www.gnu.org/licenses/agpl-3.0.html
Copyright: 2015-2025 MinIO, Inc.

Prometheus。172.19.0.5で動作しているものとします。

$ ./prometheus --version
prometheus, version 3.5.0 (branch: HEAD, revision: 8be3a9560fbdd18a94dedec4b747c35178177202)
  build user:       root@4451b64cb451
  build date:       20250714-16:15:23
  go version:       go1.24.5
  platform:         linux/amd64
  tags:             netgo,builtinassets

また、起動時にはリモート書き込みを有効にしておきます。

$ ./prometheus --web.enable-remote-write-receiver

Grafana。172.19.0.6で動作しているものとします。

$ grafana-server --version
Version 12.1.1 (commit: df5de8219b41d1e639e003bf5f3a85913761d167, branch: release-12.1.1)

準備

各種ミドルウェアの設定を行います。

OpenTelemetry Collector Contrib。

/etc/otelcol-contrib/config.yaml

receivers:
  otlp:
    protocols:
      grpc:
        endpoint: 0.0.0.0:4317
      http:
        endpoint: 0.0.0.0:4318


processors:
  batch:


exporters:
  otlphttp:
    endpoint: http://172.19.0.3:4318

  prometheusremotewrite:
    endpoint: "http://172.19.0.5:9090/api/v1/write"
    resource_to_telemetry_conversion:
      enabled: true

  debug:
    verbosity: detailed


service:
  pipelines:
    traces:
      receivers: [otlp]
      processors: [batch]
      exporters: [otlphttp]

    metrics:
      receivers: [otlp]
      processors: [batch]
      exporters: [prometheusremotewrite]

テレメトリーデータはOTLPで受信して

receivers:
  otlp:
    protocols:
      grpc:
        endpoint: 0.0.0.0:4317
      http:
        endpoint: 0.0.0.0:4318

OTLP http/protobufおよびPrometheusのリモート書き込みでエクスポートするようにしています。

exporters:
  otlphttp:
    endpoint: http://172.19.0.3:4318

  prometheusremotewrite:
    endpoint: "http://172.19.0.5:9090/api/v1/write"
    resource_to_telemetry_conversion:
      enabled: true

  debug:
    verbosity: detailed

Grafana Tempo。

/etc/tempo/config.yml

stream_over_http_enabled: true
server:
  http_listen_port: 3200
  log_level: info

query_frontend:
  search:
    duration_slo: 5s
    throughput_bytes_slo: 1.073741824e+09
    metadata_slo:
        duration_slo: 5s
        throughput_bytes_slo: 1.073741824e+09
  trace_by_id:
    duration_slo: 5s

distributor:
  receivers:
    otlp:
      protocols:
        grpc:
          endpoint: "0.0.0.0:4317"
        http:
          endpoint: "0.0.0.0:4318"

metrics_generator:
  registry:
    external_labels:
      source: tempo
      cluster: docker-compose
  storage:
    path: /var/tempo/generator/wal
    remote_write:
      - url: http://prometheus:9090/api/v1/write
        send_exemplars: true
  traces_storage:
    path: /var/tempo/generator/traces

storage:
  trace:
    backend: s3
    s3:
      bucket: "tempo-blocks"
      endpoint: "172.19.0.4:9000"
      insecure: true
      forcepathstyle: true
      access_key: "minioadmin"
      secret_key: "minioadmin"
    wal:
      path: /var/tempo/wal             # where to store the wal locally

overrides:
  defaults:
    metrics_generator:
      processors: [service-graphs, span-metrics, local-blocks] # enables metrics generator
      generate_native_histograms: both

OTLPのhttpprobuf用のエンドポイントを有効にして、

distributor:
  receivers:
    otlp:
      protocols:
        grpc:
          endpoint: "0.0.0.0:4317"
        http:
          endpoint: "0.0.0.0:4318"

ストレージはMinIOにしています。

storage:
  trace:
    backend: s3
    s3:
      bucket: "tempo-blocks"
      endpoint: "172.19.0.4:9000"
      insecure: true
      forcepathstyle: true
      access_key: "minioadmin"
      secret_key: "minioadmin"
    wal:
      path: /var/tempo/wal             # where to store the wal locally

MinIOではtempo-blocksというバケットを作成済みとします。

Prometheusはリモート書き込みのみにするので、スクレイピング対象はなしにしています。

prometheus.yml

global:
  scrape_interval: 15s
  evaluation_interval: 15s

scrape_configs:

FastAPIを使ったアプリケーションを作成する

それでは、FastAPIを使ったアプリケーションを作成します。

uvでプロジェクトを作成。

$ uv init --vcs none tempo-otel-col-test
$ cd tempo-otel-col-test

FastAPIやOpenTelemetryに関するライブラリーを追加。

$ uv add 'fastapi[standard]'
$ uv add opentelemetry-exporter-otlp
$ uv add --dev opentelemetry-distro

計装ライブラリーをインストール。

$ uv run opentelemetry-bootstrap -a requirements | xargs uv add

pyproject.tomlはこうなりました。

pyproject.toml

[project]
name = "tempo-otel-col-test"
version = "0.1.0"
description = "Add your description here"
readme = "README.md"
requires-python = ">=3.12"
dependencies = [
    "fastapi[standard]>=0.116.1",
    "opentelemetry-exporter-otlp>=1.36.0",
    "opentelemetry-instrumentation-asyncio==0.57b0",
    "opentelemetry-instrumentation-click==0.57b0",
    "opentelemetry-instrumentation-dbapi==0.57b0",
    "opentelemetry-instrumentation-fastapi==0.57b0",
    "opentelemetry-instrumentation-grpc==0.57b0",
    "opentelemetry-instrumentation-httpx==0.57b0",
    "opentelemetry-instrumentation-jinja2==0.57b0",
    "opentelemetry-instrumentation-logging==0.57b0",
    "opentelemetry-instrumentation-requests==0.57b0",
    "opentelemetry-instrumentation-sqlite3==0.57b0",
    "opentelemetry-instrumentation-starlette==0.57b0",
    "opentelemetry-instrumentation-threading==0.57b0",
    "opentelemetry-instrumentation-tortoiseorm==0.57b0",
    "opentelemetry-instrumentation-urllib==0.57b0",
    "opentelemetry-instrumentation-urllib3==0.57b0",
    "opentelemetry-instrumentation-wsgi==0.57b0",
]

[dependency-groups]
dev = [
    "opentelemetry-distro>=0.57b0",
]

インストールされたライブラリーの一覧。

$ uv pip list
Package                                   Version
----------------------------------------- --------
annotated-types                           0.7.0
anyio                                     4.10.0
asgiref                                   3.9.1
certifi                                   2025.8.3
charset-normalizer                        3.4.3
click                                     8.2.1
dnspython                                 2.7.0
email-validator                           2.2.0
fastapi                                   0.116.1
fastapi-cli                               0.0.8
fastapi-cloud-cli                         0.1.5
googleapis-common-protos                  1.70.0
grpcio                                    1.74.0
h11                                       0.16.0
httpcore                                  1.0.9
httptools                                 0.6.4
httpx                                     0.28.1
idna                                      3.10
importlib-metadata                        8.7.0
jinja2                                    3.1.6
markdown-it-py                            4.0.0
markupsafe                                3.0.2
mdurl                                     0.1.2
opentelemetry-api                         1.36.0
opentelemetry-distro                      0.57b0
opentelemetry-exporter-otlp               1.36.0
opentelemetry-exporter-otlp-proto-common  1.36.0
opentelemetry-exporter-otlp-proto-grpc    1.36.0
opentelemetry-exporter-otlp-proto-http    1.36.0
opentelemetry-instrumentation             0.57b0
opentelemetry-instrumentation-asgi        0.57b0
opentelemetry-instrumentation-asyncio     0.57b0
opentelemetry-instrumentation-click       0.57b0
opentelemetry-instrumentation-dbapi       0.57b0
opentelemetry-instrumentation-fastapi     0.57b0
opentelemetry-instrumentation-grpc        0.57b0
opentelemetry-instrumentation-httpx       0.57b0
opentelemetry-instrumentation-jinja2      0.57b0
opentelemetry-instrumentation-logging     0.57b0
opentelemetry-instrumentation-requests    0.57b0
opentelemetry-instrumentation-sqlite3     0.57b0
opentelemetry-instrumentation-starlette   0.57b0
opentelemetry-instrumentation-threading   0.57b0
opentelemetry-instrumentation-tortoiseorm 0.57b0
opentelemetry-instrumentation-urllib      0.57b0
opentelemetry-instrumentation-urllib3     0.57b0
opentelemetry-instrumentation-wsgi        0.57b0
opentelemetry-proto                       1.36.0
opentelemetry-sdk                         1.36.0
opentelemetry-semantic-conventions        0.57b0
opentelemetry-util-http                   0.57b0
packaging                                 25.0
protobuf                                  6.32.0
pydantic                                  2.11.7
pydantic-core                             2.33.2
pygments                                  2.19.2
python-dotenv                             1.1.1
python-multipart                          0.0.20
pyyaml                                    6.0.2
requests                                  2.32.4
rich                                      14.1.0
rich-toolkit                              0.15.0
rignore                                   0.6.4
sentry-sdk                                2.35.0
shellingham                               1.5.4
sniffio                                   1.3.1
starlette                                 0.47.2
typer                                     0.16.0
typing-extensions                         4.14.1
typing-inspection                         0.4.1
urllib3                                   2.5.0
uvicorn                                   0.35.0
uvloop                                    0.21.0
watchfiles                                1.1.0
websockets                                15.0.1
wrapt                                     1.17.3
zipp                                      3.23.0

ソースコード

main.py

import fastapi
from opentelemetry.instrumentation.fastapi import FastAPIInstrumentor

app = fastapi.FastAPI()

@app.get("/foobar")
async def foobar():
    return {"message": "hello world"}

FastAPIInstrumentor.instrument_app(app)

OpenTelemetry SDKの設定を環境変数で行って

$ export OTEL_PYTHON_LOGGING_AUTO_INSTRUMENTATION_ENABLED=true
$ export OTEL_TRACES_EXPORTER=otlp
$ export OTEL_METRICS_EXPORTER=otlp
$ export OTEL_LOGS_EXPORTER=none
$ export OTEL_EXPORTER_OTLP_ENDPOINT=http://172.19.0.2:4318
$ export OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf
$ export OTEL_SERVICE_NAME=app

起動。

$ uv run opentelemetry-instrument fastapi run main.py

あとはアクセスし続けるだけですね。

$ curl localhost:8000/foobar
{"message":"hello world"}

Grafanaで確認。

トレース。

メトリクス。

それぞれ収集できているようです。

これで、確認したかったことはできました、と。

おわりに

Grafana Tempoの前にOpenTelemetry Collectorを置き、ついでにPrometheusもつけた構成を試してみました。

コンポーネントの接続自体はそれほど手間取らなかったのですが、このあたりの環境を揃えるのにまあまあ苦労しました…。
これからたくさん使うと思うので、今回の結果をうまく使えるようにしたいですね。