Retour au cours

infra / monitoring-observabilite

Grafana : construire des dashboards

Leçon 61 exercice

Explication

Ce que vous allez apprendre

  • Installer Grafana et le connecter à Prometheus comme source de données
  • Provisionner les datasources et les dashboards en fichiers de code plutôt qu'à la main
  • Construire un dashboard avec les panels essentiels : débit, latence, taux d'erreur
  • Rendre un dashboard réutilisable entre plusieurs services grâce aux variables
  • Exporter un dashboard existant en JSON pour le versionner en Git

Dans quel contexte ?

L'équipe backend maîtrise maintenant PromQL, mais chaque investigation d'incident demande de retaper les mêmes requêtes à la main dans l'interface brute de Prometheus. Il faut un endroit permanent, visuel et partagé où toute l'équipe peut voir en un coup d'oeil l'état de mon-api — c'est exactement le rôle de Grafana, créé en 2014 par Torkel Ödegaard et devenu depuis le standard de facto pour la visualisation de métriques.

D'abord, Grafana a besoin d'une source de données

Grafana lui-même ne stocke aucune métrique : il ne fait que se connecter à des sources externes (Prometheus, Loki, MySQL, et bien d'autres) et afficher leurs résultats. La toute première étape consiste donc à déclarer Prometheus comme "datasource".

Deux façons d'y arriver : cliquer dans l'interface web, ou déclarer cette connexion dans un fichier YAML de provisioning. La seconde méthode est systématiquement préférée en production.

Une fois la datasource connectée, pourquoi éviter absolument le clic dans l'interface ?

Configurer Grafana uniquement à la souris ("clickops") fonctionne très bien... jusqu'au jour où le conteneur Grafana redémarre et perd tout, ou jusqu'à ce qu'il faille reproduire exactement la même configuration sur un second environnement. Le provisioning en fichiers résout ce problème une fois pour toutes.

ApprocheSurvit à un redéploiement ?Versionnable en Git ?Reproductible sur un 2e environnement
Configuration via l'UI ("clickops")Non (sauf volume persistant)NonManuel, source d'erreurs
Provisioning YAML/JSONOuiOuiAutomatique

Prérequis

Avoir Prometheus déjà installé et en train de scraper au moins une cible (voir les leçons précédentes) est indispensable : sans données à afficher, un dashboard reste une coquille vide.

Maintenant, la question centrale : quels panels mettre sur un premier dashboard ?

Il est tentant de vouloir tout afficher d'un coup. En réalité, un dashboard efficace commence toujours par les mêmes trois informations : le débit de requêtes, la latence en percentiles (P50/P95/P99), et le taux d'erreur — une préfiguration directe des "golden signals" que tu approfondiras en toute fin de cours.

Bonne pratique

Configure des seuils de couleur (vert/orange/rouge) sur tes panels de taux d'erreur, comme dans l'exemple JSON de cette leçon. Un chiffre nu de "2.3%" ne dit rien d'alarmant à l'oeil ; le même chiffre affiché en orange attire immédiatement l'attention lors d'une astreinte.

Il reste un problème une fois le premier dashboard construit : et s'il y a dix services à surveiller ?

Dupliquer le même dashboard dix fois, un par service, devient vite ingérable à maintenir : la moindre modification doit être répétée dix fois. Les variables de dashboard ($service, $env) résolvent ce problème en transformant les valeurs de labels en menus déroulants dynamiques en haut du dashboard.

Piège fréquent

Une variable de dashboard mal alimentée (par exemple label_values(métrique_inexistante, service)) affiche un menu déroulant vide, sans message d'erreur explicite. Vérifie toujours qu'au moins une série correspond réellement à la métrique utilisée dans la requête de la variable.

Le lien avec la suite

Un dashboard, aussi bien conçu soit-il, ne réveille personne à 3h du matin : il faut le regarder activement pour qu'il serve à quelque chose. La prochaine leçon aborde justement l'automatisation de cette surveillance, avec l'alerting Prometheus et Alertmanager.

Commandes & code

Grafana : construire des dashboards

yaml
# docker-compose.yml
services:
  grafana:
    image: grafana/grafana:11.1.0
    ports:
      - "3000:3000"
    environment:
      - GF_SECURITY_ADMIN_PASSWORD=changeme
    volumes:
      - grafana_data:/var/lib/grafana
      - ./provisioning:/etc/grafana/provisioning:ro   # provisioning as code, voir plus bas

volumes:
  grafana_data:
yaml
# provisioning/datasources/prometheus.yml — déclarer la source de données SANS clic dans l'UI
apiVersion: 1
datasources:
  - name: Prometheus
    type: prometheus
    access: proxy
    url: http://prometheus:9090
    isDefault: true
    jsonData:
      timeInterval: "15s"
json
{
  "title": "API — Vue d'ensemble",
  "panels": [
    {
      "title": "Débit de requêtes",
      "type": "timeseries",
      "targets": [
        { "expr": "sum(rate(http_requests_total[5m])) by (status)", "legendFormat": "{{status}}" }
      ]
    },
    {
      "title": "Latence P50 / P95 / P99",
      "type": "timeseries",
      "targets": [
        { "expr": "histogram_quantile(0.50, sum(rate(http_request_duration_seconds_bucket[5m])) by (le))", "legendFormat": "P50" },
        { "expr": "histogram_quantile(0.95, sum(rate(http_request_duration_seconds_bucket[5m])) by (le))", "legendFormat": "P95" },
        { "expr": "histogram_quantile(0.99, sum(rate(http_request_duration_seconds_bucket[5m])) by (le))", "legendFormat": "P99" }
      ]
    },
    {
      "title": "Taux d'erreur (%)",
      "type": "stat",
      "targets": [
        { "expr": "sum(rate(http_requests_total{status=~\"5..\"}[5m])) / sum(rate(http_requests_total[5m])) * 100" }
      ],
      "fieldConfig": {
        "defaults": { "thresholds": { "steps": [
          { "color": "green", "value": 0 },
          { "color": "orange", "value": 1 },
          { "color": "red", "value": 5 }
        ]}}
      }
    }
  ]
}
yaml
# provisioning/dashboards/dashboards.yml — auto-charger les dashboards JSON au démarrage de Grafana
apiVersion: 1
providers:
  - name: "default"
    folder: "API"
    type: file
    options:
      path: /etc/grafana/provisioning/dashboards/json
promql
# Variable de dashboard : rendre un dashboard réutilisable pour PLUSIEURS services/environnements
# Settings > Variables > name=service, query=label_values(http_requests_total, service)
sum(rate(http_requests_total{service="$service", env="$env"}[5m])) by (status)
# -> $service et $env deviennent des menus déroulants en haut du dashboard
bash
# Export/import d'un dashboard existant en JSON, pour le versionner en Git
curl -s -H "Authorization: Bearer $GRAFANA_TOKEN" \
  http://localhost:3000/api/dashboards/uid/abc123 | jq '.dashboard' > dashboards/api-overview.json

Résumé

  • Provisionner les datasources ET les dashboards via des fichiers YAML/JSON versionnés en Git : jamais de configuration manuelle en prod (le "clickops" ne survit pas à un redéploiement).
  • Un dashboard efficace commence toujours par : débit, latence (P50/P95/P99), taux d'erreur — les "golden signals" appliqués concrètement.
  • Les variables de dashboard ($service, $env) rendent un même dashboard réutilisable sur plusieurs services sans dupliquer les panels.

Exercices pratiques

1 disponible
1

Mission : dix services, un seul dashboard

Objectif : Provisionner Grafana en fichiers versionnés plutôt qu'à la main, et rendre un dashboard réutilisable entre plusieurs services grâce aux variables.

Contexte

L'équipe vient de dupliquer manuellement le même dashboard 10 fois, un par service, en cliquant dans l'UI Grafana. Le conteneur Grafana redémarre après une mise à jour et TOUT disparaît. Ta mission : reconstruire ça correctement, en provisioning, avec un seul dashboard variabilisé.

Résoudre l’exercice →