fr en

Grafana timelines

Posted on mer. 05 février 2025 in devops

J’utilise Grafana depuis pas mal de temps maintenant. Au début juste dans un cadre profesionnel puis pour mes projets perso. Quand je découvrais un truc sympa, je ramenais ça au boulot et ainsi de suite. Je pense que ça fera bientôt 5 ans maintenant, et pendant toute ce temps, je n’ai, au final, utilisé vraiment que 4 types de graphes sur la multitude que propose grafana : les Time séries, les Camemberts, et les Histograms, et les Gauge. Par exemple, à chaque fois que je veux un graphique à barre, je tripatouille juste des Time séries et ça marche très bien.

J’ai subitement eu envie de voir ce que donnait un autre et comme je ne les avais jamais utilisé je me suis lancé dans l’utilisation des State timeline. Malheureusement, ce n’était pas aussi simple ou direct que pour d’autres types de graphiques, et j’ai dû me battre un peu pour que ça fonctionne. En gros, j’écris cet article surtout parce que je n’ai pas trouvé beaucoup d’informations en ligne, et un article de ce genre m’aurait été très utile.

Je vais commencer par un peu de contexte, quelques exemples d’exportation de ces données, et une courte illustration de ce que j’utilise en ce moment.

Contexte

Comme mentionné plus haut, ces dernières années, j’ai exploré Grafana et Prometheus pour des projets professionnels et personnels. Avec le temps, c’est devenu un réflexe d’ajouter des métriques à chaque programme que j’écris et que je veux faire fonctionner en arrière-plan. La logique est la suivante : une fois que c’est en place et que ça tourne, je configure un système d’alerte et je l’oublie. Dans le meilleur des cas, je n’aurai jamais besoin de lire un journal de logs, et si quelque chose casse, je jetterai simplement un œil à un tableau de bord improvisé, que je sauvegarderai quelque part pour m’y référer en cas de problème ultérieur.

On pourrait dire (et je le dis) qu’il existe deux types de métriques de statut (que vous voudrez consulter via les State timelines).

A list of all the sample of the up metric on a Prometheus interface

Statuts d’exécution des programmes

Du coup j’ai pris l’habitude de rajouter dans tous mes dockers quelques métriques avec un format uniformisé, je configure Prometheus pour qu’il vienne vérifier les dites metrics de temps en temps. Une petite alerte plus tard et je peux globalement oublier ce service jusqu’à ce qu’il explose pour une raison ou une autre.

Prometheus fournit en fait ce genre de métriques par défaut. En effet, pour chaque scrape configuré dans la configuration de Prometheus, il existe une métrique up avec le nom du scrape et l’adresse consultée. Pour chaque paire job et scrape, vous aurez un enregistrement up avec instance=<machine> et address=<scraped addr>, qui vaut 1.0. Si ce n’est pas le cas, le scrape a échoué.

J’utilise cette métrique intégrée pour configurer des alertes afin de savoir si une de mes machines est hors ligne (en regroupant par instance) ou pour suivre si une sonde particulière est en panne (par exemple, si l’exportateur Dovecot ne fonctionne plus).

Vous pouvez, bien sûr, aller plus loin qu’un simple contrôle up ou down et vérifier que le programme que vous avez écrit fonctionne correctement et n’est pas bloqué dans un état zombie, (en vérifiant qu’une certaine métrique augmente toujours par exemple). Mais c’est une autre histoire.

Suivi des phases

Un autre type de métrique est ce que j’appellerais des « phases ». Par exemple, mon chauffe-eau possède différents états : il peut être off, Heating ou Not Heating. Étant donné que ce n’est pas un état binaire, il devient compliqué de le représenter simplement comme présent ou absent, comme le fait Prometheus avec la métrique up.

Je fais cette distinction uniquement pour introduire les deux méthodes que j’utilise pour configurer la surveillance.

Export des métriques

L’export le plus simple est via une Gauge. L’idée ensuite est de rassembler tous les labels qu’on considère actif et de set la Gauge à 1. Entre deux changements d’états, c’est là que c’est un peu délicat, soit il faudra effacer toute les valeurs de la métrique via METRIC.clear() soit il faudra retirer les labels un par un avec quelque chose comme METRIC.labels(label=label).remove().

On peut ainsi passer de cette état :

# HELP my_status
# TYPE my_status gauge
my_status{status="ok"} 1.0
my_status{status="nok"} 0.0

À ceci :

# HELP my_status
# TYPE my_status gauge
my_status{status="ok"} 0.0
my_status{status="nok"} 1.0

Un petit extrait de code, en python, pour produire ces états :

from prometheus_client import Gauge, start_http_server

MY_STATUS = Gauge("my_status", "", ["status"])


def main():
    while True:
        try:
            # do stuf
            MY_STATUS.labels(status="ok").set(1)
            MY_STATUS.labels(status="nok").set(0)
        except Exception:
            MY_STATUS.labels(status="ok").set(0)
            MY_STATUS.labels(status="nok").set(1)


if __name__ == "__main__":
    start_http_server(9100)
    main()

Cette approche est très utile quand il faut suivre des phases.

Et ci dessous, la même chose avec une approche un peu aggressive sur le nettoyage des données entre les boucles de rafraichissement.

MY_STATUS = Gauge("my_status", "", ["key", "value"])


def main():
    while True:
        MY_STATUS.clear()
        for key, value in get_key_value_pairs().items():
            MY_STATUS.labels(key=key, value=value).set(1)

Info

J’ai récemment découvert la métrique Info dans la blibliothèque python pour Prometheus, qui a l’avantage certain de s’auto nettoyer à chaque rafraichissement.

Le code du dessus devient donc simplement :

from prometheus_client import Info, start_http_server

MY_STATUS = Info("my_status", "", ["status"])


def main():
    while True:
        try:
            # do stuf
            MY_STATUS.info(status="ok")
        except Exception:
            MY_STATUS.info(status="nok")


if __name__ == "__main__":
    start_http_server(9100)
    main()

Et le résultat change de my_status{status="ok"} 1.0 à my_status{status="nok"} 1.0 sans de zéros restants.

Mises en garde

En ce qui concerne l’exportation, les deux approches présentent divers avantages.

L’approche avec la métrique Info est beaucoup plus propre et facile à mettre en œuvre, mais elle est en réalité assez délicate. Vous devez soit entasser toutes les étiquettes que vous voulez exporter dans une seule métrique info, soit en créer plusieurs. Étant donné que la métrique Info se nettoie automatiquement, cela signifie aussi que vous devez mettre à jour toutes vos informations d’un coup, ce qui met à jour toutes les métriques associées en même temps.

L’approche avec la métrique Gauge est beaucoup plus flexible, car vous pouvez modifier toutes les valeurs que vous souhaitez à tout moment. Cependant, c’est à vous de nettoyer les anciennes valeurs, sinon elles resteront visibles, et vous vous retrouverez avec un Gauge affichant plusieurs états simultanément.

Passons aux graphiques !

J’ai donc réfléchi un peu à la manière d’exprimer ces données sous forme de graphiques. Avant de commencer, notez que je travaille avec la version 11.5.0 de Grafana et que je n’ai pas testé ma configuration avec des versions antérieures ou ultérieures.

Je vais expliquer les détails dans l’ordre. Comme Grafana offre une fonction de recherche pour trouver les options que vous souhaitez ajuster, je mentionnerai le nom exact de chaque option (au moment de la rédaction) entre guillemets. Je couvrirai également les deux cas adjacents des métriques Gauge et Info.

  1. Dans le menu déroulant à droite, sélectionnez le bon type de graphique, ici State timeline.
  2. Tant que vous êtes dans les options, vous voudrez que votre Color Scheme ne soit pas une variation sur une seule couleur, sinon votre graphique ne sera pas très lisible (je recommande personnellement la palette classique).
  3. Vous souhaiterez probablement ajuster les couleurs dans Value Mapping en configurant les valeurs exactes qui apparaîtront sur le graphique et en sélectionnant les couleurs qui leur seront associées.
  4. Pour configurer correctement le graphique :
    • Configurez le format sur Table (déroulez la ligne du bas).
    • Sélectionnez votre Métrique.
    • Premier embranchement :
      • Info : comme vous n’avez qu’une seule métrique, rien de plus n’est nécessaire.
      • Gauge : ici, vous devez sélectionner les étiquettes que vous souhaitez voir apparaître sur votre graphique. Ajoutez une opération Group by pour cela.
  5. La partie délicate : les données seront en réalité du charabia jusqu’à présent, vous devrez donc ajouter des transformations (dans ma version de Grafana, vous pouvez le faire en sélectionnant l’onglet Transformations au-dessus de la section de création/liste des requêtes) :
    • Gauge : Vous devrez ajouter deux transformations :
      • Grouping to Matrix :
        • Column : la métrique que vous souhaitez voir sur le côté de votre graphique
        • Row : Temps
        • Cell Value : la métrique que vous souhaitez voir sur les bandes de la timeline
      • Convert field type : Pour une raison obscure, la transformation précédente modifie le format de la ligne Row, et vous devrez reconvertir le contenu en type Time pour que le graphique fonctionne.
    • Info : Ici, vous n’aurez besoin d’ajouter que Filter fields by name. C’est à cet endroit que vous ferez la sélection que vous auriez faite pour le Gauge lors de l’étape de création des métriques (étape 4).

And here you go !