Prometheus監(jiān)控業(yè)務(wù)指標(biāo)
時(shí)間:2021-11-09 13:56:35
手機(jī)看文章
掃描二維碼
隨時(shí)隨地手機(jī)看文章
[導(dǎo)讀]在Kubernetes已經(jīng)成了事實(shí)上的容器編排標(biāo)準(zhǔn)之下,微服務(wù)的部署變得非常容易。但隨著微服務(wù)規(guī)模的擴(kuò)大,服務(wù)治理帶來(lái)的挑戰(zhàn)也會(huì)越來(lái)越大。在這樣的背景下出現(xiàn)了服務(wù)可觀測(cè)性(observability)的概念。在分布式系統(tǒng)里,系統(tǒng)的故障可能出現(xiàn)在任何節(jié)點(diǎn),怎么能在出了故障的時(shí)候快速...
-
Lgging,展現(xiàn)的是應(yīng)用運(yùn)行而產(chǎn)生的事件或者程序在執(zhí)行的過(guò)程中間產(chǎn)生的一些日志,可以詳細(xì)解釋系統(tǒng)的運(yùn)行狀態(tài),但是存儲(chǔ)和查詢需要消耗大量的資源。所以往往使用過(guò)濾器減少數(shù)據(jù)量。
-
Metrics,是一種聚合數(shù)值,存儲(chǔ)空間很小,可以觀察系統(tǒng)的狀態(tài)和趨勢(shì),但對(duì)于問(wèn)題定位缺乏細(xì)節(jié)展示。這個(gè)時(shí)候使用等高線指標(biāo)等多維數(shù)據(jù)結(jié)構(gòu)來(lái)增強(qiáng)對(duì)于細(xì)節(jié)的表現(xiàn)力。例如統(tǒng)計(jì)一個(gè)服務(wù)的 TBS 的正確率、成功率、流量等,這是常見(jiàn)的針對(duì)單個(gè)指標(biāo)或者某一個(gè)數(shù)據(jù)庫(kù)的。
-
Tracing,面向的是請(qǐng)求,可以輕松分析出請(qǐng)求中異常點(diǎn),但與Logging有相同的問(wèn)題就是資源消耗較大。通常也需要通過(guò)采樣的方式減少數(shù)據(jù)量。比如一次請(qǐng)求的范圍,也就是從瀏覽器或者手機(jī)端發(fā)起的任何一次調(diào)用,一個(gè)流程化的東西,我們需要軌跡去追蹤。
我們?cè)谶@篇文章討論的主題就是可觀測(cè)性中的Metrics。在 Kubernetes作為基礎(chǔ)設(shè)施的背景下,我們知道Kubernetes本身是個(gè)復(fù)雜的容器編排系統(tǒng),它本身的穩(wěn)定運(yùn)行至關(guān)重要。與之相伴的指標(biāo)監(jiān)控系統(tǒng) Promethues也已經(jīng)成為了云原生服務(wù)下監(jiān)控體系的事實(shí)標(biāo)準(zhǔn)。相信大家對(duì)資源層面比如CPU,Memory,Network;應(yīng)用層面比如Http請(qǐng)求數(shù),請(qǐng)求耗時(shí)等指標(biāo)的監(jiān)控都有所了解。那么業(yè)務(wù)層面的指標(biāo)又怎么利用Prometheus去監(jiān)控和告警呢?這就是這篇文章的核心內(nèi)容。
以我們一個(gè)業(yè)務(wù)場(chǎng)景為例,在系統(tǒng)中有多種類型的task在運(yùn)行,并且task的運(yùn)行時(shí)間各異,task本身有各種狀態(tài)包括待執(zhí)行、執(zhí)行中、執(zhí)行成功、執(zhí)行失敗等。如果想確保系統(tǒng)的穩(wěn)定運(yùn)行,我們必須對(duì)各個(gè)類型的 task 的運(yùn)行狀況了如指掌。比如當(dāng)前是否有任務(wù)擠壓,失敗任務(wù)是否過(guò)多,并且當(dāng)超過(guò)閾值是否告警。
為了解決上述的監(jiān)控告警問(wèn)題,我們先得了解一下Prometheus的指標(biāo)類型。
在形式上,所有的指標(biāo)(Metric)都通過(guò)如下格式標(biāo)示:{=, ...}
標(biāo)簽(label)反映了當(dāng)前樣本的特征維度,通過(guò)這些維度Prometheus可以對(duì)樣本數(shù)據(jù)進(jìn)行過(guò)濾,聚合等。標(biāo)簽的名稱只能由ASCII字符、數(shù)字以及下劃線組成并滿足正則表達(dá)式[a-zA-Z_][a-zA-Z0-9_]*。
指標(biāo)類型
Prometheus定義了4種不同的指標(biāo)類型(metric type):Counter(計(jì)數(shù)器)、Gauge(儀表盤)、Histogram(直方圖)、Summary(摘要)
Counter
Counter類型的指標(biāo)其工作方式和計(jì)數(shù)器一樣,只增不減(除非系統(tǒng)發(fā)生重置)。常見(jiàn)的監(jiān)控指標(biāo),如http_requests_total,node_cpu都是Counter類型的監(jiān)控指標(biāo)。一般在定義Counter類型指標(biāo)的名稱時(shí)推薦使用_total作為后綴。通過(guò)counter指標(biāo)我們可以和容易的了解某個(gè)事件產(chǎn)生的速率變化。例如,通過(guò)rate()函數(shù)獲取HTTP請(qǐng)求量的增長(zhǎng)率:rate(http_requests_total[5m])
Gauge
Gauge類型的指標(biāo)側(cè)重于反應(yīng)系統(tǒng)的當(dāng)前狀態(tài)。因此這類指標(biāo)的樣本數(shù)據(jù)可增可減。常見(jiàn)指標(biāo)如:node_memory_MemFree(主機(jī)當(dāng)前空閑的內(nèi)容大小)、node_memory_MemAvailable(可用內(nèi)存大?。┒际荊auge類型的監(jiān)控指標(biāo)。通過(guò)Gauge指標(biāo),我們可以直接查看系統(tǒng)的當(dāng)前狀態(tài)。node_memory_MemFree
Summary
Summary主用用于統(tǒng)計(jì)和分析樣本的分布情況。比如某Http請(qǐng)求的響應(yīng)時(shí)間大多數(shù)都在100 ms內(nèi),而個(gè)別請(qǐng)求的響應(yīng)時(shí)間需要5s,那么這中情況下統(tǒng)計(jì)指標(biāo)的平均值就不能反映出真實(shí)情況。而如果通過(guò)Summary指標(biāo)我們能立馬看響應(yīng)時(shí)間的9分位數(shù),這樣的指標(biāo)才是有意義的。
例如:# HELP go_gc_duration_seconds A summary of the pause duration of garbage collection cycles.
# TYPE go_gc_duration_seconds summary
go_gc_duration_seconds{quantile="0"} 3.98e-05
go_gc_duration_seconds{quantile="0.25"} 5.31e-05
go_gc_duration_seconds{quantile="0.5"} 6.77e-05
go_gc_duration_seconds{quantile="0.75"} 0.0001428
go_gc_duration_seconds{quantile="1"} 0.0008099
go_gc_duration_seconds_sum 0.0114183
go_gc_duration_seconds_count 85
Histogram
Histogram類型的指標(biāo)同樣用于統(tǒng)計(jì)和樣本分析。與Summary類型的指標(biāo)相似之處在于Histogram類型的樣本同樣會(huì)反應(yīng)當(dāng)前指標(biāo)的記錄的總數(shù)(以_count作為后綴)以及其值的總量(以_sum作為后綴)。不同在于Histogram指標(biāo)直接反應(yīng)了在不同區(qū)間內(nèi)樣本的個(gè)數(shù),區(qū)間通過(guò)標(biāo)簽len進(jìn)行定義。同時(shí)對(duì)于Histogram的指標(biāo),可以通過(guò)histogram_quantile()函數(shù)計(jì)算出其值的分位數(shù)。
例如:# HELP prometheus_http_response_size_bytes Histogram of response size for HTTP requests.
# TYPE prometheus_http_response_size_bytes histogram
prometheus_http_response_size_bytes_bucket{handler="/",le="100"} 1
prometheus_http_response_size_bytes_bucket{handler="/",le="1000"} 1
prometheus_http_response_size_bytes_bucket{handler="/",le="10000"} 1
prometheus_http_response_size_bytes_bucket{handler="/",le="100000"} 1
prometheus_http_response_size_bytes_bucket{handler="/",le="1e 06"} 1
prometheus_http_response_size_bytes_bucket{handler="/",le=" Inf"} 1
prometheus_http_response_size_bytes_sum{handler="/"} 29
prometheus_http_response_size_bytes_count{handler="/"} 1
應(yīng)用指標(biāo)監(jiān)控
Prometheus最常用的方式是通過(guò)pull去抓取Metrics。所以我們首先在服務(wù)通過(guò)/metrics接口暴露指標(biāo),這樣Promethues server就能通過(guò)http請(qǐng)求抓取到我們的業(yè)務(wù)指標(biāo)。
接口示例:server := gin.New()
server.Use(middlewares.AccessLogger(), middlewares.Metric(), gin.Recovery())
server.GET("/health", func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"message": "ok",
})
})
server.GET("/metrics", Monitor)
func Monitor(c *gin.Context) {
h := promhttp.Handler()
h.ServeHTTP(c.Writer, c.Request)
}
定義指標(biāo)
為了方便理解,這里選取了三種類型和兩種業(yè)務(wù)場(chǎng)景的指標(biāo)。
示例:var (
//HTTPReqDuration metric:http_request_duration_seconds
HTTPReqDuration *prometheus.HistogramVec
//HTTPReqTotal metric:http_request_total
HTTPReqTotal *prometheus.CounterVec
// TaskRunning metric:task_running
TaskRunning *prometheus.GaugeVec
)
func init() {
// 監(jiān)控接口請(qǐng)求耗時(shí)
// 指標(biāo)類型是Histogram
HTTPReqDuration = prometheus.NewHistogramVec(prometheus.HistogramOpts{
Name: "http_request_duration_seconds",
Help: "http request latencies in seconds",
Buckets: nil,
}, []string{"method", "path"})
// "method"、"path" 是 label
// 監(jiān)控接口請(qǐng)求次數(shù)
// 指標(biāo)類型是 Counter
HTTPReqTotal = prometheus.NewCounterVec(prometheus.CounterOpts{
Name: "http_requests_total",
Help: "total number of http requests",
}, []string{"method", "path", "status"})
// "method"、"path"、"status" 是 label
// 監(jiān)控當(dāng)前在執(zhí)行的task數(shù)量
// 監(jiān)控類型是Gauge
TaskRunning = prometheus.NewGaugeVec(prometheus.GaugeOpts{
Name: "task_running",
Help: "current count of running task",
}, []string{"type", "state"})
// "type"、"state" 是 label
prometheus.MustRegister(
HTTPReqDuration,
HTTPReqTotal,
TaskRunning,
)
}
生成指標(biāo)
示例:start := time.Now()
c.Next()
duration := float64(time.Since(start)) / float64(time.Second)
path := c.Request.URL.Path
// 請(qǐng)求數(shù)加1
controllers.HTTPReqTotal.With(prometheus.Labels{
"method": c.Request.Method,
"path": path,
"status": strconv.Itoa(c.Writer.Status()),
}).Inc()
// 記錄本次請(qǐng)求處理時(shí)間
controllers.HTTPReqDuration.With(prometheus.Labels{
"method": c.Request.Method,
"path": path,
}).Observe(duration)
// 模擬新建任務(wù)
controllers.TaskRunning.With(prometheus.Labels{
"type": shuffle([]string{"video", "audio"}),
"state": shuffle([]string{"process", "queue"}),
}).Inc()
// 模擬任務(wù)完成
controllers.TaskRunning.With(prometheus.Labels{
"type": shuffle([]string{"video", "audio"}),
"state": shuffle([]string{"process", "queue"}),
}).Dec()
抓取指標(biāo)
Promethues抓取target配置:# 抓取間隔
scrape_interval: 5s
# 目標(biāo)
scrape_configs:
- job_name: 'prometheus'
static_configs:
- targets: ['prometheus:9090']
- job_name: 'local-service'
metrics_path: /metrics
static_configs:
- targets: ['host.docker.internal:8000']
指標(biāo)展示如下圖: