Skip to content

Commit bb9bcbb

Browse files
authored
Merge pull request #606 from MerginMaps/develop
Release 2026.3.2
2 parents c0c23f7 + 2166263 commit bb9bcbb

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+2263
-1070
lines changed

deployment/common/README.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
## Running telemetry stack
2+
3+
To run telemetry stack, run
4+
```
5+
cd deployment/common/telemetry
6+
docker compose -f docker-compose.otel.yml up -d
7+
```
8+
9+
If you want to use your own opentelemetry collector you need to modify variables in .otel.env which are used in merginmaps server and celery workers.
10+
11+
Grafana UI is accesible on port 3000 but it can be exposed via mergin nginx proxy (uncomment in nginx.conf).

deployment/common/nginx.conf

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ server {
3636
# redirects, we set the Host: header above already.
3737
proxy_redirect off;
3838
proxy_pass http://merginmaps-server:5000;
39+
proxy_hide_header X-Trace-Id;
3940

4041
# disable buffering
4142
client_max_body_size 0; # No maximum client body size
@@ -49,6 +50,7 @@ server {
4950
proxy_set_header X-Forwarded-Proto $scheme;
5051
proxy_set_header Host $http_host;
5152
proxy_pass http://merginmaps-server:5000;
53+
proxy_hide_header X-Trace-Id;
5254
}
5355

5456
location /download/ {
@@ -59,3 +61,25 @@ server {
5961
}
6062
}
6163

64+
# if needed to expose granafa
65+
# server {
66+
# listen 8082;
67+
# listen [::]:8082;
68+
# server_name _;
69+
70+
# client_max_body_size 4G;
71+
72+
# # Don't show version information
73+
# server_tokens off;
74+
75+
# location / {
76+
# proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
77+
# proxy_set_header X-Forwarded-Proto $scheme;
78+
# proxy_set_header Host $http_host;
79+
# # we don't want nginx trying to do something clever with
80+
# # redirects, we set the Host: header above already.
81+
# proxy_redirect off;
82+
# proxy_pass http://merginmaps-grafana:3000;
83+
# }
84+
# }
85+
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
loki-data
2+
dashboards
3+
alloy-data
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
OTEL_METRICS_EXPORTER=otlp
2+
OTEL_TRACES_EXPORTER=otlp
3+
OTEL_LOGS_EXPORTER=otlp
4+
OTEL_EXPORTER_OTLP_ENDPOINT=http://merginmaps-otel-collector:4317
5+
OTEL_EXPORTER_OTLP_LOGS_ENDPOINT=http://merginmaps-otel-collector:4317
6+
OTEL_METRIC_EXPORT_INTERVAL=10000
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
discovery.docker "containers" {
2+
host = "unix:///var/run/docker.sock"
3+
}
4+
5+
discovery.relabel "containers" {
6+
targets = discovery.docker.containers.targets
7+
8+
rule {
9+
source_labels = ["__meta_docker_container_name"]
10+
regex = "/(.*)"
11+
target_label = "container_name"
12+
}
13+
14+
rule {
15+
source_labels = ["__meta_docker_container_log_stream"]
16+
target_label = "stream"
17+
}
18+
19+
rule {
20+
source_labels = ["__meta_docker_container_image_name"]
21+
target_label = "image"
22+
}
23+
}
24+
25+
loki.source.docker "containers" {
26+
host = "unix:///var/run/docker.sock"
27+
targets = discovery.relabel.containers.output
28+
forward_to = [loki.write.default.receiver]
29+
}
30+
31+
loki.write "default" {
32+
endpoint {
33+
url = "http://merginmaps-loki:3100/loki/api/v1/push"
34+
}
35+
}
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
networks:
2+
merginmaps:
3+
name: mergin
4+
external: true
5+
6+
services:
7+
otel-collector:
8+
# Contrib version is required for StatsD
9+
image: otel/opentelemetry-collector-contrib:0.90.0
10+
container_name: merginmaps-otel-collector
11+
volumes:
12+
- ./otel-config.yaml:/etc/otelcol-contrib/config.yaml
13+
networks:
14+
- merginmaps
15+
ports:
16+
- "8125:8125/udp" # StatsD (Metrics)
17+
- "4317:4317" # OTLP (Traces)
18+
- "8889:8889" # Prometheus Scrape Port
19+
- "55679:55679"
20+
depends_on:
21+
tempo:
22+
condition: service_started
23+
24+
alloy:
25+
image: grafana/alloy:v1.8.3
26+
container_name: merginmaps-alloy
27+
volumes:
28+
- ./alloy-config.alloy:/etc/alloy/config.alloy
29+
- /var/run/docker.sock:/var/run/docker.sock
30+
- ./alloy-data:/var/lib/alloy/data
31+
networks:
32+
- merginmaps
33+
depends_on:
34+
- loki
35+
command: run --server.http.listen-addr=0.0.0.0:12345 --storage.path=/var/lib/alloy/data /etc/alloy/config.alloy
36+
37+
tempo:
38+
image: grafana/tempo:2.8.3
39+
container_name: merginmaps-tempo
40+
command: ["-config.file=/etc/tempo.yaml", "-target=all"]
41+
networks:
42+
- merginmaps
43+
volumes:
44+
- ./tempo.yaml:/etc/tempo.yaml
45+
# - ./tempo-data:/tmp/tempo
46+
ports:
47+
- "3200:3200" # Tempo UI/API
48+
49+
prometheus:
50+
image: prom/prometheus:v3.9.0
51+
container_name: merginmaps-prometheus
52+
networks:
53+
- merginmaps
54+
volumes:
55+
- ./prometheus.yml:/etc/prometheus/prometheus.yml
56+
ports:
57+
- "9090:9090"
58+
59+
loki:
60+
image: grafana/loki:3.6.4
61+
container_name: merginmaps-loki
62+
ports:
63+
- "3100:3100"
64+
user: "root"
65+
volumes:
66+
- ./loki-config.yaml:/etc/loki/local-config.yaml
67+
- ./loki-data:/loki # Persistent storage
68+
command: -config.file=/etc/loki/local-config.yaml
69+
networks:
70+
- merginmaps
71+
72+
grafana:
73+
image: grafana/grafana:12.3.2
74+
container_name: merginmaps-grafana
75+
networks:
76+
- merginmaps
77+
ports:
78+
- "3000:3000"
79+
environment:
80+
- GF_AUTH_ANONYMOUS_ENABLED=true
81+
- GF_AUTH_ANONYMOUS_ORG_ROLE=Admin
82+
volumes:
83+
- ./grafana-datasource.yaml:/etc/grafana/provisioning/datasources/datasource.yaml
84+
- ./dashboards:/var/lib/grafana/dashboards
85+
depends_on:
86+
- loki
87+
- prometheus
88+
- tempo
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
apiVersion: 1
2+
datasources:
3+
- name: Prometheus
4+
type: prometheus
5+
url: http://merginmaps-prometheus:9090
6+
isDefault: true
7+
- name: Tempo
8+
type: tempo
9+
url: http://merginmaps-tempo:3200
10+
- name: Loki
11+
type: loki
12+
url: http://merginmaps-loki:3100
13+
jsonData:
14+
derivedFields:
15+
- datasourceUid: Tempo
16+
matcherRegex: '\[(?:ACCESS|INFO|ERROR)\]\s\[(\w{32})\]' # Finds the trace_id in the log JSON/metadata
17+
name: TraceID
18+
url: '$${__value.raw}' # The raw ID value used to query Tempo
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
auth_enabled: false
2+
3+
server:
4+
http_listen_port: 3100
5+
grpc_listen_port: 9096
6+
7+
common:
8+
instance_addr: 0.0.0.0
9+
path_prefix: /loki
10+
storage:
11+
filesystem:
12+
chunks_directory: /loki/chunks
13+
rules_directory: /loki/rules
14+
replication_factor: 1
15+
ring:
16+
kvstore:
17+
store: inmemory
18+
19+
schema_config:
20+
configs:
21+
- from: 2024-01-01
22+
store: tsdb
23+
object_store: filesystem
24+
schema: v13
25+
index:
26+
prefix: index_
27+
period: 24h
28+
29+
limits_config:
30+
reject_old_samples: false
31+
reject_old_samples_max_age: 336h
32+
allow_structured_metadata: true
33+
otlp_config:
34+
resource_attributes:
35+
attributes_config:
36+
- action: index_label
37+
attributes:
38+
- container_name
39+
- image
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
extensions:
2+
health_check:
3+
4+
receivers:
5+
otlp:
6+
protocols:
7+
grpc:
8+
endpoint: "0.0.0.0:4317"
9+
http:
10+
endpoint: "0.0.0.0:4318"
11+
statsd:
12+
endpoint: "0.0.0.0:8125"
13+
aggregation_interval: 10s
14+
enable_metric_type: true
15+
redis:
16+
endpoint: "merginmaps-redis:6379"
17+
collection_interval: 10s
18+
#password: "${REDIS_PASSWORD}"
19+
processors:
20+
batch:
21+
transform:
22+
metric_statements:
23+
- context: metric
24+
statements:
25+
- set(name, "mergin_gunicorn_workers") where name == "app.gunicorn.workers"
26+
- set(name, "mergin_gunicorn_request_duration") where name == "app.gunicorn.request.duration"
27+
# these metrics are not working
28+
- set(name, "mergin_gunicorn_request_rate") where name == "app.gunicorn.requests"
29+
- set(name, "mergin_gunicorn_log_critical") where name == "gunicorn.log.critical"
30+
- set(name, "mergin_gunicorn_log_error") where name == "gunicorn.log.error"
31+
- set(name, "mergin_gunicorn_log_warning") where name == "gunicorn.log.warning"
32+
- set(name, "mergin_gunicorn_log_exception") where name == "gunicorn.log.exception"
33+
- set(name, "mergin_gunicorn_response_code_200") where name == "app.gunicorn.request.status.200"
34+
# log_statements:
35+
# - context: log
36+
# statements:
37+
# - set(attributes["service_name"], attributes["docker_id"])
38+
# - set(resource.attributes["service.name"], attributes["docker_id"])
39+
exporters:
40+
prometheus:
41+
endpoint: "0.0.0.0:8889" # The Collector will "host" metrics here
42+
resource_to_telemetry_conversion:
43+
enabled: true # Converts OTel resource attributes to Prometheus labels
44+
add_metric_suffixes: true
45+
otlp:
46+
endpoint: "merginmaps-tempo:4317"
47+
tls:
48+
insecure: true
49+
service:
50+
extensions: [health_check]
51+
telemetry:
52+
metrics:
53+
address: 0.0.0.0:8888 # This enables the /metrics port
54+
logs:
55+
level: "warn"
56+
pipelines:
57+
metrics:
58+
receivers: [otlp, statsd, redis]
59+
processors: [transform, batch]
60+
exporters: [prometheus]
61+
traces:
62+
receivers: [otlp]
63+
processors: [batch]
64+
exporters: [otlp]
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
global:
2+
scrape_interval: 15s # How often to scrape targets
3+
evaluation_interval: 15s
4+
5+
# Enable the Exemplars feature in the storage engine
6+
# storage:
7+
# tsdb:
8+
# out_of_order_time_window: 0s # Standard for local dev
9+
# exemplars:
10+
# max_exemplars: 100000
11+
12+
scrape_configs:
13+
# 1. Scrape the OTel Collector (where Gunicorn metrics are hosted)
14+
- job_name: 'otel-collector'
15+
static_configs:
16+
- targets: ['merginmaps-otel-collector:8889']
17+
# This allows Prometheus to pull TraceIDs from the Collector
18+
# and attach them to the metrics as Exemplars.
19+
# metric_relabel_configs:
20+
# - source_labels: [__name__]
21+
# separator: ;
22+
# regex: 'mergin_gunicorn.*'
23+
# replacement: $1
24+
# action: keep
25+
26+
# 2. Optional: Scrape Prometheus itself
27+
- job_name: 'prometheus'
28+
static_configs:
29+
- targets: ['localhost:9090']
30+
31+
# 3. Optional: Scrape Tempo (to monitor your tracing backend)
32+
- job_name: 'tempo'
33+
static_configs:
34+
- targets: ['merginmaps-tempo:3200']

0 commit comments

Comments
 (0)