Files
rippled/docker/telemetry/docker-compose.yml
Pratik Mankawde 78b29dbd0e chore(telemetry): pin the image renderer and wire its auth token
The renderer was the only service in the stack on a floating :latest tag, so
the stack was not reproducible: a renderer release could change under a
checkout that had not been touched. Pins it to v5.12.0, matching how every
other service here is pinned.

Also wires the shared render token. Grafana 13 enables the renderAuthJWT
feature toggle by default, and the renderer rejects requests unless both sides
present the same non-default token, so pinning alone would leave rendering
broken once the Grafana bump merges forward. Both values come from the same
GF_RENDERING_RENDERER_TOKEN variable and cannot drift apart; the local default
keeps `docker compose up` working with no setup.

Verified `docker compose config` resolves and both AUTH_TOKEN and
GF_RENDERING_RENDERER_TOKEN expand to the same value.
2026-08-06 14:25:59 +01:00

165 lines
6.8 KiB
YAML

# Docker Compose stack for xrpld OpenTelemetry observability.
#
# Provides services for local development:
# - otel-collector: receives OTLP traces from xrpld, batches and
# forwards them to Tempo. Also tails xrpld log files
# via filelog receiver and exports to Loki. Listens on ports
# 4317 (gRPC) and 4318 (HTTP).
# - tempo: Grafana Tempo tracing backend, queryable via Grafana Explore
# on port 3000. Recommended for production (S3/GCS storage, TraceQL).
# - loki: Grafana Loki log aggregation backend for centralized log
# ingestion and log-trace correlation (Phase 8).
# - grafana: dashboards on port 3000, pre-configured with Tempo,
# Prometheus, and Loki datasources.
#
# Usage:
# docker compose -f docker/telemetry/docker-compose.yml up -d
#
# Configure xrpld to export traces by adding to xrpld.cfg:
# [telemetry]
# enabled=1
# endpoint=http://localhost:4318/v1/traces
services:
# OpenTelemetry Collector: receives spans from xrpld via OTLP protocol,
# batches them for efficiency, and forwards to Tempo for storage.
otel-collector:
image: otel/opentelemetry-collector-contrib:0.121.0
command: ["--config=/etc/otel-collector-config.yaml"]
ports:
- "4317:4317" # OTLP gRPC
- "4318:4318" # OTLP HTTP (traces + native OTel metrics)
- "8889:8889" # Prometheus metrics (spanmetrics + OTLP)
# StatsD UDP port removed — beast::insight now uses native OTLP.
# Uncomment if using server=statsd fallback:
# - "8125:8125/udp"
volumes:
# Mount collector pipeline config (receivers → processors → exporters)
- ./otel-collector-config.yaml:/etc/otel-collector-config.yaml:ro
# Mount the xrpld log root for the filelog receiver. The telemetry
# configs write to docker/telemetry/data/logs/<network>/debug.log, so
# the default source is the repo-relative ./data/logs — user-owned and
# needing no root, so `docker compose up` works with no setup. Override
# XRPLD_LOG_DIR to point at another root (e.g. the integration test sets
# it to its own workdir). Mounted read-only so the collector only tails.
- ${XRPLD_LOG_DIR:-./data/logs}:/var/log/xrpld:ro
depends_on:
- tempo
- loki
networks:
- xrpld-telemetry
# Grafana Tempo: distributed tracing backend that stores and indexes
# spans. Queryable via TraceQL in Grafana Explore.
tempo:
image: grafana/tempo:2.7.2
command: ["-config.file=/etc/tempo.yaml"]
ports:
- "3200:3200" # Tempo HTTP API (health check, query)
volumes:
# Mount Tempo storage and ingestion config
- ./tempo.yaml:/etc/tempo.yaml:ro
# Persistent volume for trace data (WAL + blocks)
- tempo-data:/var/tempo
networks:
- xrpld-telemetry
# Grafana Loki for centralized log ingestion and log-trace
# correlation. Loki 3.x supports native OTLP ingestion, so the OTel
# Collector exports via otlphttp to Loki's /otlp endpoint.
# Query logs via Grafana Explore -> Loki at http://localhost:3000.
loki:
image: grafana/loki:3.4.2
ports:
- "3100:3100"
command: -config.file=/etc/loki/local-config.yaml
volumes:
- loki-data:/loki
networks:
- xrpld-telemetry
prometheus:
# Pinned to the v2.53 LTS line for reproducible, config-stable runs.
image: prom/prometheus:v2.53.3
ports:
- "9090:9090"
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml:ro
- prometheus-data:/prometheus
depends_on:
- otel-collector
networks:
- xrpld-telemetry
# Grafana: visualization UI with Tempo pre-configured as a datasource.
# Anonymous admin access enabled for local development convenience.
grafana:
image: grafana/grafana:11.5.2
# Alerting secrets/addresses (Slack webhook, alert email) come from this
# gitignored file; Grafana expands the ${VARS} referenced in the alerting
# provisioning YAML. Absent file = unset vars = receivers have no live
# destination, which is fine for a local stack. See .env.alerting.example.
env_file:
- path: .env.alerting
required: false
environment:
- GF_AUTH_ANONYMOUS_ENABLED=true # No login required for local dev
- GF_AUTH_ANONYMOUS_ORG_ROLE=Admin # Full access without auth
# Remote image rendering: point Grafana at the renderer container.
# These belong on the grafana service (the server delegates renders);
# the callback URL is how the renderer fetches the panel from grafana.
- GF_RENDERING_SERVER_URL=http://renderer:8081/render
- GF_RENDERING_CALLBACK_URL=http://grafana:3000/
# Grafana 13 turns the renderAuthJWT feature toggle on by default, which
# makes the renderer reject requests unless both sides share a token that
# is not the "-" default. Must match AUTH_TOKEN on the renderer service.
- GF_RENDERING_RENDERER_TOKEN=${GF_RENDERING_RENDERER_TOKEN:-xrpld-local-render}
# SMTP for the critical-tier email receiver and the Slack webhook / email
# address for the contact points all come from the env_file above, which
# injects them straight into the container environment for Grafana to
# expand. They are deliberately NOT duplicated here: a compose-level
# `environment:` entry is interpolated from the host shell and would
# override (and blank out) the env_file values.
ports:
- "3000:3000" # Grafana web UI
volumes:
# Auto-provision Tempo datasource and search filters on startup
- ./grafana/provisioning:/etc/grafana/provisioning:ro
- ./grafana/dashboards:/var/lib/grafana/dashboards:ro
depends_on:
- tempo
- prometheus
- loki
- renderer
networks:
- xrpld-telemetry
# Grafana image renderer: a sidecar that renders panels/dashboards to PNG
# for image export and alerting. Grafana calls it at http://renderer:8081.
renderer:
# Pinned like every other image in this stack so a stack that worked
# yesterday still works today. Upstream maintains only the newest release,
# which is expected to serve all supported Grafana versions.
image: grafana/grafana-image-renderer:v5.12.0
environment:
# Shared secret for the JWT-authenticated render requests Grafana 13
# sends. Must match GF_RENDERING_RENDERER_TOKEN on the grafana service.
- AUTH_TOKEN=${GF_RENDERING_RENDERER_TOKEN:-xrpld-local-render}
ports:
- "8081:8081" # Renderer HTTP endpoint (called by grafana)
networks:
- xrpld-telemetry
# Named volume for Tempo trace storage (WAL and compacted blocks).
# Data persists across container restarts. Remove with:
# docker compose -f docker/telemetry/docker-compose.yml down -v
volumes:
tempo-data:
prometheus-data:
loki-data:
# Isolated bridge network so services communicate by container name
# (e.g., the collector reaches Tempo at http://tempo:4317).
networks:
xrpld-telemetry:
driver: bridge