Atelier fondamental 2 — Grafana : une source, un panneau, un tableau de bord

Pratique guidée18 min
Durée
20 min
Module
1/7
Prérequis
le labo tourne depuis au moins deux minutes (etat affiche 8/8 cibles up), Grafana ouvert sur http://localhost:3000 avec admin / aiopsatlas2026, l'atelier 1 fait (tu sais ce que rend count(up))
Tu vas construire
un tableau de bord d'un seul panneau, créé à la main dans l'interface, enregistré, relu en JSON, puis supprimé
Livrable
le bloc JSON du panneau tel que Grafana l'a enregistré (étape 9), avec trois mots entourés : le type de visualisation, la requête, la source de données

Comment lire cette page. Dix étapes, un geste à la fois. Pour chacune : où cliquer, ce que Grafana affiche mot pour mot, et ce qu'il faut regarder. Les libellés de l'interface sont en anglais dans Grafana 13.2.2 (celui du kit) ; ils sont cités tels quels, en gras. Les chiffres seront différents chez toi ; les formes (nombre de séries, valeur du panneau, messages) doivent être les mêmes. Les blocs « Pour bien comprendre » sont facultatifs. Si le labo n'est pas démarré, retourne à la pratique guidée : la section En bref donne les commandes, kit compris (https://github.com/hrhouma2/aiopsatlas-observabilite-labo-fr). Tout ce que tu crées ici est détruit à l'étape 10 : les trois tableaux de bord livrés avec le kit ne sont pas touchés.

Objectif

Dans la pratique guidée, tu as tapé huit requêtes dans Explore et regardé trois tableaux de bord déjà faits. Tu sais donc lire Grafana. Mais si on te demande d'ajouter un chiffre sur un tableau, sais-tu par où commencer ? Ici, tu repars de zéro et tu assembles toi-même les trois objets de Grafana, du plus petit au plus grand : une source de données (Grafana ne stocke rien, il interroge Prometheus), un panneau (une requête plus une façon de la dessiner), un tableau de bord (des panneaux rangés sur une grille, avec un nom et une adresse). Une étape est un piège volontaire : tu casses une requête pour voir comment l'erreur de Prometheus traverse Grafana. À la fin, tu lis le JSON que Grafana a écrit pour toi et tu y retrouves les trois objets, puis tu supprimes tout.

Le vocabulaire en une image

Grafana est un cadre photo numérique. Il ne prend aucune photo : il va les chercher chez un photographe (la source de données : Prometheus, Loki) et les affiche. Chaque photo est un panneau : une question posée au photographe (la requête) et un format d'affichage (un chiffre, une courbe, une jauge). Le cadre lui-même, avec ses photos disposées sur une grille, c'est le tableau de bord : il a un nom, une adresse, et il est enregistré dans la base de Grafana sous la forme d'un document JSON.

GrafanaBase SQL classiqueDans cet atelier
source de données (data source)la connexion à la basePrometheus (http://prometheus:9090)
requête (query)SELECT …count(up)
panneau (panel)une vue enregistréeCibles surveillées
visualisationla façon d'afficher le résultatStat (un grand chiffre)
tableau de bord (dashboard)un rapport qui assemble plusieurs vuesAtelier M1 - Cibles Prometheus
uidclé primaireadc947c (le tien sera différent)
JSON du tableaule schéma exporté du rapportétape 9
dossier (folder)schéma de la baseDashboards (la racine) ; Labo observabilite pour les trois du kit

Où cliquer, et comment lire une réponse

Ouvre http://localhost:3000. Le menu principal est l'icône à trois traits en haut à gauche : il donne accès à Dashboards, Explore et Connections. Le fil d'Ariane en haut (par exemple Dashboards › New dashboard) dit toujours où tu es. Quand une action réussit, Grafana affiche pendant quelques secondes un bandeau en bas à droite (un toast) : Dashboard saved, Dashboard deleted. Quand une requête échoue, il affiche une boîte rouge sous le champ de requête, avec le message de Prometheus recopié tel quel.

Une fois le tableau enregistré, tu le reliras en JSON par l'API de Grafana. Dans PowerShell ou dans un terminal Linux, la même commande :

bash
curl -s -u admin:aiopsatlas2026 http://localhost:3000/api/dashboards/uid/<uid>

<uid> est l'identifiant que Grafana a donné à ton tableau ; tu le liras dans l'adresse de la page à l'étape 8.

Étape 1 — Vérifier la source de données

Menu principal → ConnectionsData sources. Trois sources sont listées : Alertmanager, Loki et Prometheus (marquée default). Clique Prometheus. La page Settings s'ouvre, avec Prometheus server URL rempli : http://prometheus:9090. Ne change rien. Descends tout en bas et clique Save & test.

text
Successfully queried the Prometheus API.
Next, you can start to visualize data by building a dashboard from scratch or by querying data in the Explore view.

À regarder : le bandeau vert. Grafana vient d'envoyer une requête à Prometheus et Prometheus a répondu. C'est le premier objet : une source de données, c'est une adresse et un type. L'adresse est http://prometheus:9090 et non http://localhost:9090 : Grafana tourne dans un conteneur, et depuis ce conteneur, Prometheus s'appelle prometheus. Ton navigateur, lui, l'atteint par localhost:9090. Deux noms pour la même machine, selon qui parle.

Pour bien comprendre : qui a créé ces trois sources ?

Toi, non. Le kit les décrit dans grafana/provisioning/datasources/sources.yml et Grafana les lit au démarrage : c'est le provisioning. Le fichier est court, ouvre-le : name: Prometheus, uid: prometheus, type: prometheus, url: http://prometheus:9090, isDefault: true. Sans ce fichier, la première chose à faire dans un Grafana neuf serait Add new data source, et tu taperais ces quatre lignes à la main. Le uid: prometheus te resservira à l'étape 9 : c'est par lui que ton panneau désignera sa source.

Étape 2 — Interroger la source dans Explore

Menu principal → Explore. En haut, le sélecteur de source affiche Prometheus. À droite du champ, passe en Code (et non Builder). Tape :

promql
up

puis Run query (ou Maj+Entrée).

text
up{instance="alertmanager:9093", job="alertmanager"}
up{instance="alloy:12345", job="alloy"}
up{instance="api:8000", job="api", service="api"}
up{instance="cadvisor:8080", job="cadvisor"}
up{instance="grafana:3000", job="grafana"}
up{instance="localhost:9090", job="prometheus"}
up{instance="loki:3100", job="loki"}
up{instance="node-exporter:9100", job="node-exporter"}

À regarder : huit lignes dans la légende sous le graphique, les huit séries de l'étape 2 de l'atelier 1, dans l'ordre alphabétique de instance. La requête est exactement celle que tu as tapée dans Prometheus ; Grafana l'a transmise et a dessiné la réponse. Explore est un brouillon : rien n'y est enregistré, c'est là qu'on met une requête au point avant de la poser sur un panneau.

Étape 3 — Le piège : une parenthèse en moins

Toujours dans Explore, remplace la requête par une version fausse, exprès :

promql
count(up

Run query :

text
bad_data: invalid parameter "query": 1:9: parse error: unclosed left parenthesis

À regarder : une boîte rouge sous le champ, et No data dans le graphique. Le message n'est pas de Grafana : parse error et la position 1:9 sont ceux de Prometheus, que tu as appris à lire dans l'atelier 1. Grafana préfixe seulement bad_data:, la catégorie d'erreur renvoyée par l'API de Prometheus. Règle : quand un panneau Grafana affiche une erreur PromQL, la corriger dans Prometheus d'abord (http://localhost:9090), puis recopier dans Grafana. Ferme la parenthèse, Run query : une seule ligne dans la légende, que Grafana nomme count(up) faute de labels (Prometheus, lui, écrivait {}), à 8. C'est la requête que tu vas poser sur ton panneau.

Étape 4 — Créer un tableau de bord vide

Menu principal → Dashboards. La page liste un seul dossier, Labo observabilite, qui contient les trois tableaux du kit. En haut à droite, NewNew dashboard.

text
New dashboard
Add a panel to visualize your data

À regarder : le fil d'Ariane dit Dashboards › New dashboard, la page est vide, et le bouton Save est déjà là en haut à droite. Rien n'est encore enregistré : si tu fermes l'onglet maintenant, il ne reste rien. Un tableau de bord n'existe qu'à partir du moment où il est sauvegardé (étape 8). À droite, un panneau latéral Add propose Panel (« Drag or click to add a panel »).

Étape 5 — Ajouter un panneau sans requête

Dans le panneau latéral Add, clique Panel. Un cadre apparaît sur la grille, titré New panel, avec le texte No visualization configured. À droite, le champ Title contient New panel : remplace-le par :

text
Cibles surveillées

À regarder : le titre du cadre change en direct. Tu as un panneau, mais il est vide : ni requête, ni visualisation. C'est le deuxième objet, réduit à son minimum : un emplacement sur la grille et un nom. Clique Edit visualization dans le panneau latéral : la page Edit panel s'ouvre, avec en bas l'onglet Queries 1 et, à droite, Suggestions / All visualizations.

Étape 6 — Poser la requête

Dans Queries, la ligne A est déjà reliée à Data source : Prometheus (la source par défaut). Passe en Code, tape dans le champ Enter a PromQL query… :

promql
count(up)

puis Run queries (ou Maj+Entrée).

text
Suggestions
Time series · Stat · Gauge · Bar gauge · Table · State timeline · Heatmap · Histogram

À regarder : le volet de droite change : sous Suggestions, Grafana propose une dizaine de visualisations, et chaque vignette affiche déjà ta donnée : count(up) et 8. Le panneau du haut, lui, dit encore « Run a query to visualize it here or go to all visualizations » : il a la requête, il n'a pas encore choisi comment la dessiner. Requête et visualisation sont deux réglages séparés d'un même panneau.

Pour bien comprendre : Builder ou Code ?

Builder construit la requête avec des menus (choisir la métrique, ajouter un label, empiler une fonction). Code te laisse la taper. Les deux produisent la même chaîne PromQL, et tu peux passer de l'un à l'autre. Dans ce cours, on reste en Code : la requête que tu tapes ici est mot pour mot celle de Prometheus, et tu la retrouveras telle quelle dans le JSON de l'étape 9 ("editorMode": "code").

Étape 7 — Choisir la visualisation

Dans Suggestions, clique la vignette Stat.

text
Stat
Value options
  Calculate | All values
  Calculation: Last *
Thresholds
  80  (rouge)
  Base  (vert)

À regarder : le panneau du haut affiche maintenant un grand 8 vert, avec la petite courbe de fond. Le volet de droite liste les options de Stat, dont deux à connaître : sous Value options, Calculate est coché et Calculation vaut Last * (le panneau montre la dernière valeur de la série, pas une moyenne) ; sous Thresholds, deux paliers, Base en vert et 80 en rouge : la valeur passerait au rouge à partir de 80. Ces seuils sont ceux que Grafana met par défaut sur tout nouveau panneau ; ils n'ont aucun sens pour un compte de cibles (tu verrais rouge à 80 cibles), tu les lirais et les changerais dans un vrai tableau. Ici, laisse-les : ce qui compte est de les reconnaître dans le JSON.

Étape 8 — Enregistrer : un nom, une adresse

En haut à droite, Save. Le dialogue Save dashboard s'ouvre, avec deux onglets, Details et Changes 3, et trois champs : Title (New dashboard), Description, Folder (Dashboards).

À regarder d'abord : efface le titre et clique Save : le mot Required apparaît en rouge sous Title et le bouton Save se grise. Un tableau de bord a obligatoirement un nom. Tape :

text
Atelier M1 - Cibles Prometheus

Le bouton se réactive. Clique Save.

text
Dashboard saved

À regarder ensuite : le bandeau Dashboard saved, puis l'adresse de la page :

text
http://localhost:3000/d/adc947c/atelier-m1-cibles-prometheus?orgId=1&from=now-6h&to=now&timezone=browser

Trois choses dans cette adresse. adc947c est le uid, tiré au hasard par Grafana : c'est l'identité du tableau, note-le. atelier-m1-cibles-prometheus est le titre mis en forme d'adresse ; il ne sert qu'à la lisibilité. from=now-6h&to=now est la période affichée, Last 6 hours par défaut pour un tableau neuf. Le crayon en haut à droite (Edit) a remplacé Save : tu es en lecture. Menu principal → Dashboards : ton tableau est dans la liste, à la racine, à côté du dossier Labo observabilite.

Pour bien comprendre : l'onglet Changes 3

Avant de cliquer Save, ouvre Changes 3 : Grafana montre, en JSON, la différence entre un tableau neuf et celui que tu enregistres. Le chiffre est le nombre de modifications qu'il a comptées ; sur un tableau vide tout juste ouvert, l'onglet dit Changes 1. C'est la première fois que tu vois le JSON qui sera écrit ; l'étape 9 te le fait relire en entier.

Étape 9 — Relire le JSON : les trois objets sont dedans

Dans un terminal, avec ton uid à la place de adc947c :

bash
curl -s -u admin:aiopsatlas2026 http://localhost:3000/api/dashboards/uid/adc947c

La réponse est un document JSON. Sa partie meta :

json
"meta": {
  "slug": "atelier-m1-cibles-prometheus",
  "url": "/d/adc947c/atelier-m1-cibles-prometheus",
  "created": "2026-09-15T21:21:02Z",
  "version": 1,
  "folderTitle": "General"
}

Et dans dashboard, le tableau panels contient un seul élément, ton panneau (raccourci aux champs qui comptent) :

json
{
  "type": "stat",
  "title": "Cibles surveillées",
  "datasource": { "type": "prometheus", "uid": "prometheus" },
  "targets": [
    {
      "datasource": { "type": "prometheus", "uid": "prometheus" },
      "editorMode": "code",
      "expr": "count(up)",
      "legendFormat": "__auto",
      "range": true,
      "refId": "A"
    }
  ],
  "fieldConfig": {
    "defaults": {
      "color": { "mode": "thresholds" },
      "thresholds": {
        "mode": "absolute",
        "steps": [
          { "color": "green", "value": 0 },
          { "color": "red", "value": 80 }
        ]
      }
    }
  },
  "options": {
    "colorMode": "value",
    "graphMode": "area",
    "reduceOptions": { "calcs": ["lastNotNull"], "fields": "", "values": false }
  },
  "gridPos": { "h": 8, "w": 12, "x": 0, "y": 0 },
  "id": 1,
  "pluginVersion": "13.2.2"
}

À regarder : les trois objets de l'atelier, écrits noir sur blanc. La source : "datasource": { "type": "prometheus", "uid": "prometheus" }, le uid du fichier de provisioning de l'étape 1. La requête : "expr": "count(up)", dans targets, avec "refId": "A" (la lettre de la ligne dans Queries) et "editorMode": "code". La visualisation : "type": "stat", et ses réglages de l'étape 7 : "calcs": ["lastNotNull"] est le Last * de l'écran, steps sont les deux Thresholds, green à la base et red à 80. gridPos dit où est le cadre sur la grille : en haut à gauche, 12 colonnes de large sur 24, 8 lignes de haut. Un tableau de bord Grafana n'est que ça : ce document, enregistré sous un uid. C'est ton livrable : ce bloc, avec stat, count(up) et prometheus entourés.

Pour bien comprendre : le même JSON dans l'interface

Sur la page du tableau, Edit (le crayon) → dans la barre latérale droite, Options (la roue) → View all settings → onglet JSON Model. C'est le même document, sans la partie meta, et tu peux le modifier là puis Save. C'est aussi ce document que le kit livre pour ses trois tableaux, dans grafana/provisioning/dashboards/*.json : ouvre api-catalogue.json et cherche "expr" : tu y liras des requêtes de la pratique guidée, exactement au format ci-dessus. Un tableau de bord se partage en envoyant ce fichier.

Étape 10 — Tout supprimer, et le prouver

Sur la page du tableau, EditOptionsView all settings. La page Settings s'ouvre (onglets General, Annotations, Variables, Links, Versions, Permissions, JSON Model). Tout en bas, le bouton rouge Delete dashboard.

text
Delete
Do you want to delete this dashboard?
Atelier M1 - Cibles Prometheus
Type "Delete" to confirm

À regarder : le bouton Delete du dialogue est grisé tant que tu n'as pas tapé le mot Delete dans le champ. Tape-le, clique Delete.

text
Dashboard deleted
View deleted dashboards

Grafana te ramène à la page d'accueil. Preuve par l'API, avec ton uid :

bash
curl -s -u admin:aiopsatlas2026 http://localhost:3000/api/dashboards/uid/adc947c
json
{"message":"Dashboard not found"}

Et la liste complète des tableaux de bord :

bash
curl -s -u admin:aiopsatlas2026 "http://localhost:3000/api/search?type=dash-db"

Trois entrées, les trois du kit, toutes dans le dossier Labo observabilite : api-catalogue (« API catalogue — signaux dorés »), hote-conteneurs (« Hôte et conteneurs »), journaux-api (« Journaux de l'API »). Le tien n'y est plus. Le dialogue de suppression le disait : Grafana garde les tableaux supprimés dans un historique jusqu'à douze mois (Recently deleted sur la page Dashboards), d'où ils peuvent être restaurés ; pour ce que tu fais dans ce cours, supprimé veut dire supprimé.

Vérification finale

Reprends les dix gestes de mémoire, dans l'ordre, et coche :

  • Connections → Data sources → Prometheus → Save & test rend Successfully queried the Prometheus API.
  • Dans Explore, up en mode Code rend 8 séries.
  • count(up rend une boîte rouge bad_data: … parse error: unclosed left parenthesis ; count(up) rend {} et 8.
  • Dashboards → New → New dashboard ouvre une page New dashboard vide, avec Save déjà visible.
  • Add → Panel crée New panel ; le champ Title renomme le cadre en direct.
  • count(up) puis Run queries remplit Suggestions avec la valeur 8 sur chaque vignette.
  • La vignette Stat affiche un grand 8 ; Calculation vaut Last * ; Thresholds : Base vert, 80 rouge.
  • Titre vide : Required et Save grisé ; titre Atelier M1 - Cibles Prometheus : bandeau Dashboard saved et un uid dans l'adresse.
  • curl … /api/dashboards/uid/<uid> contient "type": "stat", "expr": "count(up)", "uid": "prometheus".
  • Après Delete dashboard, le même curl rend {"message":"Dashboard not found"} et /api/search?type=dash-db liste exactement trois tableaux.

Côté labo, rien n'a bougé : etat affiche toujours 10/10 services, 8/8 cibles up, 0 alertes actives. Grafana a écrit puis effacé une ligne dans sa propre base ; Prometheus n'a rien vu passer.

Si ça coince

Afficher les cas où ça coince

La page de connexion s'affiche et admin / admin est refusé. Le mot de passe du kit est aiopsatlas2026 (fixé dans docker-compose.yml, variable GF_SECURITY_ADMIN_PASSWORD). En ligne de commande, le même couple : -u admin:aiopsatlas2026 ; avec un mauvais mot de passe, l'API rend {"message":"Invalid username or password", …, "statusCode":401}.

Save & test rend une erreur au lieu du bandeau vert. Prometheus est arrêté ou en train de redémarrer : etat, puis attends que labo-prometheus soit healthy et réessaie. Si tu as changé l'URL par erreur, remets http://prometheus:9090 (pas localhost : depuis le conteneur Grafana, localhost c'est Grafana lui-même).

Explore rend No data sur up sans boîte rouge. Regarde le sélecteur de source en haut : tu es peut-être sur Loki (la pratique guidée t'y a laissé). Repasse sur Prometheus. Vérifie aussi la période en haut à droite : elle doit finir à now.

Le champ de requête refuse ce que je tape, ou le texte apparaît deux fois. Tu es en Builder : le champ libre n'existe qu'en Code. Bascule, efface, retape.

Le panneau reste sur « Run a query to visualize it here ». La requête est écrite mais n'a pas été lancée : Run queries ou Maj+Entrée. Si le volet Suggestions reste vide après ça, la requête a une erreur : la boîte rouge est sous le champ, descends.

Le Save du dialogue reste grisé, avec Required sous le titre. Le titre est vide. Tape-en un. Si tu as déjà enregistré un tableau du même nom à un essai précédent, Grafana l'accepte quand même : deux tableaux peuvent porter le même titre, ils ont des uid différents. Tu auras alors deux entrées dans la liste ; supprime celle en trop (étape 10).

Je ne retrouve pas le uid. Il est dans l'adresse, juste après /d/ : http://localhost:3000/d/adc947c/…. Sinon, curl -s -u admin:aiopsatlas2026 "http://localhost:3000/api/search?query=Atelier" rend le champ "uid" de chaque tableau dont le titre contient Atelier.

curl rend {"message":"Dashboard not found"} à l'étape 9 alors que le tableau est à l'écran. Le uid est mal recopié (majuscules, caractère en trop). Recopie-le depuis l'adresse.

J'ai supprimé un tableau du kit par erreur. Il est dans Dashboards → Recently deleted : restaure-le. Sinon, docker compose restart grafana : le provisioning relit grafana/provisioning/dashboards/ au démarrage et recrée les trois tableaux depuis leurs fichiers JSON (tableaux-de-bord.yml, updateIntervalSeconds: 30).

J'ai modifié un tableau du kit et Save refuse. Normal : allowUiUpdates: false dans tableaux-de-bord.yml. Les trois tableaux du kit se lisent, ne s'écrasent pas ; enregistre ta version sous un autre nom.