Utilizzo delle metriche OpenCensus

In questa pagina vengono spiegate le nozioni di base della creazione di metriche OpenCensus per gli SLI di disponibilità e latenza. Fornisce inoltre esempi di implementazione di come definire gli SLO utilizzando le metriche OpenCensus.

Nozioni di base su OpenCensus

OpenCensus è una singola distribuzione open source di librerie, disponibile nella pagina di GitCensus GitHub, che raccoglie automaticamente tracce e metriche e le invia a qualsiasi backend. OpenCensus può essere utilizzato per utilizzare i tuoi servizi per emettere metriche personalizzate che possono essere importate in Cloud Monitoring. Puoi utilizzare queste metriche come SLI.

Per un esempio che utilizza OpenCensus per creare metriche di Monitoring non destinate specificamente agli SLI, consulta Metriche personalizzate con OpenCensus.

Metriche

Per raccogliere dati delle metriche dal tuo servizio utilizzando OpenCensus, devi utilizzare i seguenti costrutti OpenCensus:

  • Measure, che rappresenta il tipo di metrica da registrare, specificato con un nome. Un Measure può registrare valori Int64 o Float64.
  • Measurement: registra un punto dati specifico raccolto e scritto da Measure per un particolare evento. Ad esempio, un elemento Measurement potrebbe registrare la latenza di una risposta specifica.
  • View, che specifica un'aggregazione applicata a un elemento Measure. OpenCensus supporta i seguenti tipi di aggregazione:
    • Conteggio: un conteggio del numero di punti di misurazione.
    • Distribuzione: distribuzione di un istogramma dei punti di misurazione.
    • Somma: una somma dei valori di misurazione.
    • LastValue: l'ultimo valore registrato dalla misurazione.

Per ulteriori informazioni, consulta la pagina Statistiche/metriche OpenCensus. Tieni presente che OpenCensus spesso fa riferimento alle metriche come stats.

Strumentazione

Le librerie OpenCensus sono disponibili per diverse lingue. Per informazioni specifiche sulla lingua per la strumentazione del servizio per l'emissione di metriche, consulta la pagina relativa al supporto per la lingua di OpenCensus. Inoltre, le metriche personalizzate con OpenCensus forniscono esempi di lingue comunemente utilizzate con Monitoring.

Nel caso di base, devi eseguire queste operazioni:

  • Instrumenta il tuo servizio per registrare ed esportare le metriche.
  • Definisci un esportatore per ricevere le metriche.

Per ogni metrica devi definire un Measure per specificare il tipo di valore: Int64 o Float64. Devi anche definire e registrare il View per specificare il tipo di aggregazione (conteggio, distribuzione, somma o ultimo valore). Per utilizzare il tipo di aggregazione della distribuzione, devi specificare anche i limiti del bucket dell'istogramma in modo esplicito. Puoi anche specificare un nome per la metrica in View.

Esportatore

Infine, devi utilizzare un esportatore per raccogliere le metriche e scriverle in Cloud Monitoring o un altro backend. Per informazioni sugli esportatori specifici per lingua disponibili per Monitoring, consulta la pagina relativa agli esportatori OpenCensus.

Puoi anche scrivere il tuo esportatore; per ulteriori informazioni, consulta la pagina Scrivere un esportatore personalizzato.

Creazione di metriche per SLI

La tua applicazione deve creare metriche OpenCensus che possono essere utilizzate come SLI in Cloud Monitoring:

  • Per gli SLI di disponibilità su numero di richieste ed errori, utilizza un elemento Measure con aggregazione dei conteggi.
  • Per gli SLI di latenza, utilizza un Measure con aggregazione di distribuzione.

Metriche per SLI di disponibilità

Invii uno SLI di disponibilità basata su richiesta nell'API Cloud Monitoring utilizzando la struttura di TimeSeriesRatio per impostare un rapporto di richieste "buone" o "brutte"; rispetto alle richieste totali. Questo rapporto viene utilizzato nel campo goodTotalRatio di una struttura RequestBasedSli.

La tua applicazione deve creare metriche OpenCensus che possono essere utilizzate per creare questo rapporto. Nella tua applicazione, devi creare almeno due dei seguenti elementi:

  1. Una metrica che conteggia il totale degli eventi; utilizzala nel rapporto totalServiceFilter.

    Puoi creare una metrica OpenCensus di tipo Int64 con aggregazione del conteggio, in cui registri un valore 1 per ogni richiesta ricevuta.

  2. Una metrica che conteggia gli eventi"cattivi", usala nel rapporto badServiceFilter.

    Puoi creare una metrica OpenCensus di tipo Int64 con aggregazione dei conteggi, in cui registri un valore 1 per ogni richiesta non riuscita o errata.

  3. Una metrica che conta gli eventi validi, utilizzala nel rapporto goodServiceFilter.

    Puoi creare una metrica OpenCensus di tipo Int64 con aggregazione dei conteggi, in cui registri un valore 1 per ogni risposta riuscita.

Metriche per SLI di latenza

Puoi esprimere uno SLI di latenza basato su richiesta nell'API Cloud Monitoring utilizzando una struttura DistributionCut. Questa struttura è utilizzata nel campo distributionCut di una struttura RequestBasedSli.

Puoi creare un Measure Int64 o Float64 con un View utilizzando il tipo di aggregazione della distribuzione. Devi inoltre definire esplicitamente i limiti del bucket. Tieni presente che è fondamentale definire i bucket in modo da poter misurare con precisione la percentuale di richieste che rientrano nella soglia desiderata. Per una discussione su questo argomento, consulta la pagina relativa all'implementazione degli SLO nella Site Reliability Engineering Workbook.

Esempio di implementazione

Questa sezione presenta un esempio che implementa le metriche per gli SLI di base relativi a disponibilità e latenza utilizzando OpenCensus in Node.js.

Strumentazione

Per utilizzare il tuo servizio per l'emissione di metriche tramite OpenCensus, procedi nel seguente modo:

  1. Includi le librerie necessarie:

    Go

    import (
    	"flag"
    	"fmt"
    	"log"
    	"math/rand"
    	"net/http"
    	"time"
    
    	"contrib.go.opencensus.io/exporter/stackdriver"
    	"go.opencensus.io/stats"
    	"go.opencensus.io/stats/view"
    	"go.opencensus.io/tag"
    )
    

    Node.js

    // opencensus setup
    const {globalStats, MeasureUnit, AggregationType} = require('@opencensus/core');
    const {StackdriverStatsExporter} = require('@opencensus/exporter-stackdriver');

    Python

    from flask import Flask
    from opencensus.ext.prometheus import stats_exporter as prometheus
    from opencensus.stats import aggregation as aggregation_module
    from opencensus.stats import measure as measure_module
    from opencensus.stats import stats as stats_module
    from opencensus.stats import view as view_module
    from opencensus.tags import tag_map as tag_map_module
    
    from prometheus_flask_exporter import PrometheusMetrics
    
  2. Definisci e registra l'esportatore:

    Go

    // Sets up Cloud Monitoring exporter.
    sd, err := stackdriver.NewExporter(stackdriver.Options{
    	ProjectID:         *projectID,
    	MetricPrefix:      "opencensus-demo",
    	ReportingInterval: 60 * time.Second,
    })
    if err != nil {
    	log.Fatalf("Failed to create the Cloud Monitoring exporter: %v", err)
    }
    defer sd.Flush()
    
    sd.StartMetricsExporter()
    defer sd.StopMetricsExporter()

    Node.js

    // Stackdriver export interval is 60 seconds
    const EXPORT_INTERVAL = 60;
    const exporter = new StackdriverStatsExporter({
      projectId: projectId,
      period: EXPORT_INTERVAL * 1000,
    });
    globalStats.registerExporter(exporter);

    Python

    def setup_openCensus_and_prometheus_exporter() -> None:
        stats = stats_module.stats
        view_manager = stats.view_manager
        exporter = prometheus.new_stats_exporter(prometheus.Options(namespace="oc_python"))
        view_manager.register_exporter(exporter)
        register_all_views(view_manager)
  3. Definisci un Measure per ogni metrica:

    Go

    // Sets up metrics.
    var (
    	requestCount       = stats.Int64("oc_request_count", "total request count", "requests")
    	failedRequestCount = stats.Int64("oc_failed_request_count", "count of failed requests", "requests")
    	responseLatency    = stats.Float64("oc_latency_distribution", "distribution of response latencies", "s")
    )
    

    Node.js

    const REQUEST_COUNT = globalStats.createMeasureInt64(
      'request_count',
      MeasureUnit.UNIT,
      'Number of requests to the server'
    );
    const ERROR_COUNT = globalStats.createMeasureInt64(
      'error_count',
      MeasureUnit.UNIT,
      'Number of failed requests to the server'
    );
    const RESPONSE_LATENCY = globalStats.createMeasureInt64(
      'response_latency',
      MeasureUnit.MS,
      'The server response latency in milliseconds'
    );

    Python

    m_request_count = measure_module.MeasureInt(
        "python_request_count", "total requests", "requests"
    )
    m_failed_request_count = measure_module.MeasureInt(
        "python_failed_request_count", "failed requests", "requests"
    )
    m_response_latency = measure_module.MeasureFloat(
        "python_response_latency", "response latency", "s"
    )
  4. Definisci e registra l'elemento View per ogni Measure con il tipo di aggregazione appropriato e, per la latenza di risposta, i limiti del bucket:

    Go

    // Sets up views.
    var (
    	requestCountView = &view.View{
    		Name:        "oc_request_count",
    		Measure:     requestCount,
    		Description: "total request count",
    		Aggregation: view.Count(),
    	}
    	failedRequestCountView = &view.View{
    		Name:        "oc_failed_request_count",
    		Measure:     failedRequestCount,
    		Description: "count of failed requests",
    		Aggregation: view.Count(),
    	}
    	responseLatencyView = &view.View{
    		Name:        "oc_response_latency",
    		Measure:     responseLatency,
    		Description: "The distribution of the latencies",
    		// Bucket definitions must be explicitly specified.
    		Aggregation: view.Distribution(0, 1000, 2000, 3000, 4000, 5000, 6000, 7000, 8000, 9000, 10000),
    	}
    )
    
    	// Register the views.
    	if err := view.Register(requestCountView, failedRequestCountView, responseLatencyView); err != nil {
    		log.Fatalf("Failed to register the views: %v", err)
    	}

    Node.js

    const request_count_metric = globalStats.createView(
      'request_count_metric',
      REQUEST_COUNT,
      AggregationType.COUNT
    );
    globalStats.registerView(request_count_metric);
    const error_count_metric = globalStats.createView(
      'error_count_metric',
      ERROR_COUNT,
      AggregationType.COUNT
    );
    globalStats.registerView(error_count_metric);
    const latency_metric = globalStats.createView(
      'response_latency_metric',
      RESPONSE_LATENCY,
      AggregationType.DISTRIBUTION,
      [],
      'Server response latency distribution',
      // Latency in buckets:
      [0, 1000, 2000, 3000, 4000, 5000, 10000]
    );
    globalStats.registerView(latency_metric);

    Python

    # set up views
    latency_view = view_module.View(
        "python_response_latency",
        "The distribution of the latencies",
        [],
        m_response_latency,
        aggregation_module.DistributionAggregation(
            [0, 1000, 2000, 3000, 4000, 5000, 6000, 7000, 8000, 9000, 10000]
        ),
    )
    
    request_count_view = view_module.View(
        "python_request_count",
        "total requests",
        [],
        m_request_count,
        aggregation_module.CountAggregation(),
    )
    
    failed_request_count_view = view_module.View(
        "python_failed_request_count",
        "failed requests",
        [],
        m_failed_request_count,
        aggregation_module.CountAggregation(),
    )
    
    # register views
    def register_all_views(view_manager: stats_module.stats.view_manager) -> None:
        view_manager.register_view(latency_view)
        view_manager.register_view(request_count_view)
        view_manager.register_view(failed_request_count_view)
  5. Registra i valori per le metriche relative al numero di richieste e agli errori:

    Go

    // Counts the request.
    stats.Record(ctx, requestCount.M(1))
    
    // Randomly fails 10% of the time.
    if rand.Intn(100) >= 90 {
    	// Counts the error.
    	stats.Record(ctx, failedRequestCount.M(1))

    Node.js

    // record a request count for every request
    globalStats.record([
      {
        measure: REQUEST_COUNT,
        value: 1,
      },
    ]);
    
    // randomly throw an error 10% of the time
    const randomValue = Math.floor(Math.random() * 9 + 1);
    if (randomValue === 1) {
      // Record a failed request.
      globalStats.record([
        {
          measure: ERROR_COUNT,
          value: 1,
        },
      ]);

    Python

    mmap = stats_recorder.new_measurement_map()
    # count request
    mmap.measure_int_put(m_request_count, 1)
    # fail 10% of the time
    if random.randint(0, 100) > 90:
        mmap.measure_int_put(m_failed_request_count, 1)
        tmap = tag_map_module.TagMap()
        mmap.record(tmap)
        return ("error!", 500)
  6. Registra valori di latenza:

    Go

    requestReceived := time.Now()
    // Records latency for failure OR success.
    defer func() {
    	stats.Record(ctx, responseLatency.M(time.Since(requestReceived).Seconds()))
    }()

    Node.js

    globalStats.record([
      {
        measure: RESPONSE_LATENCY,
        value: stopwatch.elapsedMilliseconds,
      },
    ]);

    Python

    start_time = time.perf_counter()
    mmap = stats_recorder.new_measurement_map()
    if random.randint(0, 100) > 90:
        response_latency = time.perf_counter() - start_time
        mmap.measure_float_put(m_response_latency, response_latency)
        tmap = tag_map_module.TagMap()
        mmap.record(tmap)

Metriche importate

Quando le metriche vengono esportate in Cloud Monitoring, vengono visualizzate come tipi di metriche con un prefisso che indica che hanno avuto origine da OpenCensus. Ad esempio, il nome di ogni View OpenCensus nell'implementazione Node.js è mappato come segue:

  • request_count_sli diventa custom.googleapis.com/opencensus/request_count_sli.
  • error_count_sli diventa custom.googleapis.com/opencensus/error_count_sli.
  • response_latency_sli diventa custom.googleapis.com/opencensus/response_latency_sli.

Dopo l'esecuzione del tuo servizio, puoi confermare che le metriche vengano importate in Monitoring cercandole in Metrics Explorer.

SLI di disponibilità

In Cloud Monitoring, esprimi uno SLI di disponibilità basato su richiesta utilizzando una struttura TimeSeriesRatio. L'esempio seguente mostra uno SLO che utilizza le metriche OpenCensus importate e prevede che il servizio abbia una disponibilità del 98%, calcolato da un rapporto di error_count_sli a request_count_sli, in una finestra continuativa di 28 giorni:

{
  "serviceLevelIndicator": {
    "requestBased": {
      "goodTotalRatio": {
        "totalServiceFilter":
          "metric.type=\"custom.googleapis.com/opencensus/request_count_sli\",
       "badServiceFilter":
          "metric.type=\"custom.googleapis.com/opencensus/error_count_sli\"
      }
    }
  },
  "goal": 0.98,
  "rollingPeriod": "2419200s",
  "displayName": "98% Availability, rolling 28 days"
}

SLI di latenza

In Cloud Monitoring, esprimi uno SLI di latenza basato su richiesta utilizzando una struttura DistributionCut. L'esempio seguente mostra uno SLO che utilizza la metrica di latenza OpenCensus importati e prevede che il 98% delle richieste venga completato in meno di 1000 ms in una finestra continuativa di un giorno:

{
  "serviceLevelIndicator": {
    "requestBased": {
      "distributionCut": {
        "distributionFilter":
          "metric.type=\"custom.googleapis.com/opencensus/response_latency_sli\",
        "range": {
          "min": 0,
          "max": 1000
        }
      }
    }
  },
  "goal": 0.98,
  "rollingPeriod": "86400s",
  "displayName": "98% requests under 1000 ms"
}