これは、なにをしたくて書いたもの?
前に、ひとまず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)
$ 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もつけた構成を試してみました。
各コンポーネントの接続自体はそれほど手間取らなかったのですが、このあたりの環境を揃えるのにまあまあ苦労しました…。
これからたくさん使うと思うので、今回の結果をうまく使えるようにしたいですね。