bring back files after directory rename

This commit is contained in:
przemyslaw
2025-07-22 15:49:11 +02:00
parent f0effaa360
commit 845d02f70b
27 changed files with 1572 additions and 0 deletions

View File

@@ -0,0 +1,30 @@
Example 1 — Grafana by itself
===
No additional resources are needed to run this example.
Execute the following command to start Grafana:
```bash
docker run -i -t --rm -p 3000:3000 grafana/grafana:12.0.2
```
You can then access Grafana at `http://localhost:3000` with the default credentials (username: `admin`, password: `admin`).
##### Custom login credentials
```bash
docker run -i -t --rm -p 3000:3000 \
-e GF_SECURITY_ADMIN_USER=a \
-e GF_SECURITY_ADMIN_PASSWORD=a \
grafana/grafana:12.0.2
```
##### Custom login credentials and preinstalled plugin
```bash
docker run -i -t --rm -p 3000:3000 \
-e GF_SECURITY_ADMIN_USER=a \
-e GF_SECURITY_ADMIN_PASSWORD=a \
-e "GF_INSTALL_PLUGINS=grafana-sentry-datasource" \
grafana/grafana:12.0.2
```

View File

@@ -0,0 +1,6 @@
Example 2 — Grafana with Prometheus (metrics data)
===
```bash
docker-compose up
```

View File

@@ -0,0 +1,56 @@
services:
prometheus:
image: prom/prometheus:latest
container_name: prometheus
ports:
- "9090:9090"
volumes:
- ./prometheus/prometheus.yml:/etc/prometheus/prometheus.yml
- prometheus_data:/prometheus
command:
- '--config.file=/etc/prometheus/prometheus.yml'
- '--storage.tsdb.path=/prometheus'
- '--web.console.libraries=/etc/prometheus/console_libraries'
- '--web.console.templates=/etc/prometheus/consoles'
- '--storage.tsdb.retention.time=200h'
- '--web.enable-lifecycle'
networks:
- grafana_network
grafana:
image: grafana/grafana:12.0.2
container_name: grafana
ports:
- "3000:3000"
environment:
- GF_AUTH_ANONYMOUS_ENABLED=true
- GF_AUTH_ANONYMOUS_ORG_ROLE=Admin
- GF_AUTH_DISABLE_LOGIN_FORM=true
- GF_USERS_ALLOW_SIGN_UP=false
volumes:
- grafana_data:/var/lib/grafana
- ./grafana/provisioning:/etc/grafana/provisioning
depends_on:
- prometheus
networks:
- grafana_network
# Example microservice with metrics endpoint
metrics_generator:
image: prom/node-exporter:latest
container_name: example_metrics
ports:
- "9100:9100"
networks:
- grafana_network
volumes:
grafana_data:
driver: local
prometheus_data:
driver: local
networks:
grafana_network:
driver: bridge

View File

@@ -0,0 +1,10 @@
apiVersion: 1
providers:
- name: 'default'
orgId: 1
folder: ''
type: file
disableDeletion: false
updateIntervalSeconds: 10
options:
path: /etc/grafana/provisioning/dashboards

View File

@@ -0,0 +1,418 @@
{
"annotations": {
"list": [
{
"builtIn": 1,
"datasource": {
"type": "grafana",
"uid": "-- Grafana --"
},
"enable": true,
"hide": true,
"iconColor": "rgba(0, 211, 255, 1)",
"name": "Annotations & Alerts",
"type": "dashboard"
}
]
},
"editable": true,
"fiscalYearStartMonth": 0,
"graphTooltip": 0,
"id": 1,
"links": [],
"panels": [
{
"datasource": {
"type": "prometheus",
"uid": "PBFA97CFB590B2093"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"axisBorderShow": false,
"axisCenteredZero": false,
"axisColorMode": "text",
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"barWidthFactor": 0.6,
"drawStyle": "line",
"fillOpacity": 0,
"gradientMode": "none",
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
},
"insertNulls": false,
"lineInterpolation": "stepBefore",
"lineWidth": 4,
"pointSize": 5,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "auto",
"spanNulls": false,
"stacking": {
"group": "A",
"mode": "none"
},
"thresholdsStyle": {
"mode": "off"
}
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green"
},
{
"color": "red",
"value": 80
}
]
}
},
"overrides": []
},
"gridPos": {
"h": 8,
"w": 12,
"x": 0,
"y": 0
},
"id": 2,
"options": {
"legend": {
"calcs": [],
"displayMode": "list",
"placement": "bottom",
"showLegend": true
},
"tooltip": {
"hideZeros": false,
"mode": "single",
"sort": "none"
}
},
"pluginVersion": "12.0.2",
"targets": [
{
"disableTextWrap": false,
"editorMode": "builder",
"expr": "go_memstats_heap_objects",
"fullMetaSearch": false,
"includeNullMetadata": true,
"legendFormat": "__auto",
"range": true,
"refId": "A",
"useBackend": false
}
],
"title": "Heap objects",
"type": "timeseries"
},
{
"datasource": {
"type": "prometheus",
"uid": "PBFA97CFB590B2093"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"axisBorderShow": false,
"axisCenteredZero": false,
"axisColorMode": "text",
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"barWidthFactor": 0.6,
"drawStyle": "line",
"fillOpacity": 0,
"gradientMode": "none",
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
},
"insertNulls": false,
"lineInterpolation": "linear",
"lineWidth": 1,
"pointSize": 5,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "auto",
"spanNulls": false,
"stacking": {
"group": "A",
"mode": "none"
},
"thresholdsStyle": {
"mode": "off"
}
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green"
},
{
"color": "red",
"value": 80
}
]
}
},
"overrides": []
},
"gridPos": {
"h": 17,
"w": 12,
"x": 12,
"y": 0
},
"id": 4,
"options": {
"legend": {
"calcs": [],
"displayMode": "list",
"placement": "bottom",
"showLegend": true
},
"tooltip": {
"hideZeros": false,
"mode": "single",
"sort": "none"
}
},
"pluginVersion": "12.0.2",
"targets": [
{
"disableTextWrap": false,
"editorMode": "builder",
"expr": "node_load1",
"fullMetaSearch": false,
"includeNullMetadata": true,
"legendFormat": "__auto",
"range": true,
"refId": "A",
"useBackend": false
},
{
"datasource": {
"type": "prometheus",
"uid": "PBFA97CFB590B2093"
},
"disableTextWrap": false,
"editorMode": "builder",
"expr": "node_load5",
"fullMetaSearch": false,
"hide": false,
"includeNullMetadata": true,
"instant": false,
"legendFormat": "__auto",
"range": true,
"refId": "B",
"useBackend": false
},
{
"datasource": {
"type": "prometheus",
"uid": "PBFA97CFB590B2093"
},
"disableTextWrap": false,
"editorMode": "builder",
"expr": "node_load15",
"fullMetaSearch": false,
"hide": false,
"includeNullMetadata": true,
"instant": false,
"legendFormat": "__auto",
"range": true,
"refId": "C",
"useBackend": false
}
],
"title": "Load avg",
"type": "timeseries"
},
{
"datasource": {
"type": "prometheus",
"uid": "PBFA97CFB590B2093"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "thresholds"
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green"
},
{
"color": "red",
"value": 80
}
]
}
},
"overrides": []
},
"gridPos": {
"h": 9,
"w": 8,
"x": 0,
"y": 8
},
"id": 1,
"options": {
"minVizHeight": 75,
"minVizWidth": 75,
"orientation": "auto",
"reduceOptions": {
"calcs": [
"lastNotNull"
],
"fields": "",
"values": false
},
"showThresholdLabels": false,
"showThresholdMarkers": true,
"sizing": "auto"
},
"pluginVersion": "12.0.2",
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "PBFA97CFB590B2093"
},
"disableTextWrap": false,
"editorMode": "builder",
"expr": "go_goroutines",
"fullMetaSearch": false,
"includeNullMetadata": true,
"legendFormat": "__auto",
"range": true,
"refId": "A",
"useBackend": false
}
],
"title": "Go routines",
"type": "gauge"
},
{
"datasource": {
"type": "prometheus",
"uid": "PBFA97CFB590B2093"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "thresholds"
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green"
},
{
"color": "red",
"value": 80
}
]
}
},
"overrides": [
{
"__systemRef": "hideSeriesFrom",
"matcher": {
"id": "byNames",
"options": {
"mode": "exclude",
"names": [
"{__name__=\"node_nfs_packets_total\", instance=\"metrics_generator:9100\", job=\"metrics_generator\", protocol=\"tcp\"}"
],
"prefix": "All except:",
"readOnly": true
}
},
"properties": []
}
]
},
"gridPos": {
"h": 9,
"w": 4,
"x": 8,
"y": 8
},
"id": 3,
"options": {
"colorMode": "value",
"graphMode": "area",
"justifyMode": "auto",
"orientation": "auto",
"percentChangeColorMode": "standard",
"reduceOptions": {
"calcs": [
"lastNotNull"
],
"fields": "",
"values": false
},
"showPercentChange": false,
"textMode": "auto",
"wideLayout": true
},
"pluginVersion": "12.0.2",
"targets": [
{
"disableTextWrap": false,
"editorMode": "builder",
"expr": "process_open_fds",
"fullMetaSearch": false,
"includeNullMetadata": true,
"legendFormat": "__auto",
"range": true,
"refId": "A",
"useBackend": false
}
],
"title": "Open file descriptors",
"type": "stat"
}
],
"preload": false,
"schemaVersion": 41,
"tags": [],
"templating": {
"list": []
},
"time": {
"from": "now-5m",
"to": "now"
},
"timepicker": {},
"timezone": "browser",
"title": "Example dashboard with system metrics",
"uid": "2e48b630-2206-4876-b606-2132a21d1934",
"version": 5
}

View File

@@ -0,0 +1,8 @@
apiVersion: 1
datasources:
- name: Prometheus
type: prometheus
access: proxy
url: http://prometheus:9090
isDefault: true

View File

@@ -0,0 +1,14 @@
global:
scrape_interval: 15s
evaluation_interval: 15s
scrape_configs:
# - job_name: 'prometheus'
# static_configs:
# - targets: ['localhost:9090']
- job_name: 'metrics_generator'
static_configs:
- targets: ['metrics_generator:9100']
scrape_interval: 2s
scrape_timeout: 1s

View File

@@ -0,0 +1,6 @@
Example 3 — Grafana with Loki (logs data)
===
```bash
docker-compose up
```

View File

@@ -0,0 +1,55 @@
services:
grafana:
image: grafana/grafana:12.0.2
container_name: grafana
ports:
- "3000:3000"
environment:
- GF_AUTH_ANONYMOUS_ENABLED=true
- GF_AUTH_ANONYMOUS_ORG_ROLE=Admin
- GF_AUTH_DISABLE_LOGIN_FORM=true
- GF_USERS_ALLOW_SIGN_UP=false
volumes:
- grafana_data:/var/lib/grafana
- ./grafana/provisioning:/etc/grafana/provisioning
depends_on:
- loki
networks:
- grafana_network
loki:
image: grafana/loki:latest
container_name: loki
ports:
- "3100:3100"
volumes:
- ./loki:/etc/loki
- loki_data:/loki
command: -config.file=/etc/loki/loki.yml
networks:
- grafana_network
log-generator:
build:
context: ./log-generator
dockerfile: Dockerfile
container_name: log-generator
networks:
- grafana_network
depends_on:
- loki
volumes:
grafana_data:
driver: local
prometheus_data:
driver: local
loki_data:
driver: local
networks:
grafana_network:
driver: bridge

View File

@@ -0,0 +1,10 @@
apiVersion: 1
providers:
- name: 'default'
orgId: 1
folder: ''
type: file
disableDeletion: false
updateIntervalSeconds: 10
options:
path: /etc/grafana/provisioning/dashboards

View File

@@ -0,0 +1,274 @@
{
"annotations": {
"list": [
{
"builtIn": 1,
"datasource": {
"type": "grafana",
"uid": "-- Grafana --"
},
"enable": true,
"hide": true,
"iconColor": "rgba(0, 211, 255, 1)",
"name": "Annotations & Alerts",
"type": "dashboard"
}
]
},
"editable": true,
"fiscalYearStartMonth": 0,
"graphTooltip": 0,
"id": 1,
"links": [],
"panels": [
{
"datasource": {
"type": "loki",
"uid": "P8E80F9AEF21F6940"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
}
},
"mappings": []
},
"overrides": []
},
"gridPos": {
"h": 29,
"w": 4,
"x": 0,
"y": 0
},
"id": 3,
"options": {
"legend": {
"displayMode": "list",
"placement": "bottom",
"showLegend": true
},
"pieType": "pie",
"reduceOptions": {
"calcs": [
"lastNotNull"
],
"fields": "",
"values": false
},
"tooltip": {
"hideZeros": false,
"mode": "single",
"sort": "none"
}
},
"pluginVersion": "12.0.2",
"targets": [
{
"direction": "backward",
"editorMode": "code",
"expr": "sum by(service_name) (rate({job=\"log-generator\"} [$__auto]))",
"queryType": "range",
"refId": "A"
}
],
"title": "Logs per service",
"type": "piechart"
},
{
"datasource": {
"type": "loki",
"uid": "P8E80F9AEF21F6940"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "thresholds"
},
"custom": {
"align": "auto",
"cellOptions": {
"type": "auto"
},
"filterable": true,
"inspect": false
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green"
},
{
"color": "red",
"value": 80
}
]
}
},
"overrides": []
},
"gridPos": {
"h": 13,
"w": 20,
"x": 4,
"y": 0
},
"id": 1,
"options": {
"cellHeight": "md",
"footer": {
"countRows": false,
"enablePagination": true,
"fields": "",
"reducer": [
"sum"
],
"show": false
},
"showHeader": true
},
"pluginVersion": "12.0.2",
"targets": [
{
"datasource": {
"type": "loki",
"uid": "P8E80F9AEF21F6940"
},
"direction": "backward",
"editorMode": "builder",
"expr": "{severity=\"error\"}",
"queryType": "range",
"refId": "A"
}
],
"title": "Errors",
"type": "table"
},
{
"datasource": {
"type": "loki",
"uid": "P8E80F9AEF21F6940"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "thresholds"
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green"
},
{
"color": "red",
"value": 80
}
]
}
},
"overrides": []
},
"gridPos": {
"h": 7,
"w": 4,
"x": 4,
"y": 13
},
"id": 2,
"options": {
"minVizHeight": 75,
"minVizWidth": 75,
"orientation": "auto",
"reduceOptions": {
"calcs": [
"lastNotNull"
],
"fields": "",
"values": false
},
"showThresholdLabels": false,
"showThresholdMarkers": true,
"sizing": "auto"
},
"pluginVersion": "12.0.2",
"targets": [
{
"direction": "backward",
"editorMode": "builder",
"expr": "sum by(severity) (rate({severity=\"critical\"} [$__auto]))",
"queryType": "range",
"refId": "A"
}
],
"title": "Error log rate",
"type": "gauge"
},
{
"datasource": {
"type": "loki",
"uid": "P8E80F9AEF21F6940"
},
"fieldConfig": {
"defaults": {},
"overrides": []
},
"gridPos": {
"h": 16,
"w": 16,
"x": 8,
"y": 13
},
"id": 4,
"options": {
"dedupStrategy": "none",
"enableInfiniteScrolling": false,
"enableLogDetails": true,
"prettifyLogMessage": false,
"showCommonLabels": false,
"showLabels": false,
"showTime": true,
"sortOrder": "Descending",
"wrapLogMessage": false
},
"pluginVersion": "12.0.2",
"targets": [
{
"direction": "backward",
"editorMode": "builder",
"expr": "{job=\"log-generator\"}",
"queryType": "range",
"refId": "A"
}
],
"title": "Log tail",
"transparent": true,
"type": "logs"
}
],
"preload": false,
"schemaVersion": 41,
"tags": [],
"templating": {
"list": []
},
"time": {
"from": "now-6h",
"to": "now"
},
"timepicker": {},
"timezone": "browser",
"title": "Simple logs dashboard",
"uid": "a86bbd08-0669-4d22-9c03-f9eacfa00788",
"version": 6
}

View File

@@ -0,0 +1,8 @@
apiVersion: 1
datasources:
- name: Loki
type: loki
access: proxy
url: http://loki:3100
isDefault: false

View File

@@ -0,0 +1,11 @@
FROM golang:alpine AS builder
ADD logger.go /logger.go
RUN go build -o /service /logger.go
FROM scratch
COPY --from=builder /service .
ENTRYPOINT [ "/service" ]

View File

@@ -0,0 +1,76 @@
// Copyright Quesma, licensed under the Elastic License 2.0.
// SPDX-License-Identifier: Elastic-2.0
package main
import (
"bytes"
"encoding/json"
"fmt"
"log"
"math/rand"
"net/http"
"time"
)
const url = "http://loki:3100/loki/api/v1/push"
func main() {
hostNames := []string{"zeus", "cassandra", "hercules",
"oracle", "athena", "jupiter", "poseidon", "hades", "artemis", "apollo", "demeter",
"dionysus", "hephaestus", "hermes", "hestia", "iris", "nemesis", "pan", "persephone", "prometheus", "selen"}
serviceNames := []string{"frontend", "backend", "database", "cache", "queue", "monitoring", "loadbalancer", "proxy",
"storage", "auth", "api", "web", "worker", "scheduler", "cron", "admin", "service", "gateway", "service", "service", "service"}
sourceNames := []string{"kubernetes", "ubuntu", "debian", "centos", "redhat", "fedora", "arch", "gentoo", "alpine", "suse",
"rhel", "coreos", "docker", "rancher", "vmware", "xen", "hyperv", "openstack", "aws", "gcp", "azure", "digitalocean"}
severityNames := []string{"info", "info", "info", "info", "info", "info", "warning", "error", "critical", "debug", "debug", "debug"}
messageNames := []string{"User logged in", "User logged out", "User created", "User deleted", "User updated",
"User password changed", "User password reset", "User password reset requested", "User password reset failed"}
for {
time.Sleep(time.Duration(1000+rand.Intn(2000)) * time.Millisecond)
timestamp := time.Now()
severity := severityNames[rand.Intn(len(severityNames))]
source := sourceNames[rand.Intn(len(sourceNames))]
serviceName := serviceNames[rand.Intn(len(serviceNames))]
hostName := hostNames[rand.Intn(len(hostNames))]
message := messageNames[rand.Intn(len(messageNames))]
// Create Loki push request format
logEntry := map[string]interface{}{
"streams": []map[string]interface{}{
{
"stream": map[string]string{
"severity": severity,
"source": source,
"service_name": serviceName,
"host_name": hostName,
"job": "log-generator",
},
"values": [][]string{
{
fmt.Sprintf("%d", timestamp.UnixNano()),
message,
},
},
},
},
}
body, err := json.Marshal(logEntry)
if err != nil {
log.Fatal(err)
}
resp, err := http.Post(url, "application/json", bytes.NewBuffer(body))
if err != nil {
log.Fatal(err)
}
resp.Body.Close()
}
}

View File

@@ -0,0 +1,42 @@
auth_enabled: false
server:
http_listen_port: 3100
ingester:
lifecycler:
address: 127.0.0.1
ring:
kvstore:
store: inmemory
replication_factor: 1
final_sleep: 0s
chunk_idle_period: 5m
chunk_retain_period: 30s
wal:
dir: /loki/wal
schema_config:
configs:
- from: 2020-10-24
store: tsdb
object_store: filesystem
schema: v13
index:
prefix: index_
period: 24h
storage_config:
tsdb_shipper:
active_index_directory: /loki/tsdb-shipper-active
cache_location: /loki/tsdb-shipper-cache
filesystem:
directory: /loki/chunks
limits_config:
reject_old_samples: true
reject_old_samples_max_age: 168h
compactor:
working_directory: /loki/tsdb-shipper-compactor

View File

@@ -0,0 +1,6 @@
Example 4 — Grafana with Tempo (distributed tracing data)
===
```bash
docker-compose up
```

View File

@@ -0,0 +1,56 @@
services:
grafana:
image: grafana/grafana:12.0.2
ports:
- "3000:3000"
environment:
- GF_AUTH_ANONYMOUS_ENABLED=true
- GF_AUTH_ANONYMOUS_ORG_ROLE=Admin
- GF_AUTH_DISABLE_LOGIN_FORM=true
- GF_USERS_ALLOW_SIGN_UP=false
volumes:
- grafana_data:/var/lib/grafana
- ./grafana/provisioning:/etc/grafana/provisioning
depends_on:
- tempo
networks:
- grafana_network
tempo:
image: grafana/tempo:latest
command: ["-config.file=/etc/tempo.yaml"]
volumes:
- ./tempo/tempo.yml:/etc/tempo.yaml
- ./tempo/data:/var/tempo
ports:
- "14268:14268" # jaeger ingest
- "3200:3200" # tempo
- "9095:9095" # tempo grpc
- "4317:4317" # otlp grpc
- "4318:4318" # otlp http
- "9411:9411" # zipkin
networks:
- grafana_network
k6-tracing:
image: ghcr.io/grafana/xk6-client-tracing:v0.0.7
environment:
- ENDPOINT=tempo:4317
restart: always
depends_on:
- tempo
networks:
- grafana_network
volumes:
grafana_data:
driver: local
prometheus_data:
driver: local
networks:
grafana_network:
driver: bridge

View File

@@ -0,0 +1,10 @@
apiVersion: 1
providers:
- name: 'default'
orgId: 1
folder: ''
type: file
disableDeletion: false
updateIntervalSeconds: 10
options:
path: /etc/grafana/provisioning/dashboards

View File

@@ -0,0 +1,8 @@
apiVersion: 1
datasources:
- name: Tempo
type: tempo
access: proxy
url: http://tempo:3200
isDefault: false

View File

@@ -0,0 +1,91 @@
stream_over_http_enabled: true
server:
http_listen_port: 3200
log_level: info
cache:
background:
writeback_goroutines: 5
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: 100ms
metrics:
max_duration: 200h # maximum duration of a metrics query, increase for local setups
query_backend_after: 5m
duration_slo: 5s
throughput_bytes_slo: 1.073741824e+09
distributor:
usage:
cost_attribution:
enabled: true
receivers: # this configuration will listen on all ports and protocols that tempo is capable of.
jaeger: # the receives all come from the OpenTelemetry collector. more configuration information can
protocols: # be found there: https://github.com/open-telemetry/opentelemetry-collector/tree/main/receiver
thrift_http: #
endpoint: "tempo:14268" # for a production deployment you should only enable the receivers you need!
grpc:
endpoint: "tempo:14250"
thrift_binary:
endpoint: "tempo:6832"
thrift_compact:
endpoint: "tempo:6831"
zipkin:
endpoint: "tempo:9411"
otlp:
protocols:
grpc:
endpoint: "tempo:4317"
http:
endpoint: "tempo:4318"
opencensus:
endpoint: "tempo:55678"
ingester:
max_block_duration: 5m # cut the headblock when this much time passes. this is being set for demo purposes and should probably be left alone normally
compactor:
compaction:
block_retention: 720h # overall Tempo trace retention. set for demo purposes
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
processor:
local_blocks:
filter_server_spans: false
flush_to_storage: true
storage:
trace:
cache: ""
backend: local # backend configuration to use
wal:
path: /var/tempo/wal # where to store the wal locally
local:
path: /var/tempo/blocks
overrides:
defaults:
cost_attribution:
dimensions:
service.name: ""
metrics_generator:
processors: [service-graphs, span-metrics, local-blocks] # enables metrics generator
generate_native_histograms: both

View File

@@ -0,0 +1,6 @@
Example 5 — Grafana with Pyroscope (profiling data)
===
```bash
docker-compose up
```

View File

@@ -0,0 +1,44 @@
services:
grafana:
image: grafana/grafana:12.0.2
ports:
- "3000:3000"
environment:
- GF_INSTALL_PLUGINS=grafana-pyroscope-app
- GF_AUTH_ANONYMOUS_ENABLED=true
- GF_AUTH_ANONYMOUS_ORG_ROLE=Admin
- GF_AUTH_DISABLE_LOGIN_FORM=true
- GF_USERS_ALLOW_SIGN_UP=false
volumes:
- grafana_data:/var/lib/grafana
- ./grafana/provisioning:/etc/grafana/provisioning
depends_on:
- pyroscope
networks:
- grafana_network
pyroscope:
image: grafana/pyroscope:latest
ports:
- 4040:4040
networks:
- grafana_network
sample_app:
build:
context: ./sample_app
dockerfile: Dockerfile
environment:
- PYROSCOPE_SERVER_ADDRESS=http://pyroscope:4040
networks:
- grafana_network
volumes:
grafana_data:
driver: local
networks:
grafana_network:
driver: bridge

View File

@@ -0,0 +1,10 @@
apiVersion: 1
providers:
- name: 'default'
orgId: 1
folder: ''
type: file
disableDeletion: false
updateIntervalSeconds: 10
options:
path: /etc/grafana/provisioning/dashboards

View File

@@ -0,0 +1,13 @@
FROM golang:1.23.11
WORKDIR /go/src/app
COPY main.go go.mod go.sum ./
RUN go get -d ./
RUN go build -o main .
RUN adduser --disabled-password --gecos --quiet pyroscope
USER pyroscope
CMD ["./main"]

View File

@@ -0,0 +1,10 @@
module sample_app
go 1.23.0
require github.com/grafana/pyroscope-go v1.2.4
require (
github.com/grafana/pyroscope-go/godeltaprof v0.1.8 // indirect
github.com/klauspost/compress v1.17.11 // indirect
)

View File

@@ -0,0 +1,6 @@
github.com/grafana/pyroscope-go v1.2.4 h1:B22GMXz+O0nWLatxLuaP7o7L9dvP0clLvIpmeEQQM0Q=
github.com/grafana/pyroscope-go v1.2.4/go.mod h1:zzT9QXQAp2Iz2ZdS216UiV8y9uXJYQiGE1q8v1FyhqU=
github.com/grafana/pyroscope-go/godeltaprof v0.1.8 h1:iwOtYXeeVSAeYefJNaxDytgjKtUuKQbJqgAIjlnicKg=
github.com/grafana/pyroscope-go/godeltaprof v0.1.8/go.mod h1:2+l7K7twW49Ct4wFluZD3tZ6e0SjanjcUUBPVD/UuGU=
github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc=
github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0=

View File

@@ -0,0 +1,288 @@
package main
import (
"context"
"crypto/rand"
"encoding/json"
"fmt"
"log"
"math"
"os"
"runtime/pprof"
"sort"
"strconv"
"strings"
"sync"
"time"
"github.com/grafana/pyroscope-go"
)
// Simulate different types of workloads
//go:noinline
func cpuIntensiveWork(n int) {
// CPU-bound work with actual computation
sum := 0.0
for i := 0; i < n; i++ {
sum += math.Sqrt(float64(i)) * math.Sin(float64(i))
}
_ = sum // prevent optimization
}
//go:noinline
func memoryIntensiveWork(size int) {
// Memory allocation and manipulation
data := make([][]byte, size)
for i := range data {
data[i] = make([]byte, 1024)
rand.Read(data[i])
}
// Some processing to prevent optimization
for i := range data {
for j := range data[i] {
data[i][j] = byte((int(data[i][j]) + i + j) % 256)
}
}
}
//go:noinline
func recursiveFibonacci(n int) int {
if n <= 1 {
return n
}
return recursiveFibonacci(n-1) + recursiveFibonacci(n-2)
}
//go:noinline
func recursiveFactorial(n int) int {
if n <= 1 {
return 1
}
return n * recursiveFactorial(n-1)
}
//go:noinline
func stringProcessing(iterations int) {
var builder strings.Builder
for i := 0; i < iterations; i++ {
builder.WriteString(fmt.Sprintf("iteration-%d-", i))
}
// JSON marshaling/unmarshaling
data := map[string]interface{}{
"message": builder.String(),
"count": iterations,
"nested": map[string]int{
"a": 1, "b": 2, "c": 3,
},
}
jsonData, _ := json.Marshal(data)
var result map[string]interface{}
json.Unmarshal(jsonData, &result)
}
//go:noinline
func sortingWork(size int) {
// Create random data
data := make([]int, size)
for i := range data {
data[i] = int(time.Now().UnixNano()) % 10000
}
// Multiple sorting algorithms
data1 := make([]int, len(data))
copy(data1, data)
sort.Ints(data1)
// Bubble sort for smaller datasets (inefficient by design)
if size <= 1000 {
data2 := make([]int, len(data))
copy(data2, data)
bubbleSort(data2)
}
}
//go:noinline
func bubbleSort(arr []int) {
n := len(arr)
for i := 0; i < n-1; i++ {
for j := 0; j < n-i-1; j++ {
if arr[j] > arr[j+1] {
arr[j], arr[j+1] = arr[j+1], arr[j]
}
}
}
}
//go:noinline
func networkSimulation(requests int) {
// Simulate network-like delays and processing
for i := 0; i < requests; i++ {
// Simulate variable latency
latency := time.Duration(10+i%50) * time.Microsecond
time.Sleep(latency)
// Simulate request processing
processRequest(i)
}
}
//go:noinline
func processRequest(id int) {
// Simulate request processing with string operations
request := fmt.Sprintf("request-%d", id)
parts := strings.Split(request, "-")
if len(parts) > 1 {
num, _ := strconv.Atoi(parts[1])
_ = num * 2
}
}
//go:noinline
func concurrentWork(ctx context.Context, workers int) {
var wg sync.WaitGroup
for i := 0; i < workers; i++ {
wg.Add(1)
go func(workerID int) {
defer wg.Done()
pyroscope.TagWrapper(ctx, pyroscope.Labels("worker", fmt.Sprintf("worker-%d", workerID)), func(c context.Context) {
// Each worker does different types of work
switch workerID % 3 {
case 0:
cpuIntensiveWork(1000000)
case 1:
memoryIntensiveWork(500)
case 2:
stringProcessing(10000)
}
})
}(i)
}
wg.Wait()
}
// Different function types for varied flame graph patterns
func fastFunction(c context.Context) {
pyroscope.TagWrapper(c, pyroscope.Labels("function", "fast", "type", "cpu"), func(c context.Context) {
cpuIntensiveWork(1000000)
})
}
func slowFunction(c context.Context) {
pprof.Do(c, pprof.Labels("function", "slow", "type", "mixed"), func(c context.Context) {
cpuIntensiveWork(5000000)
memoryIntensiveWork(200)
})
}
func recursiveFunction(c context.Context) {
pyroscope.TagWrapper(c, pyroscope.Labels("function", "recursive", "type", "fibonacci"), func(c context.Context) {
// Fibonacci creates deep call stacks
result := recursiveFibonacci(35)
_ = result
})
}
func memoryFunction(c context.Context) {
pyroscope.TagWrapper(c, pyroscope.Labels("function", "memory", "type", "allocation"), func(c context.Context) {
memoryIntensiveWork(1000)
})
}
func stringFunction(c context.Context) {
pyroscope.TagWrapper(c, pyroscope.Labels("function", "string", "type", "processing"), func(c context.Context) {
stringProcessing(50000)
})
}
func sortingFunction(c context.Context) {
pyroscope.TagWrapper(c, pyroscope.Labels("function", "sorting", "type", "algorithms"), func(c context.Context) {
sortingWork(5000)
})
}
func networkFunction(c context.Context) {
pyroscope.TagWrapper(c, pyroscope.Labels("function", "network", "type", "simulation"), func(c context.Context) {
networkSimulation(100)
})
}
func mathFunction(c context.Context) {
pyroscope.TagWrapper(c, pyroscope.Labels("function", "math", "type", "factorial"), func(c context.Context) {
result := recursiveFactorial(15)
_ = result
})
}
func concurrentFunction(c context.Context) {
pyroscope.TagWrapper(c, pyroscope.Labels("function", "concurrent", "type", "goroutines"), func(c context.Context) {
concurrentWork(c, 10)
})
}
func main() {
serverAddress := os.Getenv("PYROSCOPE_SERVER_ADDRESS")
if serverAddress == "" {
serverAddress = "http://localhost:4040"
}
_, err := pyroscope.Start(pyroscope.Config{
ApplicationName: "sample_app",
ServerAddress: serverAddress,
Logger: pyroscope.StandardLogger,
Tags: map[string]string{
"version": "2.0",
"env": "demo",
},
})
if err != nil {
log.Fatalf("error starting pyroscope profiler: %v", err)
}
// Main execution loop with varied workloads
pyroscope.TagWrapper(context.Background(), pyroscope.Labels("phase", "main"), func(c context.Context) {
iteration := 0
for {
iteration++
// Create varied execution patterns
switch iteration % 9 {
case 0:
fastFunction(c)
case 1:
slowFunction(c)
case 2:
recursiveFunction(c)
case 3:
memoryFunction(c)
case 4:
stringFunction(c)
case 5:
sortingFunction(c)
case 6:
networkFunction(c)
case 7:
mathFunction(c)
case 8:
concurrentFunction(c)
}
// Occasional heavy computation phase
if iteration%20 == 0 {
pyroscope.TagWrapper(c, pyroscope.Labels("phase", "heavy"), func(c context.Context) {
cpuIntensiveWork(10000000)
memoryIntensiveWork(2000)
})
}
// Short pause to make the profiling more readable
time.Sleep(10 * time.Millisecond)
}
})
}