一文帶你瞭解 Prometheus

作者:kevinkrcai,騰訊 IEG 後臺開發工程師

Prometheus 是一個開源的完整監控解決方案,本文將從指標抓取到查詢及可視化展示,以及最後的監控告警,對 Prometheus 做一個基本的認識。

1. 簡介

Prometheus 是古希臘神話裏泰坦族的一名神明,名字的意思是 "先見之明",下圖中是 Prometheus 被宙斯懲罰,飽受肝臟日食夜長之苦。

下面就是我們 CRUD Boy 所瞭解的 Prometheus,下面是其官網封面圖引導語:From metrics to insight,從指標到洞察力,通過指標去洞察你的系統,爲我們的系統提供指標收集和監控的開源解決方案。也就是說,Prometheus 是一個數據監控的解決方案,讓我們能隨時掌握系統運行的狀態,快速定位問題和排除故障。

Prometheus 發展速度很快,12 年開發完成,16 年加入 CNCF,成爲繼 K8s 之後第二個 CNCF 託管的項目,目前 Github 42k 的 🌟,而且社區很活躍,維護頻率很高,基本穩定在 1 個月 1 個小版本的迭代速度。

2. 整體生態

Prometheus 提供了從指標暴露,到指標抓取、存儲和可視化,以及最後的監控告警等一系列組件。

2.1 指標暴露

每一個被 Prometheus 監控的服務都是一個 Job,Prometheus 爲這些 Job 提供了官方的 SDK ,利用這個 SDK 可以自定義並導出自己的業務指標,也可以使用 Prometheus 官方提供的各種常用組件和中間件的 Exporter(比如常用的 MySQL,Consul 等等)。對於短時間執行的腳本任務或者不好直接 Pull 指標的服務,Prometheus 提供了 PushGateWay 網關給這些任務將服務指標主動推 Push 到網關,Prometheus 再從這個網關裏 Pull 指標。

2.2 指標抓取

上面提到了 Push 和 Pull,其實這是兩種指標抓取模型。

被監控服務一般通過主動暴露 metrics 端口或者通過 Exporter 的方式暴露指標,監控服務依賴服務發現模塊發現被監控服務,從而去定期的抓取指標。

對於 Prometheus 中的指標抓取,採用的是 Pull 模型,默認是一分鐘去拉取一次指標,通過 Prometheus.yaml 配置文件中的 scrape_interval 配置項配置,Prometheus 對外都是用的 Pull 模型,一個是 Pull Exporter 的暴露的指標,一個是 Pull PushGateway 暴露的指標。

2.3 指標存儲和查詢

指標抓取後會存儲在內置的時序數據庫中,Prometheus 也提供了 PromQL 查詢語言給我們做指標的查詢,我們可以在 Prometheus 的 WebUI 上通過 PromQL,可視化查詢我們的指標,也可以很方便的接入第三方的可視化工具,例如 grafana。

2.4 監控告警

prometheus 提供了 alertmanageer 基於 promql 來做系統的監控告警,當 promql 查詢出來的指標超過我們定義的閾值時,prometheus 會發送一條告警信息到 alertmanager,manager 會將告警下發到配置好的郵箱或者微信。

3. 工作原理

Prometheus 的從被監控服務的註冊到指標抓取到指標查詢的流程分爲五個步驟:

3.1 服務註冊

被監控服務在 Prometheus 中是一個 Job 存在,被監控服務的所有實例在 Prometheus 中是一個 target 的存在,所以被監控服務的註冊就是在 Prometheus 中註冊一個 Job 和其所有的 target,這個註冊分爲:

靜態註冊:靜態的將服務的 IP 和抓取指標的端口號配置在 Prometheus yaml 文件的 scrape_configs 配置下:

scrape_configs:
 - job_name: "prometheus"
   static_configs:
   - targets: ["localhost:9090"]

以上就是註冊了一個名爲 prometheus 的服務,這個服務下有一個實例,暴露的抓取地址是 localhost:9090。

動態註冊:動態註冊就是在 Prometheus yaml 文件的 scrape_configs 配置下配置服務發現的地址和服務名,Prometheus 會去該地址,根據你提供的服務名動態發現實例列表,在 Prometheus 中,支持 consul,DNS,文件,K8s 等多種服務發現機制。

基於 consul 的服務發現:

 - job_name: "node_export_consul"
  metrics_path: /node_metrics
  scheme: http
  consul_sd_configs:
   - server: localhost:8500
     services:
     - node_exporter

我們 consul 的地址就是:localhost:8500,服務名是 node_exporter,在這個服務下有一個 exporter 實例:localhost:9600。

注意:如果是動態註冊,最好加上這兩配置,靜態註冊指標拉取的路徑會默認的幫我們指定爲 metrics_path:/metrics,所以如果暴露的指標抓取路徑不同或者是動態的服務註冊,最好加上這兩個配置。不然會報錯 “INVALID“ is not a valid start token,演示下,百度了一下,這裏可能是數據格式不統一導致。

metrics_path: /node_metrics
scheme: http

最後可以在 webUI 中查看發現的實例:

目前,Prometheus 支持多達二十多種服務發現協議:

<azure_sd_config>
<consul_sd_config>
<digitalocean_sd_config>
<docker_sd_config>
<dockerswarm_sd_config>
<dns_sd_config>
<ec2_sd_config>
<openstack_sd_config>
<file_sd_config>
<gce_sd_config>
<hetzner_sd_config>
<http_sd_config>
<kubernetes_sd_config>
<kuma_sd_config>
<lightsail_sd_config>
<linode_sd_config>
<marathon_sd_config>
<nerve_sd_config>
<serverset_sd_config>
<triton_sd_config>
<eureka_sd_config>
<scaleway_sd_config>
<static_config>

3.2 配置更新

在更新完 Prometheus 的配置文件後,我們需要更新我們的配置到程序內存裏,這裏的更新方式有兩種,第一種簡單粗暴,就是重啓 Prometheus,第二種是動態更新的方式。如何實現動態的更新 Prometheus 配置。

第一步:首先要保證啓動 Prometheus 的時候帶上啓動參數:--web.enable-lifecycle

prometheus --config.file=/usr/local/etc/prometheus.yml --web.enable-lifecycle

第二步:去更新我們的 Prometheus 配置:

第三步:更新完配置後,我們可以通過 Post 請求的方式,動態更新配置:

curl -v --request POST 'http://localhost:9090/-/reload'

原理:

Prometheus 在 web 模塊中,註冊了一個 handler:

if o.EnableLifecycle {
  router.Post("/-/quit", h.quit)
  router.Put("/-/quit", h.quit)
  router.Post("/-/reload", h.reload) // reload配置
  router.Put("/-/reload", h.reload)
}

通過 h.reload 這個 handler 方法實現:這個 handler 就是往一個 channle 中發送一個信號:

func (h *Handler) reload(w http.ResponseWriter, r *http.Request) {
  rc := make(chan error)
  h.reloadCh <- rc  // 發送一個信號到channe了中
  if err := <-rc; err != nil {
   http.Error(w, fmt.Sprintf("failed to reload config: %s", err), http.StatusInternalServerError)
  }
}

在 main 函數中會去監聽這個 channel,只要有監聽到信號,就會做配置的 reload,重新將新配置加載到內存中:

case rc := <-webHandler.Reload():
  if err := reloadConfig(cfg.configFile, cfg.enableExpandExternalLabels, cfg.tsdb.EnableExemplarStorage, logger, noStepSubqueryInterval, reloaders...); err != nil {
   level.Error(logger).Log("msg""Error reloading config""err", err)
   rc <- err
  } else {
   rc <- nil
  }

3.3 指標抓取和存儲

Prometheus 對指標的抓取採取主動 Pull 的方式,即週期性的請求被監控服務暴露的 metrics 接口或者是 PushGateway,從而獲取到 Metrics 指標,默認時間是 15s 抓取一次,配置項如下:

global:
 scrape_interval: 15s

抓取到的指標會被以時間序列的形式保存在內存中,並且定時刷到磁盤上,默認是兩個小時回刷一次。並且爲了防止 Prometheus 發生崩潰或重啓時能夠恢復數據,Prometheus 也提供了類似 MySQL 中 binlog 一樣的預寫日誌,當 Prometheus 崩潰重啓時,會讀這個預寫日誌來恢復數據。

4. Metric 指標

4.1 數據模型

Prometheus 採集的所有指標都是以時間序列的形式進行存儲,每一個時間序列有三部分組成:

可以通過查看 Prometheus 的 metrics 接口查看所有上報的指標:

所有的指標也都是通過如下所示的格式來標識的:

# HELP  // HELP:這裏描述的指標的信息,表示這個是一個什麼指標,統計什麼的
# TYPE  // TYPE:這個指標是什麼類型的
<metric name>{<label name>=<label value>, ...} value  // 指標的具體格式,<指標名>{標籤集合} 指標值
4.2 指標類型

Prometheus 底層存儲上其實並沒有對指標做類型的區分,都是以時間序列的形式存儲,但是爲了方便用戶的使用和理解不同監控指標之間的差異,Prometheus 定義了 4 種不同的指標類型:計數器 counter,儀表盤 gauge,直方圖 histogram,摘要 summary。

Counter 計數器:

Counter 類型和 redis 的自增命令一樣,只增不減,通過 Counter 指標可以統計 Http 請求數量,請求錯誤數,接口調用次數等單調遞增的數據。同時可以結合 increase 和 rate 等函數統計變化速率,後續我們會提到這些內置函數。

Gauge 儀表盤:

和 Counter 不同,Gauge 是可增可減的,可以反映一些動態變化的數據,例如當前內存佔用,CPU 利用,Gc 次數等動態可上升可下降的數據,在 Prometheus 上通過 Gauge,可以不用經過內置函數直觀的反映數據的變化情況,如下圖表示堆可分配的空間大小:

上面兩種是數值指標,代表數據的變化情況,Histogram 和 Summary 是統計類型的指標,表示數據的分佈情況。

Histogram 直方圖:

Histogram 是一種直方圖類型,可以觀察到指標在各個不同的區間範圍的分佈情況,如下圖所示:可以觀察到請求耗時在各個桶的分佈。

有一點要注意的是,Histogram 是累計直方圖,即每一個桶的是隻有上區間,例如下圖表示小於 0.1 毫秒(le="0.1")的請求數量是 18173 個,小於 0.2 毫秒(le="0.2") 的請求是 18182 個,在 le="0.2" 這個桶中是包含了 le="0.1" 這個桶的數據,如果我們要拿到 0.1 毫秒到 0.2 毫秒的請求數量,可以通過兩個桶想減得到。

在直方圖中,還可以通過 histogram_quantile 函數求出百分位數,比如 P50,P90,P99 等數據

Summary 摘要

Summary 也是用來做統計分析的,和 Histogram 區別在於,Summary 直接存儲的就是百分位數,如下所示:可以直觀的觀察到樣本的中位數,P90 和 P99。

Summary 的百分位數是客戶端計算好直接讓 Prometheus 抓取的,不需要 Prometheus 計算,直方圖是通過內置函數 histogram_quantile 在 Prometheus 服務端計算求出。

4.3 指標導出

指標導出有兩種方式,一種是使用 Prometheus 社區提供的定製好的 Exporter 對一些組件諸如 MySQL,Kafka 等的指標作導出,也可以利用社區提供的 Client 來自定義指標導出。

github.com/prometheus/client_golang/prometheus/promhttp

自定義 Prometheus exporter:

package main
import (
  "net/http"
  "github.com/prometheus/client_golang/prometheus/promhttp"
)

func main() {
  http.Handle("/metrics", promhttp.Handler())
  http.ListenAndServe(":8080", nil)
}

訪問:http://localhost:8080/metrics,即可看到導出的指標,這裏我們沒有自定義任何的指標,但是能看到一些內置的 Go 的運行時指標和 promhttp 相關的指標,這個 Client 默認爲我們暴露的指標,go__:以 go_ 爲前綴的指標是關於 Go 運行時相關的指標,比如垃圾回收時間、goroutine 數量等,這些都是 Go 客戶端庫特有的,其他語言的客戶端庫可能會暴露各自語言的其他運行時指標。promhttp**:來自 promhttp 工具包的相關指標,用於跟蹤對指標請求的處理。

添加自定義指標:

package main
import (
  "net/http"

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

func main() {

  // 1.定義指標(類型,名字,幫助信息)
  myCounter := prometheus.NewCounter(prometheus.CounterOpts{
   Name: "my_counter_total",
   Help: "自定義counter",
  })
  // 2.註冊指標
  prometheus.MustRegister(myCounter)
  // 3.設置指標值
  myCounter.Add(23)

  http.Handle("/metrics", promhttp.Handler())
  http.ListenAndServe(":8080", nil)

}

運行:

模擬下在業務中上報接口請求量:

package main

import (
  "fmt"
  "net/http"
  "github.com/prometheus/client_golang/prometheus"
)

var (
  MyCounter prometheus.Counter
)


// init 註冊指標
func init() {
  // 1.定義指標(類型,名字,幫助信息)
  MyCounter = prometheus.NewCounter(prometheus.CounterOpts{
   Name: "my_counter_total",
   Help: "自定義counter",
  })

  // 2.註冊指標
  prometheus.MustRegister(MyCounter)
}

// Sayhello
func Sayhello(w http.ResponseWriter, r *http.Request) {
  // 接口請求量遞增
  MyCounter.Inc()
  fmt.Fprintf(w, "Hello Wrold!")
}

main.go:

package main
import (
  "net/http"
  "github.com/prometheus/client_golang/prometheus/promhttp"
)
func main() {
  http.Handle("/metrics", promhttp.Handler())
  http.HandleFunc("/counter",Sayhello)
  http.ListenAndServe(":8080", nil)
}

一開始啓動時,指標 counter 是 0:

調用:/counter 接口後,指標數據發生了變化,這樣就可以簡單實現了接口請求數的統計:

對於其他指標定義方式是一樣的:

var (
  MyCounter prometheus.Counter
  MyGauge prometheus.Gauge
  MyHistogram prometheus.Histogram
  MySummary prometheus.Summary

)

// init 註冊指標
func init() {
  // 1.定義指標(類型,名字,幫助信息)
  MyCounter = prometheus.NewCounter(prometheus.CounterOpts{
   Name: "my_counter_total",
   Help: "自定義counter",
  })

  // 定義gauge類型指標
  MyGauge = prometheus.NewGauge(prometheus.GaugeOpts{
   Name: "my_gauge_num",
   Help: "自定義gauge",

  })

  // 定義histogram
  MyHistogram = prometheus.NewHistogram(prometheus.HistogramOpts{
   Name: "my_histogram_bucket",
   Help: "自定義histogram",
   Buckets: []float64{0.1,0.2,0.3,0.4,0.5},  // 需要指定桶

  })

  // 定義Summary
  MySummary = prometheus.NewSummary(prometheus.SummaryOpts{
   Name: "my_summary_bucket",
   Help: "自定義summary",
   // 這部分可以算好後在set
   Objectives: map[float64]float64{
     0.5: 0.05,
     0.9: 0.01,
     0.99: 0.001,
   },
  })

  // 2.註冊指標
  prometheus.MustRegister(MyCounter)
  prometheus.MustRegister(MyGauge)
  prometheus.MustRegister(MyHistogram)
  prometheus.MustRegister(MySummary)
}

上面的指標都是沒有設置標籤的,我們一般的指標都是帶有標籤的,如何設置指標的標籤呢?

如果我要設置帶標籤的 counter 類型指標,只需要將原來的 NewCounter 方法替換爲 NewCounterVec 方法即可,並且傳入標籤集合。

MyCounter *prometheus.CounterVec
// 1.定義指標(類型,名字,幫助信息)
MyCounter = prometheus.NewCounterVec(
  prometheus.CounterOpts{
  Name: "my_counter_total",
  Help: "自定義counter",
  },
  // 標籤集合
  []string{"label1","label2"},
)
// 帶標籤的set指標值
MyCounter.With(prometheus.Labels{"label1":"1","label2":"2"}).Inc()

其他同理。

5. PromQL

剛剛提到了 Prometheus 中指標有哪些類型以及如何導出我們的指標,現在指標導出到 Prometheus 了,利用其提供的 PromQL 可以查詢我們導出的指標。

PromQL 是 Prometheus 爲我們提供的函數式的查詢語言,查詢表達式有四種類型:

5.1 瞬時查詢

直接通過指標名即可進行查詢,查詢結果是當前指標最新的時間序列,比如查詢 Gc 累積消耗的時間:

go_gc_duration_seconds_count

img

我們可以看到查詢出來有多個同名指標結果,可以用 {} 做標籤過濾查詢:比如我們想查指定實例的指標。

go_gc_duration_seconds_count{instance="127.0.0.1:9600"}

而且也支持則表達式,通過 =~ 指定正則表達式,如下所示:查詢所有 instance 是 localhost 開頭的指標。

go_gc_duration_seconds_count{instance=~"localhost.*"}

5.2 範圍查詢

範圍查詢的結果集就是區間向量,可以通過 [] 指定時間來做範圍查詢,查詢 5 分鐘內的 Gc 累積消耗時間:

go_gc_duration_seconds_count{}[5m]

注意:這裏範圍查詢第一個點並不一定精確到剛剛好 5 分鐘前的那個時序樣本點,他是以 5 分鐘作爲一個區間,尋找這個區間的第一個點到最後一個樣本點。

時間單位:

d:天,h:小時,m:分鐘,ms:毫秒,s:秒,w:周,y:年

同樣支持類似 SQL 中的 offset 查詢,如下:查詢一天前當前 5 分鐘前的時序數據集:

go_gc_duration_seconds_count{}[5m] offset 1d

5.3 內置函數

Prometheus 內置了很多函數,這裏主要記錄下常用的幾個函數的使用:

rate 和 irate 函數:

rate 函數可以用來求指標的平均變化速率

rate函數=時間區間前後兩個點的差 / 時間範圍

一般 rate 函數可以用來求某個時間區間內的請求速率,也就是我們常說的 QPS:

但是 rate 函數只是算出來了某個時間區間內的平均速率,沒辦法反映突發變化,假設在一分鐘的時間區間裏,前 50 秒的請求量都是 0 到 10 左右,但是最後 10 秒的請求量暴增到 100 以上,這時候算出來的值可能無法很好的反映這個峯值變化。這個問題可以通過 irate 函數解決,irate 函數求出來的就是瞬時變化率。

時間區間內最後兩個樣本點的差 / 最後兩個樣本點的時間差

可以通過圖像看下兩者的區別:irate 函數的圖像峯值變化大,rate 函數變化較爲平緩。

rate 函數:

irate 函數:

聚合函數:Sum() by() without()

也是上邊的例子,我們在求指定接口的 QPS 的時候,可能會出現多個實例的 QPS 的計算結果,如下是存在多個接口,三個服務的 QPS。

rate(demo_api_request_duration_seconds_count{job="demo", method="GET", status="200"}[5m])

利用 sum 函數可以將三個 QPS 聚合,即可得到整個服務該接口的 QPS:其實 Sum 就是將指標值做相加。

但是這樣直接的相加太籠統抽象了,可以配合 by 和 without 函數在 sum 的時候,基於某些標籤分組,類似 SQL 中的 group by

例如,我可以根據請求接口標籤分組:這樣拿到的就是具體接口的 QPS:

sum(rate(demo_api_request_duration_seconds_count{job="demo", method="GET", status="200"}[5m])) by(path)

也可以不根據接口路徑分組:通過 without 指定:

sum(rate(demo_api_request_duration_seconds_count{job="demo", method="GET", status="200"}[5m])) without(path)

可以通過 histogram_quantile 函數做數據統計:可以用來統計百分位數:第一個參數是百分位,第二個 histogram 指標,這樣計算出來的就是中位數,即 P50

histogram_quantile(0.5,go_gc_pauses_seconds_total_bucket)

分享之前和同事一起發現的坑:

在剛剛寫的自定義 exporter 上新增幾個 histogram 的樣本點:

MyHistogram.Observe(0.3)
MyHistogram.Observe(0.4)
MyHistogram.Observe(0.5)

histogram 的桶設置:

MyHistogram = prometheus.NewHistogram(prometheus.HistogramOpts{
  Name: "my_histogram_bucket",
  Help: "自定義histogram",
  Buckets: []float64{0,2.5,5,7.5,10},  // 需要指定桶
})

如果這樣的話,所有指標都會直接進入到第一個桶,即 0 到 2.5 這個桶,如果我要計算中位數,那麼這個中位數按照數學公式來算的話,肯定是在 0 到 2.5 之間的,而且肯定是 0.3 到 0.5 之間。

我用 histogram_quantile 函數計算下:計算結果是 1.25,其實已經不對了。

histogram_quantile(0.5,my_histogram_bucket_bucket)

我在計算下 P99,等於 2.475:

histogram_quantile(0.99,my_histogram_bucket_bucket)

我的指標都是不大於 1 的,爲啥算出來的 P50 和 P99 都這麼離譜呢?

這是因爲 Prometheus 他是不保存你具體的指標數值的,他會幫你把指標放到具體的桶,但是他不會保存你指標的值,計算的分位數是一個預估的值,怎麼預估呢?就是假設每個桶內的樣本分佈是均勻的,線性分佈來計算的,比如剛剛的 P50,其實就是算排在第 50% 位置的樣本值,因爲剛剛所有的數據都落在了第一個桶,那麼他在計算的時候就會假定這個 50% 值在第一個桶的中點,他就會假定這個數就是 0.5 _ 2.5,P99 就是第一個桶的 99% 的位置,他就會假定這個數就是 0.99 _ 2.5。

導致這個誤差較大的原因就是我們的 bucket 設置的不合理。

重新定義桶:

// 定義histogram
MyHistogram = prometheus.NewHistogram(prometheus.HistogramOpts{
  Name: "my_histogram_bucket",
  Help: "自定義histogram",
  Buckets: []float64{0.1,0.2,0.3,0.4,0.5},  // 需要指定桶
})

上報數據:

MyHistogram.Observe(0.1)
MyHistogram.Observe(0.3)
MyHistogram.Observe(0.4)

重新計算 P50,P99:

桶設置的越合理,計算的誤差越小

6. Grafana 可視化

除了可以利用 Prometheus 提供的 webUI 可視化我們的指標外,還可以接入 Grafana 來做指標的可視化。

第一步,對接數據源:

配置好 prometheus 的地址:

第二步:創建儀表盤

編輯儀表盤:

在 metrics 處編寫 PromQL 即可完成查詢和可視化:

儀表盤編輯完後,可以導出對應的 json 文件,方便下次導入同樣的儀表盤:

以上是我之前搭建的儀表盤:

7. 監控告警

AlertManager 是 prometheus 提供的告警信息下發組件,包含了對告警信息的分組,下發,靜默等策略。配置完成後可以在 webui 上看到對應的告警策略信息。告警規則也是基於 PromQL 進行定製的。

編寫告警配置:當 Http_srv 這個服務掛了,Prometheus 採集不到指標,並且持續時間 1 分鐘,就會觸發告警

groups:
\- name: simulator-alert-rule
 rules:
 \- alert: HttpSimulatorDown
  expr: sum(up{job="http_srv"}) == 0
  for: 1m
  labels:
   severity: critical

在 prometheus.yml 中配置告警配置文件,需要配置上 alertmanager 的地址和告警文件的地址

\# Alertmanager configuration
alerting:
 alertmanagers:
 \- static_configs:
  \- targets: ['localhost:9093']
\# Load rules once and periodically evaluate them according to the global 'evaluation_interval'.
rule_files:
  \- "alert_rules.yml"
  \#"first_rules.yml"

配置告警信息,例如告警發送地址,告警內容模版,分組策略等都在 alertmanager 的配置文件中配置:

global:
 smtp_smarthost: 'smtp.qq.com:465'
 smtp_from: 'xxxx@qq.com'
 smtp_auth_username: 'xxxx@qq.com'
 smtp_auth_password: 'xxxx'
 smtp_require_tls: false

route:
 group_interval: 1m
 repeat_interval: 1m
 receiver: 'mail-receiver'


# group_by       //採用哪個標籤作爲分組
# group_wait      //分組等待的時間,收到報警不是立馬發送出去,而是等待一段時間,看看同一組中是否有其他報警,如果有一併發送
# group_interval    //告警時間間隔
# repeat_interval   //重複告警時間間隔,可以減少發送告警的頻率
# receiver       //接收者是誰
# routes        //子路由配置

receivers:
\- name: 'mail-receiver'
 email_configs:
  \- to: 'xxxx@qq.com'

當我 kill 進程:

prometheus 已經觸發告警:

在等待 1 分鐘,如果持續還是符合告警策略,則狀態爲從 pending 變爲 FIRING 會發送郵件到我的郵箱

此時我的郵箱收到了一條告警消息:

alertmanager 也支持對告警進行靜默,在 alertmanager 的 WEBUI 中配置即可:

間隔了 4 分鐘,沒有收到告警,靜默生效:

一個小時沒有收到告警信息:

8. 參考文獻

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/sQpB0WTs7eBDi4BuWp7gQg