開發自己的Prometheus Exporter、實現自定義指標

2023-07-16 15:00:27

Prometheus Exporter基礎知識

Prometheus Exporter的概念、工作原理

 Prometheus Exporter是一個用來收集和暴露指標資料的工具,通過與Prometheus監控系統一起使用。它的結構包括兩個元件:Collector和Exporter:
  • Collector:用於從目標應用程式或系統收集指標並將其轉化為Prometheus可識別的格式。收集器可以使用Prometheus使用者端庫來生成指標,並公開HTTP/metrics以便Prometheus Server進行定期呼叫和拉取指標。

  • Exporter:它會從Collector獲取指標資料,並將其轉成為Prometheus可讀格式。

Prometheus Server:它是一個用於儲存和查詢指標資料的中心化實踐序列資料庫伺服器。Prometheus Server使用scrape_configs的YAML組態檔,其中指定了每個Exporter的設定資訊,包括URL、抓取間隔等。

Prometheus 指標格式及其應用

Prometheus使用一種簡單的文字格式來表示指標資料,即「度量名稱{標籤名="標籤值", ...} 值 時間戳」這樣的格式。例如:

http_request_duration_seconds_bucket{le="0.2"} 150 
http_request_duration_seconds_bucket{le="0.5"} 200

其中,」http_request_duration_seconds_bucket」是度量名稱,」le」是標籤名,」0.2」、」0.5」是標籤值, 150、200是相應的值,時間戳則通常省略不寫。

Prometheus指標格式的應用包括:

1.收集指標資料:在Prometheus Exporter中,我們通過Collector來收集指標資料,並將其轉換為合適的指標格式。

2.暴露指標資料:Prometheus Exporter會把採集到的指標資料暴露給Prometheus,使得Prometheus能夠對其進行監控和分析。

3.查詢指標資料:在Prometheus中,我們可以使用PromQL查詢語言對指標資料查詢和分析,比如計算指標的平均值、最大值、最小值等等。

4.視覺化指標:通過Grafana等視覺化工具,我們可以將Prometheus收集到的指標資料進行圖表展示和監控報警等操作。

總之,Prometheus指標格式是Prometheus監控系統中非常重要的一個概念,它是實現收集、查詢、暴露、查詢和視覺化資料的基礎。

Prometheus四種指標型別

Prometheus定義了四種主要的指標型別:

  • Counter(計數器):用於表示單調遞增的指標,例如請求數等。Counter在每次觀測時會增加它所代表的值(通常是一個整數),但不會減少或者重置。

  • Gauge(儀表盤):用於表示可變化的度量值,例如CPU利用率、記憶體用量等。Gauge可以增加、減少或重置,代表著當前的狀態。

  • Histogram(直方圖):用於表示資料樣本的分佈情況,例如請求延遲等。Histogram將資料按照桶(bucket)進行劃分,並對每個桶內的樣本計算出一些統計資訊,如樣本數量、總和、平均值等。

  • Summary(摘要):類似於Histogram,也用於表示資料樣本的分佈情況,但同時展示更多的統計資訊,如樣本數量、總和、平均值、上分位數、下分位數等。

這些指標型別可以組合使用,以便更好的描述系統執行狀態和效能指標。例如使用Counter來記錄某個API的請求數,使用Gauge來記錄記憶體使用量,使用Histogram來記錄請求延遲分佈情況。使用Summary來記錄響應時間分佈情況。在實際應用中應根據需要選擇合適的指標型別來表示監控資料。

Exporter Counter 範例

import (
 "net/http"

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

func main() {
 // 建立一個Counter指標
 counterMetric := prometheus.NewCounter(prometheus.CounterOpts{
  Name: "example_counter", // 指標名稱
  Help: "An example counter metric.", // 指標幫助資訊
 })

 // 註冊指標
 prometheus.MustRegister(counterMetric)

 // 增加指標值
 counterMetric.Inc()

 // 建立一個HTTP處理器來暴露指標
 http.Handle("/metrics", promhttp.Handler())

 // 啟動Web伺服器
 http.ListenAndServe(":8080", nil)
}

Exporter Gauge範例

import (
 "net/http"

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

func main() {
 // 建立一個Gauge指標
 guageMetric := prometheus.NewGauge(prometheus.GaugeOpts{
  Name: "example_gauge", // 指標名稱
  Help: "An example gauge metric.", // 指標幫助資訊
 })

 // 註冊指標
 prometheus.MustRegister(guageMetric)

 // 設定指標值
 guageMetric.Set(100)

 // 建立一個HTTP處理器來暴露指標
 http.Handle("/metrics", promhttp.Handler())

 // 啟動Web伺服器
 http.ListenAndServe(":8080", nil)
}

Exporter Histogram範例

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

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

func main() {
 // 建立一個Histogram指標
 histogramMetric := prometheus.NewHistogram(prometheus.HistogramOpts{
  Name:    "example_histogram", // 指標名稱
  Help:    "An example histogram metric.", // 指標幫助資訊
  Buckets: prometheus.LinearBuckets(0, 10, 10), // 設定桶寬度
 })

 // 註冊指標
 prometheus.MustRegister(histogramMetric)

 // 定期更新指標值
 go func() {
  for {
   time.Sleep(time.Second)
   histogramMetric.Observe(rand.Float64() * 100)
  }
 }()

 // 建立一個HTTP處理器來暴露指標
 http.Handle("/metrics", promhttp.Handler())

 // 啟動Web伺服器
 http.ListenAndServe(":8080", nil)
}

Exporter Summary範例

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

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

func main() {
 // 建立一個Summary指標
 summaryMetric := prometheus.NewSummary(prometheus.SummaryOpts{
  Name:       "example_summary", // 指標名稱
  Help:       "An example summary metric.", // 指標幫助資訊
  Objectives: map[float64]float64{0.5: 0.05, 0.9: 0.01, 0.99: 0.001}, // 設定分位數和偏差
 })

 // 註冊指標
 prometheus.MustRegister(summaryMetric)

 // 定期更新指標值
 go func() {
  for {
   time.Sleep(time.Second)
   summaryMetric.Observe(rand.Float64() * 100)
  }
 }()

 // 建立一個HTTP處理器來暴露指標
 http.Handle("/metrics", promhttp.Handler())

 // 啟動Web伺服器
 http.ListenAndServe(":8080", nil)
}

設定Exporter 開發環境

在kubernetes中安裝Prometheus

  • 建立一個prometheus-configmap.yaml檔案,用於定義Prometheus設定

apiVersion: v1
kind: ConfigMap
metadata:
  name: prometheus-config
data:
  prometheus.yml: |
    global:
      scrape_interval: 15s
    scrape_configs:
      - job_name: 'pushgateway'
        static_configs:
          - targets: ['pushgateway:9091']

該設定為Prometheus提供了一些基本選項,並定義了pushgateway的作業。pushgateway用於從pushgateway範例中收集指標資料。

  • 使用kubectl apply命令建立Prometheus ConfigMap

kubectl apply -f prometheus-configmap.yaml
  • 建立一個prometheus-deployment.yaml檔案,用於定義Prometheus Deployment的設定

apiVersion: apps/v1
kind: Deployment
metadata:
  name: prometheus
spec:
  selector:
    matchLabels:
      app: prometheus
  replicas: 1
  template:
    metadata:
      labels:
        app: prometheus
    spec:
      containers:
      - name: prometheus
        image: prom/prometheus:v2.32.0
        args:
        - "--config.file=/etc/prometheus/prometheus.yml"
        ports:
        - containerPort: 9090
        volumeMounts:  # 掛載ConfigMap中的prometheus.yml組態檔
        - name: config-volume
          mountPath: /etc/prometheus
      volumes:
      - name: config-volume
        configMap:
          name: prometheus-config  #參照第一步建立的prometheus-config ConfigMap
  • 建立一個Service物件,在kubernetes叢集內部和外部暴露Prometheus服務的存取端點

apiVersion: v1
kind: Service
metadata:
  name: prometheus
spec:
  type: NodePort
  ports:
  - port: 9090
    targetPort: 9090 
  selector:
    app: prometheus
  • 存取Prometheus UI

kubectl apply get svc

http://nodeIP:埠/graph

安裝PushGateway

  • 建立一個pushgateway-deployment.yaml檔案,用於定義PushGateway Deployment的設定

apiVersion: apps/v1
kind: Deployment
metadata:
  name: pushgateway
spec:
  selector:
    matchLabels:
      app: pushgateway
  replicas: 1
  template:
    metadata:
      labels:
        app: pushgateway
    spec:
      containers:
      - name: pushgateway
        image: prom/pushgateway:v1.5.1
        ports:
        - containerPort: 9091
        args:
        - "--web.listen-address=:9091"
  • 使用kubectl apply命令建立PushGateway Deployment

kubectl apply -f pushgateway-deployment.yaml
  • 建立一個pushgateway-service.yaml檔案,用於定義pushgateway service設定

apiVersion: v1
kind: Service
metadata:
  name: pushgateway
spec:
  type: NodePort
  ports:
  - port: 9091
    targetPort: 9091
  selector:
    app: pushgateway
  • 使用kubectl apply命令建立PushGateway Service

kubectl apply -f pushgateway-service.yaml
  • 在Prometheus組態檔中新增Pushgateway作為目錄,以便Promethazine可以獲取Pushgateway中的指標資料(在第一步prometheus-configmap.yaml中已加過如下設定、此步跳過)。

scrape_configs:
  - job_name: 'pushgateway'
    static_configs:
      - targets: ['pushgateway:9091']
  • 存取PushGateway

  • Prometheus UI中檢視pushgateway

安裝Grafana並連線Prometheus

  • 建立grafana.yaml用於定於grafana的設定

kind: Deployment
metadata:
  name: grafana-dp
spec:
  selector:
    matchLabels:
      app: grafana-dp
  replicas: 1
  template:
    metadata:
      labels:
        app: grafana-dp
    spec:
      containers:
        - name: grafana
          image: grafana/grafana
          imagePullPolicy: IfNotPresent
          ports:
          - containerPort: 3000

---
apiVersion: v1
kind: Service
metadata:
  name: grafana-svc
spec:
  type: NodePort
  ports:
    - port: 3000
      targetPort: 3000
  selector:
    app: grafana-dp
  • 建立grafana deployment和service

kubectl apply -f grafana.yaml
kubectl get svc

使用者名稱admin密碼admin

  • 新增Prometheus資料來源

測Go/Python編寫的HTTP Server

https://github.com/prometheus/client_golang

https://prometheus.io/docs/instrumenting/clientlibs/

https://prometheus.io/docs/tutorials/instrumenting_http_server_in_go/

使用client_golang庫測HTTP server

package main

import (
   "fmt"
   "net/http"

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

var pingCounter = prometheus.NewCounter(
   prometheus.CounterOpts{
       Name: "ping_request_count",
       Help: "No of request handled by Ping handler",
   },
)

func ping(w http.ResponseWriter, req *http.Request) {
   pingCounter.Inc()
   fmt.Fprintf(w, "pong")
}

func main() {
   prometheus.MustRegister(pingCounter)

   http.HandleFunc("/ping", ping)
   http.Handle("/metrics", promhttp.Handler())
   http.ListenAndServe(":8090", nil)
}
  • 存取metrics介面

 

使用client_python庫測HTTP server

https://github.com/prometheus/client_python

from flask import Flask
from werkzeug.serving import run_simple
from werkzeug.middleware.dispatcher import DispatcherMiddleware
from prometheus_client import make_wsgi_app

app = Flask(__name__)
app.debug = True
app.wsgi_app = DispatcherMiddleware(app.wsgi_app, {
    '/metrics': make_wsgi_app()
})

@app.route('/')
def hello_world():
    return 'Hello World!'

if __name__ == '__main__':
    run_simple('localhost', 5000, app, use_reloader=True, use_debugger=True, use_evalex=True)
  • requirements.txt

Flask==2.3.2
uWSGI==2.0.21
prometheus-client==0.17.0
  • 啟動Web應用程式

pip install uwsgi
uwsgi --http 127.0.0.1:8000 --wsgi-file main.py --callable app
  • 存取metrics介面

 

Nginx Exporter開發

Nginx status簡介

http_stub_status_module時nginx一個模組,它提供了一些簡單的指標和狀態資訊,這些資訊可用於監控nginx伺服器的監控狀態和效能。要啟用該模組,需要在nginx組態檔中新增以下內容:

location /nginx_status {
  stub_status on;
  allow 127.0.0.1;
  deny all;
}

此設定將使用nginx在nginx_status上公開stub_status資訊。這些資訊只允許本地主機(即127.0.0.1)上存取,並拒絕來自其他主機上的存取請求。然後,您可以使用curl等工具向nginx伺服器傳送HTTP GET請求,以獲取當前的狀態資訊。例如,以下命令將獲取位於localhost上的nginx伺服器的狀態資訊:

curl http://localhost/nginx_status

此命令將返回像這樣的響應:

Active connections: 1
server accepts handled requests
10 10 10
Reading: 0 Writing: 1 Waiting: 0
  • Active connectons:當前活躍的連線數。

  • Server Accepts handled requests: 表示從啟動到現在一共處理過的連線數,其中accepts表示接收的連線數,handled表示已經處理完成的連線數(可能包括已經關閉的連線數),requests表示已經處理完成的請求數;

  • Reading: 正在讀取使用者端的請求的連線數;

  • Writing:正在向用戶端傳送響應的連線數;

  • Waiting:正在等待請求處理的連線數。

除了以上資訊,可以通過第三方模組來擴充套件stub_status模組的功能,例如:ngx_http_substitutions_filter_module、lua-nginx-module。

Nginx Exporter開發

專案原始碼:https://gitee.com/KubeSec/nginx-exporter

  • /metrics

Exporter部署和測試

cd nginx-exporter
docker build -t nginx-exporter:latest .
kubectl apply -f manifests/install.yaml
  • 存取/metrics

http://nodeIP:nodePort/metrics

  • 在Prometheus中設定我們的nginx-exporter

    scrape_configs:
      - job_name: 'nginx-exporter'
        static_configs:
          - targets: ['nginx-exporter-svc:2113']
  • 在Prometheus UI中檢視我的 exporter

  • 補充: memcached_exporter_example專案原始碼https://gitee.com/KubeSec/memcached_exporter

 

使用Grafana視覺化指標

Grafana圖表型別

  • Time Series:時序資料,用於顯示時間序列資料。可以使用折線圖、面截圖、柱狀圖等形式來呈現。

  • Stat: 可以用於顯示資料的統計資訊,例如平均值、總和、最大值、最小值等。

  • Table: 用以表格形式顯示資料。可以將資料按行或列進行排序,並支援篩選和聚合操作。

 PromQL 查詢和分析指標資料

PromQL(Prometheus Query Language)是由Prometheus開發的一種靈活的查詢語言,用於收集的時間序列中提取、聚合和分析Metric。

up: 返回當前所有Exporter的狀態, 1表示健康正在執行,0表示不可存取或異常。sum(nginx_status_requests{job="nginx-exporter", instance="nginx-exporter-svc:2113"})要檢索Nginx伺服器中當前連結的數量。

建立Nginx Exporter Dashboard

Grafana匯入manifests/nginx-exporter-dashboard.json