docker-compose.yml
yaml
version: "3.9"
services:
tempo:
image: grafana/tempo:latest
container_name: tempo
command: ["-config.file=/etc/tempo/tempo.yaml"]
volumes:
- ./tempo.yaml:/etc/tempo/tempo.yaml:ro
ports:
- "3200:3200" # Grafana 访问 Tempo HTTP
- "4317:4317" # OTLP gRPC(给外部用可选,但留着也没坏处)
otel-collector:
image: otel/opentelemetry-collector:0.140.1
container_name: otel-collector
command: ["--config=/etc/otelcol/config.yaml"]
volumes:
- ./otel-collector.yaml:/etc/otelcol/config.yaml:ro
ports:
- "4318:4318" # Spring Boot OTLP HTTP
- "9464:9464" # Prometheus scrape
- "13133:13133" # 健康检查(可选)
depends_on:
- tempo
- loki
loki:
image: grafana/loki:2.9.0
container_name: loki
user: "0" # 👈 关键:以 root 用户运行,避免权限问题
command: ["-config.file=/etc/loki/loki-config.yaml"]
volumes:
- ./loki-config.yaml:/etc/loki/loki-config.yaml:ro
- loki-data:/loki # 👈 新增:宿主机目录挂到 /loki
ports:
- "3100:3100"
prometheus:
image: prom/prometheus:latest
container_name: prometheus
command:
- "--config.file=/etc/prometheus/prometheus.yml"
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml:ro
ports:
- "9090:9090"
depends_on:
- otel-collector
grafana:
image: grafana/grafana:latest
container_name: grafana
ports:
- "3000:3000"
depends_on:
- prometheus
- tempo
- loki
volumes:
loki-data: {}
tempo.yaml
yaml
server:
http_listen_port: 3200
grpc_listen_port: 9095 # Tempo 内部 gRPC,用默认端口
distributor:
receivers:
otlp:
protocols:
grpc:
endpoint: 0.0.0.0:4317 # ⭐ OTLP gRPC 在容器内监听 4317
storage:
trace:
backend: local
wal:
path: /tmp/tempo/wal
local:
path: /tmp/tempo/blocks
compactor:
compaction:
compaction_window: 1h
block_retention: 24h
compacted_block_retention: 24h
loki-config.yaml
yaml
auth_enabled: false
server:
http_listen_port: 3100
grpc_listen_port: 9096
ingester:
wal:
enabled: true
dir: /loki/wal # 👈 WAL 放在 /loki 下面,挂了 volume 就不会权限问题
lifecycler:
address: 127.0.0.1
ring:
kvstore:
store: inmemory
replication_factor: 1
final_sleep: 0s
chunk_idle_period: 3m
max_chunk_age: 1h
chunk_target_size: 1048576
max_transfer_retries: 0
schema_config:
configs:
- from: 2020-10-24
store: boltdb-shipper
object_store: filesystem
schema: v11
index:
prefix: index_
period: 24h
storage_config:
boltdb_shipper:
active_index_directory: /loki/index
cache_location: /loki/cache
shared_store: filesystem
filesystem:
directory: /loki/chunks # 👈 数据块目录,同样在 /loki 下面
compactor:
working_directory: /loki/compactor
shared_store: filesystem
compaction_interval: 10m
limits_config:
allow_structured_metadata: true
enforce_metric_name: false
reject_old_samples: true
reject_old_samples_max_age: 168h
max_streams_per_user: 0
max_global_streams_per_user: 0
query_range:
parallelise_shardable_queries: true
align_queries_with_step: true
cache_results: true
max_retries: 5
querier:
engine:
max_look_back_period: 0s
query_timeout: 1m
otel-collector.yaml
yaml
receivers:
otlp:
protocols:
http:
endpoint: 0.0.0.0:4318 # HTTP OTLP,给 Spring Boot 用
grpc:
endpoint: 0.0.0.0:4317 # gRPC OTLP(可选)
exporters:
prometheus:
endpoint: "0.0.0.0:9464"
namespace: "myapp"
otlp/tempo:
endpoint: "tempo:4317" # ⭐ 发 gRPC 到 Tempo
tls:
insecure: true
otlphttp/logs:
endpoint: "http://loki:3100/otlp"
tls:
insecure: true
processors:
batch:
timeout: 5s
send_batch_size: 1000
service:
pipelines:
metrics:
receivers: [otlp]
processors: [batch]
exporters: [prometheus]
traces:
receivers: [otlp]
processors: [batch]
exporters: [otlp/tempo]
logs:
receivers: [otlp]
processors: [batch]
exporters: [otlphttp/logs]
prometheus.yml
yaml
global:
scrape_interval: 10s
scrape_configs:
- job_name: 'otel-collector'
static_configs:
- targets: ['otel-collector:9464']