2 분 소요

개요


특징

  • 기존 push 방식이 아닌 pull 방식을 채택
  • Pushgateway를 통해 push 방식도 지원
  • Prometheus에 수집할 엔드포인트와 주기를 설정하면 주기마다 해당 url를 http get하여 결과를 저장하는 방식
  • 직접적인 확장을 지원하지는 않으며 Prometheus에 Prometheus를 연결하거나 Thanos를 이용하여 확장
  • PromQL(Prometheus Query Language) 사용
  • 메트릭을 시각화 해주는 오픈소스인 Grafana와 함께 사용되는 경우가 많음


적합한 경우

  • 숫자 시계열 저장이 필요한 경우


부적합한 경우

  • 100% 정확도가 필요한 경우 수집된 데이터가 부정확할 수 있음


config

	  prometheus.yml: |
	    global:
	      scrape_interval: 15s
	      scrape_timeout: 10s
	    scrape_configs:
	    - job_name: test-1
	      metrics_path: /metrics
	      static_configs:
	      - targets:
	        - 127.0.0.1:10000


설치

        prometheus:
          service:
            type: NodePort

        grafana:
          service:
            type: NodePort
            nodePort: 30100
  • helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
  • helm repo update
  • kubectl create ns prometheus-stack
  • helm install kube-prometheus-stack prometheus-community/kube-prometheus-stack -f kube-prometheus-stack-values.yaml -n prometheus-stack


삭제

  • helm uninstall kube-prometheus-stack -n prometheus-stack


UI 접속 정보

  • Prometheus
    • http://127.0.0.1:30090/
  • Grafana:
    • http://127.0.0.1:30100/


metric_types

  • https://prometheus.io/docs/concepts/metric_types/
  • Counter
    • 0부터 증가만 가능한 단일 숫자 누적 매트릭
  • Gauge
    • 특정 값, 증가, 감소가 가능한 단일 숫자 매트릭
  • Histogram
    • 관찰한 값 들에 대해 관찰 카운트, 관찰 된 값의 합, 관찰 버킷에 대한 누적 카운터를 제공
  • Summary
    • Histogram과 유사
    • 관찰한 값 들에 대해 관찰 카운트, 관찰 된 값의 합, φ-quantiles를 제공


metric exporter example code

  • https://github.com/prometheus/client_golang
  • http://ip:10000에 접속하여 메트릭 값을 변경
  • http://ip:10000/metrics에 접속하여 매트릭 값 확인
  • 자동으로 레지스트리에 추가되는 것을 원하지 않으면 promauto.NewXXX() 대신 prometheus.NewXXX()를 쓰고 prometheus.MustRegister()로 개별 등록
	package main

	import (
		"log"
		"math/rand"
		"net/http"

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

	var (
		counterTest = promauto.NewCounter(
			prometheus.CounterOpts{
				Namespace: "namespace_test",
				Subsystem: "subsystem_test",
				Name:      "counter_test",
				Help:      "counter test help",
			})

		gaugeTest = promauto.NewGauge(prometheus.GaugeOpts{
			Namespace: "namespace_test",
			Subsystem: "subsystem_test",
			Name:      "gauge_test",
			Help:      "gauge test help",
		})

		histogramTest = promauto.NewHistogram(prometheus.HistogramOpts{
			Namespace: "namespace_test",
			Subsystem: "subsystem_test",
			Name:      "histogram_test",
			Help:      "histogram test help",
			Buckets:   prometheus.LinearBuckets(20, 5, 5),
		})

		summaryTest = promauto.NewSummary(prometheus.SummaryOpts{
			Namespace:  "namespace_test",
			Subsystem:  "subsystem_test",
			Name:       "summary_test",
			Help:       "summary test help",
			Objectives: map[float64]float64{0.5: 0.05, 0.9: 0.01, 0.99: 0.001},
		})
	)

	func handler(w http.ResponseWriter, r *http.Request) {
		counterTest.Inc()
		counterTest.Add(1)

		//  gaugeTest.SetToCurrentTime()
		//  gaugeTest.Set(1)
		gaugeTest.Inc()
		gaugeTest.Dec()
		gaugeTest.Add(1)
		gaugeTest.Sub(-1)

		histogramTest.Observe(float64(rand.Intn(10) + 1))

		summaryTest.Observe(float64(rand.Intn(10) + 1))

		w.Write([]byte("ok"))
	}

	func main() {
		http.HandleFunc("/", handler)
		http.Handle("/metrics", promhttp.Handler())
		log.Fatal(http.ListenAndServe(":10000", nil))
	}


Prometheus HTTP API example code

	package main

	import (
		"context"
		"fmt"
		"os"
		"time"

		"github.com/prometheus/client_golang/api"
		v1 "github.com/prometheus/client_golang/api/prometheus/v1"
	)

	func query(address, query string) {
		client, err := api.NewClient(api.Config{
			Address: address,
		})
		if err != nil {
			fmt.Printf("new client error : %s\n", err.Error())
			os.Exit(1)
		}

		v1api := v1.NewAPI(client)

		ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
		defer cancel()

		result, warnings, err := v1api.Query(ctx, query, time.Now())
		if err != nil {
			fmt.Printf("query error : (%s)\n", err.Error())
			os.Exit(1)
		}
		if len(warnings) > 0 {
			fmt.Printf("query warnings : %v\n", warnings)
		}

		fmt.Printf("%v", result)
	}

	func queryRange(address, query string) {
		client, err := api.NewClient(api.Config{
			Address: address,
		})
		if err != nil {
			fmt.Printf("new client error : %s\n", err.Error())
			os.Exit(1)
		}

		v1api := v1.NewAPI(client)

		ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
		defer cancel()

		r := v1.Range{
			Start: time.Now().Add(-time.Hour),
			End:   time.Now(),
			Step:  time.Minute,
		}
		result, warnings, err := v1api.QueryRange(ctx, query, r)
		if err != nil {
			fmt.Printf("query range error : (%s)\n", err.Error())
			os.Exit(1)
		}
		if len(warnings) > 0 {
			fmt.Printf("query range warnings : %v\n", warnings)
		}

		fmt.Printf("%v", result)
	}

	func main() {
		query("http://127.0.0.1:31005", "up")
		fmt.Printf("\n=========================================================\n\n")
		queryRange("http://127.0.0.1:31006", "rate(namespace_test_subsystem_test_counter_test[5m])")
	}