DevOps and Infrastructure

Moderniser la surveillance des systèmes hérités : Guide pour créer des exportateurs Prometheus personnalisés

Introduction

Dans le domaine de l'infrastructure moderne, Prometheus est devenu la norme de facto pour la collecte de métriques et l'alerting. Cependant, de nombreuses organisations s'appuient encore sur des systèmes « hérités » — applications monolithiques, mainframes ou logiciels propriétaires — qui ne prennent pas en charge nativement le format d'exposition de Prometheus. Ces systèmes utilisent souvent des pièges SNMP, des journaux propriétaires ou des réponses HTTP simples, incompatibles avec les piles d'observabilité modernes. Tenter d'interroger directement ces systèmes est souvent impossible en raison de contraintes de sécurité ou de limitations de protocole. La solution réside dans la création d'exportateurs Prometheus personnalisés. Un exportateur est un service léger qui s'intercale entre votre infrastructure héritée et Prometheus, traduisant les anciens formats de données en métriques standardisées que Prometheus comprend. Cet article vous guidera à travers l'architecture, la mise en œuvre et les meilleures pratiques pour créer ces ponts.

Comprendre l'architecture de l'exportateur

Avant de plonger dans le code, il est crucial de comprendre le modèle architectural. Un exportateur personnalisé agit comme un adaptateur. Il remplit généralement trois fonctions principales : 1. **Découverte/Connexion :** Il se connecte au système hérité en utilisant un protocole disponible (par exemple, JDBC pour les bases de données, SSH pour les serveurs ou les API HTTP). 2. **Traduction :** Il extrait les données brutes et les mappe aux types de métriques Prometheus (Counter, Gauge, Histogramme ou Summary). 3. **Exposition :** Il expose un endpoint `/metrics` au format texte Prometheus, prêt à être interrogé. L'exportateur lui-même doit être sans état concernant les données qu'il collecte, mais avec état concernant sa connexion au système hérité. Il est recommandé de maintenir l'exportateur séparé de l'application héritée afin d'éviter une surcharge de performance sur le système de production critique.

Choisir le bon langage

Bien que vous puissiez créer des exportateurs dans n'importe quel langage, Go (Golang) est la norme industrielle pour les exportateurs Prometheus. La bibliothèque officielle `prometheus/client_golang` fournit des primitives robustes et de bas niveau qui garantissent des performances élevées et une consommation de ressources minimale. Python est également une option viable pour des scripts plus simples, mais Go offre une meilleure gestion de la concurrence et une compilation de binaires statiques, ce qui simplifie le déploiement.

Exemple pratique : Création d'un exportateur Go

Supposons que nous disposions d'un service interne hérité qui expose un nombre unique via une simple requête HTTP GET à l'adresse `http://legacy-system:8080/status`. Nous souhaitons exposer cette valeur en tant que métrique Gauge Prometheus. Tout d'abord, assurez-vous d'avoir les dépendances nécessaires : ```bash go get github.com/prometheus/client_golang/prometheus go get github.com/prometheus/client_golang/prometheus/promhttp ``` Voici une implémentation complète et minimale d'un exportateur personnalisé :
package main

import (
	"io/ioutil"
	"log"
	"net/http"
	"strings"

	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/client_golang/prometheus/promhttp"
)

// legacyStatusGauge est la métrique que nous souhaitons exposer
var legacyStatusGauge = prometheus.NewGauge(
	prometheus.GaugeOpts{
		Name: "legacy_system_status_code",
		Help: "Code de statut actuel renvoyé par le système hérité",
	},
)

// Collector implémente l'interface prometheus.Collector
type LegacyCollector struct{}

// Describe envoie l'ensemble de tous les descripteurs possibles des métriques
func (c *LegacyCollector) Describe(ch chan<- *prometheus.Desc) {
	ch <- legacyStatusGauge.Desc()
}

// Collect est appelée par le registre Prometheus lors de la collecte des métriques
func (c *LegacyCollector) Collect(ch chan<- prometheus.Metric) {
	// Récupérer les données du système hérité
	resp, err := http.Get("http://legacy-system:8080/status")
	if err != nil {
		log.Printf("Erreur lors de la récupération du statut : %v", err)
		// Ne pas retourner en cas d'erreur ; laisser Prometheus gérer les erreurs de connexion
		return
	}
	defer resp.Body.Close()

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		log.Printf("Erreur lors de la lecture du corps : %v", err)
		return
	}

	// Analyser la réponse du système hérité (en supposant qu'il s'agit d'une chaîne numérique)
	statusValue := 0.0
	if strings.TrimSpace(string(body)) != "" {
		// Dans un scénario réel, utilisez une logique d'analyse appropriée
		statusValue = 1.0 
	}

	// Définir la valeur de la jauge
	legacyStatusGauge.Set(statusValue)
	
	// Envoyer la métrique à Prometheus
	ch <- legacyStatusGauge
}

func main() {
	// Enregistrer le collecteur
	prometheus.MustRegister(&LegacyCollector{})

	// Exposer l'endpoint /metrics
	http.Handle("/metrics", promhttp.Handler())
	
	log.Println("Démarrage de l'exportateur sur :9100")
	log.Fatal(http.ListenAndServe(":9100", nil))
}

Meilleures pratiques pour la production

Lors du passage d'un prototype à la production, envisagez les optimisations suivantes : * **Gestion des erreurs :** Ne laissez jamais une erreur de collecte provoquer un panic dans l'exportateur. Si le système hérité est injoignable, l'exportateur doit continuer à fonctionner et exposer des valeurs nulles ou les dernières valeurs connues si cela est approprié, permettant ainsi à Prometheus de générer des alertes basées sur la péremption (staleness). * **Authentification :** Si votre système hérité nécessite une authentification, assurez-vous que les identifiants sont gérés de manière sécurisée via des variables d'environnement ou des outils de gestion des secrets, jamais codés en dur. * **Nomination des métriques :** Suivez les conventions de nommage de Prometheus. Utilisez des underscores, pas de camelCase, et incluez l'unité dans le texte d'aide. Évitez les étiquettes « informatives » ayant une cardinalité élevée (comme les identifiants utilisateur uniques), car cela peut submerger la base de données de séries temporelles de Prometheus. * **Contrôles de santé (Health Checks) :** Implémentez un endpoint `/health` à côté de `/metrics` pour permettre aux orchestrateurs de conteneurs (comme Kubernetes) de vérifier que l'exportateur est opérationnel et capable de se connecter au backend.

Conclusion

La création d'exportateurs Prometheus personnalisés est une compétence essentielle pour les ingénieurs DevOps gérant des environnements hybrides ou fortement dépendants de systèmes hérités. En découplant la logique de surveillance de l'application héritée, vous gagnez en observabilité sans compromettre la stabilité. Bien que la configuration initiale demande des efforts, les avantages à long terme d'une alerting unifiée, de la visualisation et de la corrélation des données sur l'ensemble de votre infrastructure sont inestimables. Commencez petit, itérez sur vos définitions de métriques et assurez-vous que vos exportateurs sont aussi légers et robustes que les systèmes qu'ils surveillent.
Share: