1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
# Operator-side dashboard stack for the wingfoil end-to-end latency demo.
#
# Brings up Prometheus (metric scrape), Tempo (traces), and Grafana
# (provisioned with both datasources and the latency dashboard). Expects
# ws_server / fix_gw to be running on the host; ws_server's Prometheus
# exporter is reached via host.docker.internal:9091 and its OTLP trace
# push targets host.docker.internal:4318 → Tempo.
#
# Usage:
# docker compose -f examples/latency_e2e/docker-compose.yml up -d
# open http://localhost:3000 (admin/admin; dashboard is auto-loaded)
#
# ws_server:
# WINGFOIL_OTLP_ENDPOINT=http://localhost:4318 \
# cargo run --release --example latency_e2e_ws_server ...
services:
prometheus:
image: prom/prometheus:v2.55.1
ports:
- "9090:9090"
# Long-form bind with `create_host_path: false` so a missing source on
# the host fails fast with "source path does not exist" instead of
# docker silently auto-creating an empty directory there and then
# failing the mount with the misleading "not a directory: Are you
# trying to mount a directory onto a file" error.
volumes:
- type: bind
source: ./prometheus/prometheus.yml
target: /etc/prometheus/prometheus.yml
read_only: true
bind:
create_host_path: false
extra_hosts:
- "host.docker.internal:host-gateway"
tempo:
image: grafana/tempo:2.6.1
command:
ports:
- "3200:3200" # Tempo HTTP API (Grafana datasource talks here)
- "4318:4318" # OTLP / HTTP (ws_server push target)
volumes:
- type: bind
source: ./tempo/tempo.yaml
target: /etc/tempo/tempo.yaml
read_only: true
bind:
create_host_path: false
grafana:
image: grafana/grafana:11.3.0
ports:
- "3000:3000"
environment:
# Override locally if you want to log in as admin:
# GF_SECURITY_ADMIN_PASSWORD=$(openssl rand -hex 16) docker compose up -d
# Default to a long random value so a left-running compose stack
# doesn't sit on `admin/admin`.
GF_SECURITY_ADMIN_PASSWORD: "${GF_SECURITY_ADMIN_PASSWORD:-changeme-set-via-env}"
GF_AUTH_ANONYMOUS_ENABLED: "true"
GF_AUTH_ANONYMOUS_ORG_ROLE: Viewer
GF_AUTH_DISABLE_LOGIN_FORM: "true"
GF_SECURITY_ALLOW_EMBEDDING: "true"
GF_DASHBOARDS_DEFAULT_HOME_DASHBOARD_PATH: /etc/grafana/provisioning/dashboards/latency.json
GF_FEATURE_TOGGLES_ENABLE: "traceqlEditor"
# HTTPS opt-in: defaults to HTTP for local dev. The Pulumi stacks
# set GF_SERVER_PROTOCOL=https in the on-host .env and bind-mount
# /etc/wingfoil/tls into the container. Cert/key must be readable
# by the grafana process (UID 472) — the user_data scripts chmod
# 0644 the key for that reason.
GF_SERVER_PROTOCOL: "${GF_SERVER_PROTOCOL:-http}"
GF_SERVER_CERT_FILE: "${GF_SERVER_CERT_FILE:-}"
GF_SERVER_CERT_KEY: "${GF_SERVER_CERT_KEY:-}"
volumes:
- type: bind
source: ./grafana/provisioning
target: /etc/grafana/provisioning
read_only: true
bind:
create_host_path: false
# The Pulumi stacks (ec2-spot, baremetal) append a second volume
# binding /etc/wingfoil/tls -> /etc/wingfoil/tls so Grafana can
# serve HTTPS using the same cert as ws_server. Local dev keeps
# GF_SERVER_PROTOCOL unset (defaults to http) so no cert mount is
# needed.
depends_on:
- prometheus
- tempo