使用 Prometheus 對 Go 應用程序進行監測


監測服務級別的指標能讓團隊成員更清晰的看到你的程序表現如何,你的程序如何被使用,並且可以幫助定位潛在的性能瓶頸。

Prometheus 是一個開源的監測解決方案,原生的服務發現支持讓它成為動態環境下進行服務監測的一個完美選擇。Prometheus 支持從 AWS, Kubernetes, Consul 等 拉取服務 !

當使用 Prometheus 生成服務級別的指標時,有兩個典型的方法:內嵌地運行在一個服務里並在 HTTP 服務器上暴露一個 /metrics 端點,或者創建一個獨立運行的進程,建立一個所謂的導出器。

在這篇指南里,我們從頭到尾過一遍如何使用官方的 Golang 客戶端在基於 Go 的服務中集成 Prometheus。查閱這個關於 向一個基於 worker 的 Go 服務添加指標 的完整示例。


開始使用

Prometheus 程序庫 提供了一個用 Golang 寫成的健壯的插樁庫,可以用來注冊,收集和暴露服務的指標。在講述如何在應用程序中暴露指標前,讓我們先來探究一下 Prometheus 庫提供的各種指標類型。

指標類型

Prometheus 客戶端公開了在暴露服務指標時能夠運用的四種指標類型。查看 Prometheus 的文檔 以獲得關於各種指標類型的深入信息。

Counter(計數器)

counter 是一個累計的指標,代表一個單調遞增的計數器,它的值只會增加或在重啟時重置為零。例如,你可以使用 counter 來代表服務過的請求數,完成的任務數,或者錯誤的次數。

Gauge(計量器)

gauge 是代表一個數值類型的指標,它的值可以增或減。gauge 通常用於一些度量的值例如溫度或是當前內存使用,也可以用於一些可以增減的“計數”,如正在運行的 Goroutine 個數。

Histogram(分布圖)

histogram 對觀測值(類似請求延遲或回復包大小)進行采樣,並用一些可配置的桶來計數。它也會給出一個所有觀測值的總和。

Summary(摘要)

跟 histogram 類似,summary 也對觀測值(類似請求延遲或回復包大小)進行采樣。同時它會給出一個總數以及所有觀測值的總和,它在一個滑動的時間窗口上計算可配置的分位數。

Prometheus HTTP 服務器

在你的服務中集成 prometheus 的第一步就是初始化一個 HTTP 服務器用來提供 Prometheus 的指標。這個服務器應該監聽一個只在你的基礎設施內可用的內部端口;通常是在 9xxx 范圍內。Prometheus 團隊維護一個 默認端口分配 的列表,當你選擇端口時可以參考。

// create a new mux server server := http.NewServeMux() // register a new handler for the /metrics endpoint server.Handle("/metrics", promhttp.Handler()) // start an http server using the mux server http.ListenAndServe(":9001", server) 

這將創建一個新的 HTTP 服務器運行在端口 :9001 上,它將暴露 Prometheus 預期格式的指標。在啟動了 HTTP 服務器后,嘗試運行 curl localhost:9001/metrics. 你將看到如下格式的指標。

# HELP Go_goroutines Number of Goroutines that currently exist.
# TYPE Go_goroutines gauge
go_goroutines 5

對外暴露服務指標

針對這個例子,我們將把 prometheus 統計數據 添加到一個處理后台任務的隊列系統。為了模擬執行時間各不相同的任務,每個任務將 sleep 一個隨機時間。每個 worker 都配置為對它處理的每個任務打印一行日志。

func main() { ... // create a channel with a 10,000 Job buffer jobChannel := make(chan *Job, 10000) // start the job processor Go startJobProcessor(jobChannel) // start a Goroutine to create some mock jobs Go createJobs(jobChannel) ... } // Create a new worker that will process jobs on an job channel func startWorker(workerID string, jobs <- chan *Job) { for { select { // read from the job channel case job := <-jobs: log.Printf( "[%s] Processing job with worker %s\n", time.Now().String(), workerID, ) // fake processing the request time.Sleep(job.Sleep) } } } 

試着執行程序並看一下你是否能夠測定出正在被處理的任務數,等待處理的任務數,或是處理任務所用的時間。也試着看一下這些統計數據在歷史上是什么表現。現在,顯然我們可以把這些信息記錄在一行日志里,把這些日志送到 ELK 集群,然后每天調用一次。但是,在指標和日志之間是存在一個折中的。

由於存儲和傳輸成本都比較低,指標的開銷往往比日志要小。所以我們如何修改我們的服務去添加 Prometheus 統計數據?需要做的第一件事就是修改我們的程序以創建我們想要采集的 Prometheus 指標。

那么讓我們專注於采集三組數據點:已經處理的任務數,等待處理的任務數,以及處理一個任務的平均時間。

添加服務指標

那么首先,讓我們專注於采集已被我們的 worker 處理過的任務數。這個指標也將讓我們能夠采集到單個 worker 處理過的任務數。當你注冊了這個計數器 (counter),你將需要修改 worker 的函數以追蹤處理過的任務數。

var ( totalCounterVec = prometheus.NewCounterVec( prometheus.CounterOpts{ Namespace: "worker", Subsystem: "jobs", Name: "processed_total", Help: "Total number of jobs processed by the workers", }, // We will want to monitor the worker ID that processed the // job, and the type of job that was processed []string{"worker_id", "type"}, ) ) func INIt() { ... // register with the prometheus collector prometheus.MustRegister(totalCounterVec) ... } func startWorker(workerID string, jobs <-chan *Job) { for { select { case job := <-jobs: ... totalCounterVec.WithLabelValues(workerID, job.Type).Inc() ... } } } 

當服務更新后,再次運行它並向 prometheus 端點發請求。你應該會在 prometheus 的輸出中看到一個新的指標代表被給定的 worker 處理過的任務數。輸出會看起來跟下面的類似。

# HELP worker_jobs_processed_total Total jobs processed by the workers
# TYPE worker_jobs_processed_total counter
worker_jobs_processed_total{type="activation",      worker_id="1"} 22
worker_jobs_processed_total{type="activation",      worker_id="2"} 16
worker_jobs_processed_total{type="customer_renew",  worker_id="1"} 1
worker_jobs_processed_total{type="deactivation",    worker_id="2"} 22
worker_jobs_processed_total{type="email",           worker_id="1"} 20
worker_jobs_processed_total{type="order_processed", worker_id="2"} 13
worker_jobs_processed_total{type="transaction",     worker_id="1"} 16

下一步,試試看你能否更新 worker 以采集正在處理的任務數 (提示 : 使用 Guage ???? ) 以及 worker 處理一個任務所花費的時間 (提示 : 使用 Histogram ???? ).


分析數據

在我們能夠分析服務暴露出的指標之前,我們需要對 Prometheus 進行配置,使其能夠向服務拉取指標。

設置 Prometheus

那么,現在我們已經更新了服務,能夠暴露 Prometheus 指標,我們需要配置 Prometheus 使其從我們的服務拉取指標。為此,我們將創建一個新的 prometheus 抓取配置,以便從服務拉取。參閱 Prometheus 文檔 獲得更多關於抓取配置的信息。

scrape_configs:
  - job_name: 'demo'
    # scrape the service every second
    scrape_interval: 1s
    # setup the static configs
    static_configs:
      - targets: ['docker.for.mac.localhost:9009']

接下來,啟動 Prometheus 服務器,開始采集服務暴露的指標。你應當可以使用下列 docker compose 服務配置。

services:
  prometheus:
    image: 'prom/prometheus:latest'
    ports:
    - '8080:8080'
    volumes:
    - './prometheus.yml:/etc/prometheus/prometheus.yml'

查詢數據

注:參閱 查詢的文檔 以獲取更多關於 Prometheus 查詢的信息。

現在 Prometheus 從我們服務的端點抓取指標,你可以使用 Prometheus 的查詢語言來生成對你的應用有意義的指標。例如,目前所有 worker 每秒處理的任務數就應該是一個很重要的指標。我們可以使用 rate() 函數來生成。下列查詢將生成在 5 分鍾間隔內每秒鍾處理的任務數。

sum by (type) (rate(worker_jobs_processed_total[5m]))

監測任務加入隊列的速度對這個服務來講也是一個有用的指標。因為正在處理的任務數指標使用的是 Gauge,所以我們可以使用 deriv() 函數來計算每秒鍾等待處理的任務數的變化速度。這個指標很有用,可以用來判斷當前運行的 worker 處理當前的任務量是否足夠。

sum by (type) (deriv(worker_jobs_inflight[5m]))

另一個 Prometheus 可以計算出的有用指標是一個 worker 處理它的任務所花費的平均時間。為此指標,我們需要使用 rate() 函數來比較處理任務所花費的秒數以及處理完成的任務數。

sum(
  rate(worker_jobs_process_time_seconds_sum[5m])
  /
  rate(worker_jobs_process_time_seconds_count[5m])
)

因為 worker_jobs_process_time_seconds 指標是一個 Histogram,我們可以使用 histogram_quantile()) 函數來顯示一個 worker 處理分配給它的任務所耗時間的第 50, 95, 100 百分位數。這將讓我們更好的看到不同 worker 處理任務所花時間的分布。注意 quantile 函數依賴 le 標簽才能正常工作,且必須包含在 aggregation 里。(非常感謝 @jwenz723 提供這些查詢示例!)

第 50 百分位數

histogram_quantile(
  0.5,
  sum by (worker, le) (rate(worker_jobs_process_time_seconds_bucket[5m]))
)

第 95 百分位數

histogram_quantile(
  0.95,
  sum by (worker, le) (rate(worker_jobs_process_time_seconds_bucket[5m]))
)

第 100 百分位數

histogram_quantile(
  1,
  sum by (worker, le) (rate(worker_jobs_process_time_seconds_bucket[5m]))
)

最后,我推薦配置 Grafana 來向你的 Prometheus 服務器查詢指標。Grafana 是一個令人驚嘆的開源可視化解決方案,它能幫助你把 Prometheus 統計數據變成漂亮的可操作的面板。這里有一些用這種方式創建出的面板。

image

查閱這個關於 向你的 Golang 服務添加 Prometheus 指標 的示例獲得更多 Grafana 面板的例子。


Dec 17, 2018


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM