Prometheus 측정항목 사용

이 페이지에서는 가용성 및 지연 시간 SLI의 Prometheus 측정항목을 만드는 기본 사항을 설명합니다. 또한 Prometheus 측정항목을 사용하여 SLO를 정의하는 방법의 구현 예시를 제공합니다.

Prometheus 기본 사항

Prometheus는 측정항목 및 알림에 사용되는 최고의 오픈소스 모니터링 솔루션입니다.

Prometheus는 측정항목의 키-값 식별자가 포함된 측정기준 데이터를 지원하고 PromQL 쿼리 언어를 제공하며 다른 제품에 내보내기를 제공하여 여러 통합을 지원합니다.

Prometheus는 Stackdriver 수집기를 통해 Cloud Monitoring과 통합됩니다.

측정항목

Prometheus에서는 다음 유형의 측정항목을 지원합니다.

  • 카운터: 단조 증가 또는 재시작 시에만 0으로 재설정될 수 있는 단일 값입니다.
  • 게이지: 임의로 설정할 수 있는 단일 숫자 값입니다.
  • 히스토그램: 범위 변동 및 범위 값을 샘플링하기 위한 구성 가능한 버킷 그룹이며 관찰된 모든 값의 합계도 제공합니다.
  • 요약: 히스토그램과 비슷하지만 슬라이딩 기간 동안의 구성 가능한 분위수를 계산합니다.

자세한 내용은 측정항목 유형을 참조하세요.

도구 작동

Prometheus가 애플리케이션에서 측정항목을 수신하려면 애플리케이션은 여기에서 사용 가능한 측정항목 값으로 전용 엔드포인트(종종 /metrics)를 노출해야 합니다. 이러한 엔드포인트를 노출하려면 Prometheus 클라이언트 라이브러리를 사용합니다.

애플리케이션에서 이미 데이터베이스나 파일과 같은 다른 대상에 측정항목을 작성한 경우 Prometheus 내보내기를 만들어 데이터를 읽고 노출할 수 있습니다. 자세한 내용은 내보내기를 참조하세요.

마지막으로 측정항목이 저장되는 Prometheus 서버와 서버에서 Cloud Monitoring을 수집하는 측정항목 방법이 모두 있어야 합니다. Stackdriver 수집기는 이러한 용도로 제공됩니다.

SLI 측정항목 만들기

애플리케이션은 Cloud Monitoring에서 SLI로 사용할 수 있는 Prometheus 측정항목을 만들어야 합니다.

  • 요청 및 오류 수 계산에 대한 가용성 SLI의 경우 Promethus 카운터 측정항목으로 시작할 수 있습니다.
  • 지연 시간 SLI의 경우 Promethus 히스토그램이나 요약 측정항목을 사용할 수 있습니다.

가용성 SLI 측정항목

Cloud Monitoring API에서 TimeSeriesRatio 구조를 사용하여 총 요청에 대한 '양호' 또는 '불량' 요청의 비율을 설정해 요청 기반 가용성 SLI를 표현합니다. 이 비율은 RequestBasedSli 구조의 goodTotalRatio 필드에서 사용됩니다.

애플리케이션에서 이 비율을 구성하는 데 사용할 수 있는 Prometheus 측정항목을 만들어야 합니다. 애플리케이션에서 다음 중 최소 두 개 이상을 만들어야 합니다.

  1. 총 이벤트 수를 계산하는 측정항목. 비율의 totalServiceFilter에서 이 측정항목을 사용합니다.

    모든 이벤트에 대해 증가하는 Prometheus 카운터를 만들 수 있습니다.

  2. '불량' 이벤트를 계산하는 측정항목. 비율의 badServiceFilter에서 이 측정항목을 사용합니다.

    모든 오류 또는 다른 '불량' 이벤트에 대해 증가하는 Prometheus 카운터를 만들 수 있습니다.

  3. '양호' 이벤트를 계산하는 측정항목. 비율의 goodServiceFilter에서 이 측정항목을 사용합니다.

    성공 또는 기타 '양호' 이벤트에 대해 증가하는 Prometheus 카운터를 만들 수 있습니다.

구현 예시의 예시에서는 총 요청 수에 대한 카운터인 nodeRequestsCounter와 실패한 요청 수에 대한 카운터인 nodeFailedRequestsCounter를 만듭니다.

지연 시간 SLI 측정항목

Cloud Monitoring API에서 DistributionCut 구조를 만들어 요청 기반 지연 시간 SLI를 표현합니다. 이 구조는 RequestBasedSli 구조의 distributionCut 필드에서 사용됩니다.

애플리케이션에서 분포-컷 값을 구성하는 데 사용할 수 있는 Prometheus 측정항목을 만들어야 합니다. 이 용도로 Promethus 히스토그램이나 요약을 사용할 수 있습니다. 응답이 SLO에 포함되었는지 여부를 정확하게 측정하는 버킷을 정의하는 방법은 Prometheus 문서의 측정항목 유형을 참조하세요.

구현 예시의 예시에서는 nodeLatenciesHistogram 경로별로 응답 지연 시간의 히스토그램을 만듭니다.

구현 예시

이 섹션에서는 Node.js에서 Prometheus를 사용하여 기본 가용성 및 지연 시간 SLI의 측정항목을 구현하는 예시를 보여줍니다.

도구 작동

Prometheus 측정항목을 노출하도록 서비스를 계측하려면 다음 안내를 따르세요.

  1. 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. 클라이언트를 사용하여 측정항목을 정의합니다.

    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. 익스프레스를 사용하여 Prometheus 측정항목을 노출할 엔드포인트를 정의합니다.

    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. 카운터 측정항목을 적절하게 증가시킵니다.

    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. 지연 시간 측정항목을 적절하게 추적합니다.

    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()

수집 구성

서비스가 엔드포인트에서 실행되고 측정항목을 내보내면 측정항목을 Cloud Monitoring으로 수집하도록 Prometheus 스크레이핑Stackdriver 수집기에 적절한 설정을 구성합니다.

이 구성은 Prometheus 측정항목이 Monitoring에 표시되는 방식을 결정합니다. 이 예시에서 Prometheus 측정항목은 다음과 같이 매핑됩니다.

  • nodeRequestCounterexternal.googleapis.com/prometheus/total_request_count가 됩니다.
  • nodeFailedRequestCounterexternal.googleapis.com/prometheus/error_count가 됩니다.
  • nodeLatenciesHistogramexternal.googleapis.com/prometheus/reponse_latency가 됩니다.

연결된 모니터링 리소스 유형은 k8s_container입니다.

이렇게 수집된 측정항목을 사용하여 SLI를 정의합니다.

가용성 SLI

Cloud Monitoring에서 TimeSeriesRatio 구조를 사용하여 요청 기반 가용성 SLI를 표현합니다. 다음 예시는 수집된 Prometheus 측정항목을 사용하고 28일 동안 총 요청에 대한 불량 비율을 계산하여 서비스가 98% 가용성이라고 예상하는 SLO를 보여줍니다.

{
 "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

Cloud Monitoring에서 DistributionCut 구조를 사용하여 요청 기반 지연 시간 SLI를 표현합니다. 다음 예시는 수집된 Prometheus 지연 시간 측정항목을 사용하고 요청의 98%가 1일 동안 500ms 이내로 완료된다고 예상하는 SLO를 보여줍니다.

{
  "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"
}