Utiliser les métriques Prometheus

Cette page présente les principes de base de la création de métriques Prometheus pour les SLI de disponibilité et de latence. Elle fournit également des exemples de mise en œuvre pour définir des SLO à l'aide de métriques Prometheus.

Les bases de Prometheus

Prometheus est une solution de surveillance Open Source de pointe pour les métriques et les alertes.

Prometheus accepte les données de dimension avec des identifiants de clé-valeur pour les métriques, fournit le langage de requête PromQL et accepte de nombreuses intégrations en fournissant des exportateurs pour d'autres produits.

Prometheus s'intègre à Cloud Monitoring à l'aide du collecteur Stackdriver.

Métriques

Prometheus est compatible avec les types de métriques suivants:

  • Compteur: valeur unique qui ne peut être réinitialisée sur 0 au redémarrage ou augmentée de façon monotone.
  • Jauge: valeur numérique unique pouvant être définie de manière arbitraire.
  • Histogramme: groupe de buckets configurables permettant d'effectuer un échantillonnage et d'enregistrer des valeurs dans des plages, et fournit également la somme de toutes les valeurs observées
  • Résumé: semblable à un histogramme, il calcule également des quantiles configurables sur une fenêtre à durée flexible.

Pour plus d'informations, consultez la page Types de métriques.

Instrumentation

Pour que Prometheus reçoive des métriques de votre application, celle-ci doit exposer un point de terminaison dédié (souvent /metrics) dans lequel les valeurs de métriques sont disponibles. Pour exposer un tel point de terminaison, utilisez les bibliothèques clientes Prometheus.

Si votre application écrit déjà des métriques vers une autre destination, telle qu'une base de données ou un fichier, vous pouvez créer un exportateur Prometheus pour lire les données et les exposer. Pour en savoir plus, consultez la page sur les exportateurs.

Enfin, vous devez disposer à la fois d'un serveur Prometheus dans lequel les métriques sont stockées et d'un moyen pour que les métriques puissent être ingérées dans Cloud Monitoring à partir du serveur. Le collecteur Stackdriver répond à cet effet.

Créer des métriques pour les SLI

Votre application doit créer des métriques Prometheus pouvant être utilisées comme SLI dans Cloud Monitoring:

  • Pour les SLI de disponibilité sur le nombre de requêtes et d'erreurs, vous pouvez commencer par les métriques de compteur de Prometheus.
  • Pour les SLI de latence, vous pouvez utiliser les métriques d'histogramme ou de résumé Promethus.

Métriques pour les SLI de disponibilité

Vous pouvez exprimer un SLI de disponibilité basé sur les requêtes dans l'API Cloud Monitoring à l'aide de la structure TimeSeriesRatio pour définir un ratio de "bonnes" ou "mauvaises" requêtes par rapport au total de requêtes. Ce ratio est utilisé dans le champ goodTotalRatio d'une structure RequestBasedSli.

Votre application doit créer des métriques Prometheus pouvant être utilisées pour créer ce ratio. Dans votre application, vous devez créer au moins deux des éléments suivants:

  1. une métrique qui comptabilise le nombre total d'événements ; Utilisez cette métrique dans le totalServiceFilter du ratio.

    Vous pouvez créer un compteur Prometheus qui est incrémenté pour chaque événement.

  2. Métrique qui compte les événements "bad", utilisez cette métrique dans le badServiceFilter du ratio.

    Vous pouvez créer un compteur Prometheus qui est incrémenté pour chaque erreur ou tout autre événement "bad".

  3. Métrique qui comptabilise les événements "satisfaisants", utilisez cette métrique dans le goodServiceFilter du ratio.

    Vous pouvez créer un compteur Prometheus qui est incrémenté pour chaque événement réussi ou "good".

Le cas de l'exemple de mise en œuvre crée un compteur pour le nombre total de requêtes, nodeRequestsCounter, et un compteur pour le nombre de requêtes ayant échoué, nodeFailedRequestsCounter.

Métriques pour les SLI de latence

Vous pouvez exprimer un SLI de latence basé sur les requêtes dans l'API Cloud Monitoring en créant une structure DistributionCut. Cette structure est utilisée dans le champ distributionCut d'une structure RequestBasedSli.

Votre application doit créer une métrique Prometheus pouvant être utilisée pour construire la valeur de répartition de distribution. Vous pouvez utiliser un histogramme ou un résumé de Promethus à cette fin. Pour déterminer comment définir vos buckets de manière à mesurer précisément si vos réponses sont comprises dans votre SLO, consultez la section Types de métriques dans la documentation de Prometheus.

Le cas de l'exemple de mise en œuvre crée un histogramme pour les latences de réponse par chemin, nodeLatenciesHistogram.

Exemple de mise en œuvre

Cette section présente un exemple qui met en œuvre des métriques pour des SLI de disponibilité et de latence de base à l'aide de Prometheus dans Node.js.

Instrumentation

Pour instrumenter votre service afin d'exposer des métriques Prometheus, procédez comme suit:

  1. Incluez ou importez le client Prometheus:

    Go

    import (
    	"fmt"
    	"log"
    	"math/rand"
    	"net/http"
    	"time"
    
    	"github.com/prometheus/client_golang/prometheus"
    	"github.com/prometheus/client_golang/prometheus/promauto"
    	"github.com/prometheus/client_golang/prometheus/promhttp"
    )
    

    Node.js

    const prometheus = require('prom-client');
    const collectDefaultMetrics = prometheus.collectDefaultMetrics;
    const Registry = prometheus.Registry;
    const register = new Registry();
    collectDefaultMetrics({register});

    Python

    import random
    import time
    
    from flask import Flask
    
    from prometheus_client import (
        Counter,
        generate_latest,
        Histogram,
        REGISTRY,
    )
    
  2. Utilisez le client pour définir les métriques:

    Go

    // Sets up metrics.
    var (
    	requestCount = promauto.NewCounter(prometheus.CounterOpts{
    		Name: "go_request_count",
    		Help: "total request count",
    	})
    	failedRequestCount = promauto.NewCounter(prometheus.CounterOpts{
    		Name: "go_failed_request_count",
    		Help: "failed request count",
    	})
    	responseLatency = promauto.NewHistogram(prometheus.HistogramOpts{
    		Name: "go_response_latency",
    		Help: "response latencies",
    	})
    )
    

    Node.js

    // total requests - counter
    const nodeRequestsCounter = new prometheus.Counter({
      name: 'node_requests',
      help: 'total requests',
    });
    
    // failed requests - counter
    const nodeFailedRequestsCounter = new prometheus.Counter({
      name: 'node_failed_requests',
      help: 'failed requests',
    });
    
    // latency - histogram
    const nodeLatenciesHistogram = new prometheus.Histogram({
      name: 'node_request_latency',
      help: 'request latency by path',
      labelNames: ['route'],
      buckets: [100, 400],
    });

    Python

    PYTHON_REQUESTS_COUNTER = Counter("python_requests", "total requests")
    PYTHON_FAILED_REQUESTS_COUNTER = Counter("python_failed_requests", "failed requests")
    PYTHON_LATENCIES_HISTOGRAM = Histogram(
        "python_request_latency", "request latency by path"
    )
  3. Définissez le point de terminaison sur lequel exposer vos métriques Prometheus (à l'aide d'Express):

    Go

    http.Handle("/metrics", promhttp.Handler())

    Node.js

    app.get('/metrics', async (req, res) => {
      try {
        res.set('Content-Type', register.contentType);
        res.end(await register.metrics());
      } catch (ex) {
        res.status(500).end(ex);
      }
    });

    Python

    @app.route("/metrics", methods=["GET"])
    def stats():
        return generate_latest(REGISTRY), 200
    
    
  4. Incrémentez les métriques de compteur de façon appropriée:

    Go

    requestCount.Inc()
    
    // Fails 10% of the time.
    if rand.Intn(100) >= 90 {
    	log.Printf("intentional failure encountered")
    	failedRequestCount.Inc()
    	http.Error(w, "intentional error!", http.StatusInternalServerError)
    	return
    }

    Node.js

    // increment total requests counter
    nodeRequestsCounter.inc();
    // return an error 10% of the time
    if (Math.floor(Math.random() * 100) > 90) {
      // increment error counter
      nodeFailedRequestsCounter.inc();

    Python

    PYTHON_REQUESTS_COUNTER.inc()
    # fail 10% of the time
    if random.randint(0, 100) > 90:
        PYTHON_FAILED_REQUESTS_COUNTER.inc()
  5. Effectuez un suivi approprié de la métrique de latence:

    Go

    requestReceived := time.Now()
    defer func() {
    	responseLatency.Observe(time.Since(requestReceived).Seconds())
    }()

    Node.js

    // start latency timer
    const requestReceived = new Date().getTime();
    console.log('request made');
    // increment total requests counter
    nodeRequestsCounter.inc();
    // return an error 10% of the time
    if (Math.floor(Math.random() * 100) > 90) {
      // increment error counter
      nodeFailedRequestsCounter.inc();
      // return error code
      res.send('error!', 500);
    } else {
      // delay for a bit
      sleep.msleep(Math.floor(Math.random() * 1000));
      // record response latency
      const responseLatency = new Date().getTime() - requestReceived;
      nodeLatenciesHistogram.labels(req.route.path).observe(responseLatency);

    Python

    @PYTHON_LATENCIES_HISTOGRAM.time()

Configurer l'ingestion

Une fois que votre service est en cours d'exécution et émet des métriques sur un point de terminaison, configurez les paramètres appropriés pour la récupération Prometheus et le collecteur Stackdriver pour l'ingestion des métriques dans Cloud Monitoring

Cette configuration détermine la manière dont vos métriques Prometheus apparaissent dans Monitoring. Dans cet exemple, les métriques Prometheus sont mappées comme suit:

  • nodeRequestCounter devient external.googleapis.com/prometheus/total_request_count.
  • nodeFailedRequestCounter devient external.googleapis.com/prometheus/error_count.
  • nodeLatenciesHistogram devient external.googleapis.com/prometheus/reponse_latency.

Le type de ressource surveillée associée est k8s_container.

Utilisez ces métriques ingérées pour définir vos SLI.

SLI de disponibilité

Dans Cloud Monitoring, vous exprimez un SLI de disponibilité basé sur les requêtes à l'aide d'une structure TimeSeriesRatio. L'exemple suivant montre un SLO qui utilise les métriques Prometheus ingérées et s'attend à ce que le service dispose d'une disponibilité de 98 %, d'après le ratio de requêtes "bad" par rapport au nombre total de requêtes, sur une période glissante de 28 jours:

{
 "serviceLevelIndicator": {
   "requestBased": {
     "goodTotalRatio": {
       "totalServiceFilter":
         "metric.type=\"external.googleapis.com/prometheus/total_request_count\"
          resource.type=\"k8s_container\"",
       "badServiceFilter":
         "metric.type=\"external.googleapis.com/prometheus/error_count\"
          resource.type=\"k8s_container\""
     }
   }
 },
 "goal": 0.98,
 "rollingPeriod": "2419200s",
 "displayName": "98% Availability, rolling 28 days"
}

SLI de latence

Dans Cloud Monitoring, vous exprimez un SLI de latence basé sur les requêtes à l'aide d'une structure DistributionCut. L'exemple suivant montre un SLO qui utilise la métrique de latence ingéré Prometheus et s'attend à ce que 98% des requêtes se terminent en moins de 500 ms sur une fenêtre glissante d'une journée:

{
  "serviceLevelIndicator": {
    "requestBased": {
      "distributionCut": {
        "distributionFilter":
          "metric.type=\"external.googleapis.com/prometheus/response_latency\"
           resource.type=\"k8s_container\"",
        "range": {
          "min": 0,
          "max": 500
        }
      }
    }
  },
  "goal": 0.98,
  "rollingPeriod": "86400s",
  "displayName": "98% requests under 500 ms"
}