⚠️ Traduction non officielle - Cette documentation est une traduction communautaire non officielle de Docker.

Construire l'application

Prérequis

  • Vous disposez d'un client Git. Les exemples de cette section utilisent un client Git en ligne de commande, mais vous pouvez utiliser n'importe quel client.

Vous allez créer un serveur Golang avec quelques points de terminaison pour simuler une application du monde réel. Ensuite, vous exposerez les métriques du serveur à l'aide de Prometheus.

Obtenir l'application d'exemple

Clonez l'application d'exemple à utiliser avec ce guide. Ouvrez un terminal, changez de répertoire pour un répertoire dans lequel vous souhaitez travailler, et exécutez la commande suivante pour cloner le dépôt :

$ git clone https://github.com/dockersamples/go-prometheus-monitoring.git 

Une fois que vous avez cloné, vous verrez la structure de contenu suivante à l'intérieur du répertoire go-prometheus-monitoring,

go-prometheus-monitoring
├── CONTRIBUTING.md
├── Docker
│   ├── grafana.yml
│   └── prometheus.yml
├── dashboard.json
├── Dockerfile
├── LICENSE
├── README.md
├── compose.yaml
├── go.mod
├── go.sum
└── main.go
  • main.go - Le point d'entrée de l'application.
  • go.mod et go.sum - Fichiers de module Go.
  • Dockerfile - Dockerfile utilisé pour construire l'application.
  • Docker/ - Contient les fichiers de configuration Docker Compose pour Grafana et Prometheus.
  • compose.yaml - Fichier Compose pour tout lancer (application Golang, Prometheus et Grafana).
  • dashboard.json - Fichier de configuration du tableau de bord Grafana.
  • Dockerfile - Dockerfile utilisé pour construire l'application Golang.
  • compose.yaml - Fichier Docker Compose pour tout lancer (application Golang, Prometheus et Grafana).
  • Les autres fichiers sont à des fins de licence et de documentation.

Comprendre l'application

Voici la logique complète de l'application que vous trouverez dans main.go.

package main

import (
	"strconv"

	"github.com/gin-gonic/gin"
	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/client_golang/prometheus/promhttp"
)

// Définir les métriques
var (
	HttpRequestTotal = prometheus.NewCounterVec(prometheus.CounterOpts{
		Name: "api_http_request_total",
		Help: "Nombre total de requêtes traitées par l'API",
	}, []string{"path", "status"})

	HttpRequestErrorTotal = prometheus.NewCounterVec(prometheus.CounterOpts{
		Name: "api_http_request_error_total",
		Help: "Nombre total d'erreurs retournées par l'API",
	}, []string{"path", "status"})
)

// Registre personnalisé (sans les métriques Go par défaut)
var customRegistry = prometheus.NewRegistry()

// Enregistrer les métriques avec le registre personnalisé
func init() {
	customRegistry.MustRegister(HttpRequestTotal, HttpRequestErrorTotal)
}

func main() {
	router := gin.Default()

	// Enregistrer /metrics avant le middleware
	router.GET("/metrics", PrometheusHandler())
	
	router.Use(RequestMetricsMiddleware())
	router.GET("/health", func(c *gin.Context) {
		c.JSON(200, gin.H{
			"message": "Up and running!",
		})
	})
	router.GET("/v1/users", func(c *gin.Context) {
		c.JSON(200, gin.H{
			"message": "Hello from /v1/users",
		})
	})

	router.Run(":8000")
}

// Gestionnaire de métriques personnalisé avec registre personnalisé
func PrometheusHandler() gin.HandlerFunc {
	h := promhttp.HandlerFor(customRegistry, promhttp.HandlerOpts{})
	return func(c *gin.Context) {
		h.ServeHTTP(c.Writer, c.Request)
	}
}

// Middleware pour enregistrer les métriques des requêtes entrantes
func RequestMetricsMiddleware() gin.HandlerFunc {
	return func(c *gin.Context) {
		path := c.Request.URL.Path
		c.Next()
		status := c.Writer.Status()
		if status < 400 {
			HttpRequestTotal.WithLabelValues(path, strconv.Itoa(status)).Inc()
		} else {
			HttpRequestErrorTotal.WithLabelValues(path, strconv.Itoa(status)).Inc()
		}
	}
}

Dans cette partie du code, vous avez importé les paquets requis gin, prometheus et promhttp. Ensuite, vous avez défini quelques variables, HttpRequestTotal et HttpRequestErrorTotal sont des métriques de compteur Prometheus, et customRegistry est un registre personnalisé qui sera utilisé pour enregistrer ces métriques. Le nom de la métrique est une chaîne que vous pouvez utiliser pour identifier la métrique. La chaîne d'aide est une chaîne qui sera affichée lorsque vous interrogerez le point de terminaison /metrics pour comprendre la métrique. La raison pour laquelle vous utilisez le registre personnalisé est d'éviter les métriques Go par défaut qui sont enregistrées par défaut par le client Prometheus. Ensuite, à l'aide de la fonction init, vous enregistrez les métriques avec le registre personnalisé.

import (
	"strconv"

	"github.com/gin-gonic/gin"
	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/client_golang/prometheus/promhttp"
)

// Définir les métriques
var (
	HttpRequestTotal = prometheus.NewCounterVec(prometheus.CounterOpts{
		Name: "api_http_request_total",
		Help: "Nombre total de requêtes traitées par l'API",
	}, []string{"path", "status"})

	HttpRequestErrorTotal = prometheus.NewCounterVec(prometheus.CounterOpts{
		Name: "api_http_request_error_total",
		Help: "Nombre total d'erreurs retournées par l'API",
	}, []string{"path", "status"})
)

// Registre personnalisé (sans les métriques Go par défaut)
var customRegistry = prometheus.NewRegistry()

// Enregistrer les métriques avec le registre personnalisé
func init() {
	customRegistry.MustRegister(HttpRequestTotal, HttpRequestErrorTotal)
}

Dans la fonction main, vous avez créé une nouvelle instance du framework gin et créé trois routes. Vous pouvez voir le point de terminaison de santé qui se trouve sur le chemin /health qui retournera un JSON avec {"message": "Up and running!"} et le point de terminaison /v1/users qui retournera un JSON avec {"message": "Hello from /v1/users"}. La troisième route est pour le point de terminaison /metrics qui retournera les métriques au format Prometheus. Ensuite, vous avez le middleware RequestMetricsMiddleware, il sera appelé pour chaque requête faite à l'API. Il enregistrera les métriques des requêtes entrantes comme les codes d'état et les chemins. Enfin, vous exécutez l'application gin sur le port 8000.

func main() {
	router := gin.Default()

	// Enregistrer /metrics avant le middleware
	router.GET("/metrics", PrometheusHandler())
	
	router.Use(RequestMetricsMiddleware())
	router.GET("/health", func(c *gin.Context) {
		c.JSON(200, gin.H{
			"message": "Up and running!",
		})
	})
	router.GET("/v1/users", func(c *gin.Context) {
		c.JSON(200, gin.H{
			"message": "Hello from /v1/users",
		})
	})

	router.Run(":8000")
}

Vient maintenant la fonction middleware RequestMetricsMiddleware. Cette fonction est appelée pour chaque requête faite à l'API. Elle incrémente le compteur HttpRequestTotal (compteur différent pour différents chemins et codes d'état) si le code d'état est inférieur ou égal à 400. Si le code d'état est supérieur à 400, elle incrémente le compteur HttpRequestErrorTotal (compteur différent pour différents chemins et codes d'état). La fonction PrometheusHandler est le gestionnaire personnalisé qui sera appelé pour le point de terminaison /metrics. Il retournera les métriques au format Prometheus.

// Gestionnaire de métriques personnalisé avec registre personnalisé
func PrometheusHandler() gin.HandlerFunc {
	h := promhttp.HandlerFor(customRegistry, promhttp.HandlerOpts{})
	return func(c *gin.Context) {
		h.ServeHTTP(c.Writer, c.Request)
	}
}

// Middleware pour enregistrer les métriques des requêtes entrantes
func RequestMetricsMiddleware() gin.HandlerFunc {
	return func(c *gin.Context) {
		path := c.Request.URL.Path
		c.Next()
		status := c.Writer.Status()
		if status < 400 {
			HttpRequestTotal.WithLabelValues(path, strconv.Itoa(status)).Inc()
		} else {
			HttpRequestErrorTotal.WithLabelValues(path, strconv.Itoa(status)).Inc()
		}
	}
}

C'est tout, c'était l'essentiel de l'application. Il est maintenant temps d'exécuter et de tester si l'application enregistre correctement les métriques.

Exécuter l'application

Assurez-vous que vous êtes toujours dans le répertoire go-prometheus-monitoring dans le terminal, et exécutez la commande suivante. Installez les dépendances en exécutant go mod tidy, puis construisez et exécutez l'application en exécutant go run main.go. Ensuite, visitez http://localhost:8000/health ou http://localhost:8000/v1/users. Vous devriez voir la sortie {"message": "Up and running!"} ou {"message": "Hello from /v1/users"}. Si vous pouvez voir cela, votre application est démarrée et fonctionne avec succès.

Maintenant, vérifiez les métriques de votre application en accédant au point de terminaison /metrics. Ouvrez http://localhost:8000/metrics dans votre navigateur. Vous devriez voir une sortie similaire à la suivante.

# HELP api_http_request_error_total Nombre total d'erreurs retournées par l'API
# TYPE api_http_request_error_total counter
api_http_request_error_total{path="/",status="404"} 1
api_http_request_error_total{path="//v1/users",status="404"} 1
api_http_request_error_total{path="/favicon.ico",status="404"} 1
# HELP api_http_request_total Nombre total de requêtes traitées par l'API
# TYPE api_http_request_total counter
api_http_request_total{path="/health",status="200"} 2
api_http_request_total{path="/v1/users",status="200"} 1

Dans le terminal, appuyez sur ctrl + c pour arrêter l'application.

Note

Si vous ne voulez pas exécuter l'application localement, et que vous voulez l'exécuter dans un conteneur Docker, passez à la page suivante où vous créerez un Dockerfile et conteneuriserez l'application.

Résumé

Dans cette section, vous avez appris à créer une application Golang pour enregistrer des métriques avec Prometheus. En implémentant des fonctions middleware, vous avez pu incrémenter les compteurs en fonction du chemin de la requête et des codes d'état.

Prochaines étapes

Dans la section suivante, vous apprendrez à conteneuriser votre application.