Grafana¶
Grafana est l'outil principal de visualisation de PerfShop. Il agrège les quatre puits de télémétrie (Prometheus, Loki, Tempo, Pyroscope), expose 10 dashboards livrés (5 Élèves + 5 Formateurs) avec une politique d'accès différenciée, et permet l'exploration interactive en mode Explore.
Source de vérité
Cette page est tirée du bloc grafana dans les fichiers compose, des fichiers de provisioning grafana/provisioning/datasources/*.yml, grafana/provisioning/dashboards/dashboards.yml, et du script d'initialisation grafana-seed/seed.py.
Version pinned¶
PerfShop fige Grafana à la version 12.0.0 (image grafana/grafana:12.0.0). Les autres composants d'observabilité utilisent latest ; Grafana est l'exception, parce que :
- Les 10 dashboards livrés reposent sur des fonctionnalités de panel (flamegraph, traces table, TraceQL editor, format auto-units) introduites en Grafana 11+ et stabilisées en 12.
- Le seed Python utilise l'API legacy
POST /api/folders/{uid}/permissionsqui pourrait évoluer dans une version majeure ultérieure. - Les datasources Pyroscope et Tempo dépendent de plugins natifs dont le format de configuration peut changer.
Une mise à jour vers une version ultérieure devra être validée à la main contre les 10 dashboards.
Configuration via variables d'environnement¶
environment:
- GF_SECURITY_ADMIN_USER=admin
- GF_SECURITY_ADMIN_PASSWORD=${GF_SECURITY_ADMIN_PASSWORD:-perfshop}
- GF_USERS_ALLOW_SIGN_UP=false
- GF_SERVER_ROOT_URL=${PUBLIC_GRAFANA_URL:-http://localhost:3002}
- GF_PANELS_DISABLE_SANITIZE_HTML=true
- GF_AUTH_ANONYMOUS_ENABLED=true
- GF_AUTH_ANONYMOUS_ORG_NAME=Main Org.
- GF_AUTH_ANONYMOUS_ORG_ROLE=Viewer
- GF_USERS_DEFAULT_LANGUAGE=${PERFSHOP_UI_LOCALE:-en-US}
| Variable | Effet |
|---|---|
GF_SECURITY_ADMIN_USER / _PASSWORD |
Compte superadmin Grafana, utilisé par le seed et par le formateur |
GF_USERS_ALLOW_SIGN_UP=false |
Désactive l'auto-inscription depuis l'UI |
GF_SERVER_ROOT_URL |
URL publique utilisée pour les liens absolus (mails, partage, OAuth) |
GF_PANELS_DISABLE_SANITIZE_HTML=true |
Permet aux panels Text d'utiliser du HTML brut (utilisé par les panels « Guide » des dashboards APM) |
GF_AUTH_ANONYMOUS_ENABLED=true |
Accès anonyme activé — clé de la stratégie pédagogique |
GF_AUTH_ANONYMOUS_ORG_ROLE=Viewer |
Les utilisateurs anonymes ont uniquement le rôle Viewer (lecture seule) |
GF_USERS_DEFAULT_LANGUAGE |
Locale UI Grafana (fr-FR ou en-US), pilotée par PERFSHOP_UI_LOCALE |
Le port management interne de Grafana est 3000, exposé sur le port hôte 3002 par défaut (variable GRAFANA_HTTP_PORT).
Le healthcheck Docker interroge /api/health toutes les 10 secondes avec start_period: 30s :
healthcheck:
test: ["CMD-SHELL", "wget -q --spider http://localhost:3000/api/health || exit 1"]
interval: 10s
timeout: 5s
retries: 10
start_period: 30s
Datasources provisionnées¶
Grafana lit automatiquement le dossier grafana/provisioning/datasources/ au démarrage et y déclare quatre datasources, chacune avec un UID stable utilisé par les dashboards JSON.
flowchart LR
G["Grafana 12.0.0"]
G --> P["uid: prometheus<br/>http://prometheus:9090<br/>(default)"]
G --> L["uid: loki<br/>http://perfshop-loki:3100"]
G --> T["uid: tempo<br/>http://perfshop-tempo:3200"]
G --> PY["uid: pyroscope<br/>http://perfshop-pyroscope:4040"]
Datasource Prometheus¶
- name: Prometheus
type: prometheus
uid: prometheus
access: proxy
url: http://prometheus:9090
isDefault: true
editable: true
jsonData:
timeInterval: "5s"
isDefault: true: c'est la datasource sélectionnée par défaut dans l'éditeur de panel et dans Explore.timeInterval: "5s": informe Grafana duscrape_intervalréel pour calibrer correctement les fonctionsrate()etirate()quand l'utilisateur ne précise pas la fenêtre.
Datasource Loki¶
- name: Loki
type: loki
uid: loki
access: proxy
url: http://perfshop-loki:3100
isDefault: false
editable: true
jsonData:
maxLines: 1000
timeout: 60
maxLines: 1000: limite le nombre de lignes retournées par défaut dans les panels logs (peut être surchargé par panel).timeout: 60: timeout de query côté Grafana en secondes.
Datasource Tempo¶
- name: Tempo
type: tempo
uid: tempo
access: proxy
url: http://perfshop-tempo:3200
isDefault: false
editable: true
jsonData:
httpMethod: GET
tracesToLogsV2:
datasourceUid: loki
spanStartTimeShift: "-1m"
spanEndTimeShift: "1m"
tags:
- key: service.name
value: app
filterByTraceID: true
filterBySpanID: false
customQuery: false
tracesToMetrics:
datasourceUid: prometheus
spanStartTimeShift: "-1m"
spanEndTimeShift: "1m"
tags:
- key: service.name
value: app
serviceMap:
datasourceUid: prometheus
search:
hide: false
nodeGraph:
enabled: true
lokiSearch:
datasourceUid: loki
C'est la datasource la plus richement configurée. Trois corrélations croisées sont activées :
tracesToLogsV2→ Loki : depuis n'importe quel span dans la vue trace, on peut cliquer pour ouvrir Loki avec un filtre temporel±1 minautour du span et un filtretraceIDautomatique.tracesToMetrics→ Prometheus : depuis un span, on peut afficher les métriques Prometheus de l'opération sur la même fenêtre temporelle.serviceMap: utilise les métriquestraces_service_graph_*produites par lemetrics_generatorde Tempo et stockées dans Prometheus pour générer la carte des services.nodeGraph: enabled: true: active la visualisation graphe pour les traces.lokiSearch: permet la recherche de logs corrélés directement depuis l'éditeur Tempo.
Datasource Pyroscope¶
- name: Pyroscope
type: grafana-pyroscope-datasource
uid: pyroscope
access: proxy
url: http://perfshop-pyroscope:4040
isDefault: false
editable: true
Configuration minimale — Pyroscope n'a pas de corrélations spéciales à configurer ; le plugin natif de Grafana gère tout.
Dossiers et provisioning des dashboards¶
Les dashboards sont organisés en deux dossiers distincts dans Grafana, avec deux providers de provisioning séparés.
# grafana/provisioning/dashboards/dashboards.yml
apiVersion: 1
providers:
- name: 'PerfShop Dashboards — Élèves'
orgId: 1
folder: 'Élèves'
folderUid: 'perfshop-eleves'
type: file
disableDeletion: false
updateIntervalSeconds: 10
allowUiUpdates: true
options:
path: /etc/grafana/dashboards/eleves
- name: 'PerfShop Dashboards — Formateurs'
orgId: 1
folder: 'Formateurs'
folderUid: 'perfshop-formateurs'
type: file
disableDeletion: false
updateIntervalSeconds: 10
allowUiUpdates: true
options:
path: /etc/grafana/dashboards/formateurs
| Aspect | Dossier Élèves | Dossier Formateurs |
|---|---|---|
folderUid |
perfshop-eleves |
perfshop-formateurs |
| Chemin source | ./grafana/dashboards/eleves/ |
./grafana/dashboards/formateurs/ |
| Nombre de dashboards | 5 | 5 |
| ACL après seed | Hérite du rôle Viewer (visible sans login) | Admin uniquement (posée par le seed) |
updateIntervalSeconds |
10 (Grafana relit les fichiers toutes les 10 s) | 10 |
allowUiUpdates |
true | true |
disableDeletion |
false | false |
Les bind mounts dans le compose :
volumes:
- grafana-data:/var/lib/grafana
- ./grafana/provisioning:/etc/grafana/provisioning
- ./grafana/dashboards:/etc/grafana/dashboards
Stratégie d'accès — l'élément clé¶
PerfShop sépare nettement deux populations d'utilisateurs sur Grafana :
flowchart TB
V["Utilisateur anonyme<br/>(pas de login)"]
A["Admin connecté<br/>(login admin/perfshop)"]
subgraph G["Grafana"]
direction TB
FE["Dossier Élèves<br/>(visible Viewer)"]
FF["Dossier Formateurs<br/>(ACL Admin-only)"]
end
V -->|GF_AUTH_ANONYMOUS_ENABLED=true<br/>Role=Viewer| FE
V -.-|❌ HTTP 403| FF
A -->|✅| FE
A -->|✅| FF
Comment ça marche concrètement¶
GF_AUTH_ANONYMOUS_ENABLED=true+GF_AUTH_ANONYMOUS_ORG_ROLE=Viewerdéclarés en variables d'environnement → tout visiteur arrivant sur l'URL Grafana est automatiquement authentifié comme un utilisateur anonyme avec le rôle Viewer dans l'org "Main Org.".- Par défaut, Grafana applique l'héritage organisationnel : un Viewer voit tous les dossiers de son org.
- Le seed
perfshop-grafana-seedapplique une ACL explicite sur le dossier Formateurs :Admin uniquement. Cette ACL remplace l'héritage Viewer pour ce dossier — résultat, les utilisateurs anonymes voient le dossier Élèves mais pas le dossier Formateurs.
Le seed grafana-seed/seed.py¶
Le service perfshop-grafana-seed est un container python:3.11-slim qui :
- Installe
requests==2.31.0au démarrage. - Attend que Grafana réponde sur
/api/health(boucle retry, timeout 7m30). - Vérifie que le dossier Formateurs est bien provisionné (boucle retry sur
GET /api/folders/{uid}). - Pose l'ACL Admin-only via
POST /api/folders/perfshop-formateurs/permissions:permission: 4= Admin (1=View, 2=Edit, 4=Admin). Un seul item suffit : l'API replace toute l'ACL existante. - Vérifie que l'ACL est effective en faisant un appel sans authentification sur
/api/folders/perfshop-formateurs— doit retourner 401 ou 403. - Configure le dashboard home sur
perfshop-general-v1(« Vue Générale Containers ») viaPATCH /api/org/preferencesavec{"homeDashboardUID": "perfshop-general-v1"}.
Le seed est marqué restart: "no" (one-shot) et depends_on: grafana: condition: service_healthy.
sequenceDiagram
autonumber
participant S as perfshop-grafana-seed
participant G as Grafana
S->>G: GET /api/health<br/>(boucle 5s × 90)
G-->>S: 200 OK
S->>G: GET /api/folders/perfshop-formateurs<br/>(auth admin)<br/>(boucle 5s × 24)
G-->>S: 200 (dossier provisionné)
S->>G: POST /api/folders/perfshop-formateurs/permissions<br/>{"items":[{"role":"Admin","permission":4}]}
G-->>S: 200 OK
S->>G: GET /api/folders/perfshop-formateurs<br/>(SANS auth)
G-->>S: 401 ou 403
Note over S: ✓ ACL effective
S->>G: PATCH /api/org/preferences<br/>{"homeDashboardUID":"perfshop-general-v1"}
G-->>S: 200 OK
Note over S: Sortie code 0
Idempotence du seed
Le seed peut être relancé manuellement à tout moment (docker compose up perfshop-grafana-seed) sans effet indésirable : POST /api/folders/{uid}/permissions remplace l'ACL complète, et PATCH /api/org/preferences est une simple mise à jour.
Volumes et données persistantes¶
| Volume | Montage | Contenu |
|---|---|---|
grafana-data (volume nommé) |
/var/lib/grafana |
Base SQLite Grafana, sessions utilisateurs, plugins installés, dashboards créés à la main, ACL personnalisées |
./grafana/provisioning (bind mount) |
/etc/grafana/provisioning |
Fichiers de provisioning datasources et dashboards (lecture seule en pratique) |
./grafana/dashboards (bind mount) |
/etc/grafana/dashboards |
10 fichiers JSON livrés (Élèves + Formateurs) |
Modifier un dashboard livré
Les dashboards JSON livrés sont rechargés toutes les 10 s par Grafana (updateIntervalSeconds: 10). Une modification faite dans l'UI est conservée pendant 10 s puis écrasée par le contenu du fichier JSON. Pour modifier durablement un dashboard livré, il faut éditer le fichier JSON correspondant dans grafana/dashboards/{eleves|formateurs}/ et le repackager dans le déploiement.
Pour aller plus loin¶
- Vue d'ensemble — flux global d'observabilité
- Dashboards livrés — détail panel par panel des 10 dashboards
- Prometheus — datasource principale
- Loki, Tempo, Pyroscope — autres datasources