成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

Prometheus告警規則管理

存儲 存儲軟件
Prometheus支持用戶自定義Rule規則。Rule分為兩類,一類是Recording Rule,另一類是Alerting Rule。Recording Rule的主要目的是通過PromQL可以實時對Prometheus中采集到的樣本數據進行查詢,聚合以及其它各種運算操作。

[[419847]]

什么是Rule

Prometheus支持用戶自定義Rule規則。Rule分為兩類,一類是Recording Rule,另一類是Alerting Rule。Recording Rule的主要目的是通過PromQL可以實時對Prometheus中采集到的樣本數據進行查詢,聚合以及其它各種運算操作。而在某些PromQL較為復雜且計算量較大時,直接使用PromQL可能會導致Prometheus響應超時的情況。這時需要一種能夠類似于后臺批處理的機制能夠在后臺完成這些復雜運算的計算,對于使用者而言只需要查詢這些運算結果即可。Prometheus通過Recoding Rule規則支持這種后臺計算的方式,可以實現對復雜查詢的性能優化,提高查詢效率。

今天主要帶來告警規則的分析。Prometheus中的告警規則允許你基于PromQL表達式定義告警觸發條件,Prometheus后端對這些觸發規則進行周期性計算,當滿足觸發條件后則會觸發告警通知。

什么是告警Rule

告警是prometheus的一個重要功能,接下來從源碼的角度來分析下告警的執行流程。

怎么定義告警Rule

一條典型的告警規則如下所示:

  1. groups: 
  2. name: example 
  3.   rules: 
  4.   - alert: HighErrorRate 
  5.     #指標需要在觸發告警之前的10分鐘內大于0.5。 
  6.     expr: job:request_latency_seconds:mean5m{job="myjob"} > 0.5 
  7.     for: 10m 
  8.     labels: 
  9.       severity: page 
  10.     annotations: 
  11.       summary: High request latency 
  12.       description: description info 

在告警規則文件中,我們可以將一組相關的規則設置定義在一個group下。在每一個group中我們可以定義多個告警規則(rule)。一條告警規則主要由以下幾部分組成:

  • alert:告警規則的名稱。
  • expr:基于PromQL表達式告警觸發條件,用于計算是否有時間序列滿足該條件。
  • for:評估等待時間,可選參數。用于表示只有當觸發條件持續一段時間后才發送告警。在等待期間新產生告警的狀態為pending。
  • labels:自定義標簽,允許用戶指定要附加到告警上的一組附加標簽。
  • annotations:用于指定一組附加信息,比如用于描述告警詳細信息的文字等,annotations的內容在告警產生時會一同作為參數發送到Alertmanager。

Rule管理器

規則管理器會根據配置的規則,基于規則PromQL表達式告警的觸發條件,用于計算是否有時間序列滿足該條件。在滿足該條件時,將告警信息發送給告警服務。

  1. type Manager struct { 
  2.  opts     *ManagerOptions //外部的依賴 
  3.  groups   map[string]*Group //當前的規則組 
  4.  mtx      sync.RWMutex //規則管理器讀寫鎖 
  5.  block    chan struct{}  
  6.  done     chan struct{}  
  7.  restored bool  
  8.  
  9.  logger log.Logger  
  • opts(*ManagerOptions類型):記錄了Manager實例使用到的其他模塊,例如storage模塊、notify模塊等。
  • groups(map[string]*Group類型):記錄了所有的rules.Group實例,其中key由rules.Group的名稱及其所在的配置文件構成。
  • mtx(sync.RWMutex類型):在讀寫groups字段時都需要獲取該鎖進行同步。

讀取Rule組配置

在Prometheus Server啟動的過程中,首先會調用Manager.Update()方法加載Rule配置文件并進行解析,其大致流程如下。

  • 調用Manager.LoadGroups()方法加載并解析Rule配置文件,最終得到rules.Group實例集合。
  • 停止原有的rules.Group實例,啟動新的rules.Group實例。其中會為每個rules.Group實例啟動一個goroutine,它會關聯rules.Group實例下的全部PromQL查詢。
  1. func (m *Manager) Update(interval time.Duration, files []string, externalLabels labels.Labels, externalURL string) error { 
  2.  m.mtx.Lock() 
  3.  defer m.mtx.Unlock() 
  4.     // 從當前文件中加載規則 
  5.  groups, errs := m.LoadGroups(interval, externalLabels, externalURL, files...) 
  6.  if errs != nil { 
  7.   for _, e := range errs { 
  8.    level.Error(m.logger).Log("msg""loading groups failed""err", e) 
  9.   } 
  10.   return errors.New("error loading rules, previous rule set restored"
  11.  } 
  12.  m.restored = true 
  13.  
  14.  var wg sync.WaitGroup 
  15.    //循環遍歷規則組 
  16.  for _, newg := range groups { 
  17.   // If there is an old group with the same identifier, 
  18.   // check if new group equals with the old group, if yes then skip it. 
  19.   // If not equals, stop it and wait for it to finish the current iteration. 
  20.   // Then copy it into the new group
  21.   //根據新的rules.Group的信息獲取規則組名 
  22.   gn := GroupKey(newg.file, newg.name
  23.    //根據規則組名獲取到老的規則組并刪除原有的rules.Group實例 
  24.   oldg, ok := m.groups[gn] 
  25.   delete(m.groups, gn) 
  26.  
  27.   if ok && oldg.Equals(newg) { 
  28.    groups[gn] = oldg 
  29.    continue 
  30.   } 
  31.  
  32.   wg.Add(1) 
  33.     //為每一個rules.Group實例啟動一個goroutine 
  34.   go func(newg *Group) { 
  35.    if ok { 
  36.     oldg.stop() 
  37.      //將老的規則組中的狀態信息復制到新的規則組 
  38.     newg.CopyState(oldg) 
  39.    } 
  40.    wg.Done() 
  41.    // Wait with starting evaluation until the rule manager 
  42.    // is told to run. This is necessary to avoid running 
  43.    // queries against a bootstrapping storage. 
  44.    <-m.block 
  45.      //調用rules.Group.run()方法,開始周期性的執行PromQl語句 
  46.    newg.run(m.opts.Context) 
  47.   }(newg) 
  48.  } 
  49.  
  50.  // Stop remaining old groups. 
  51.  //停止所有老規則組的服務 
  52.  wg.Add(len(m.groups)) 
  53.  for n, oldg := range m.groups { 
  54.   go func(n string, g *Group) { 
  55.    g.markStale = true 
  56.    g.stop() 
  57.    if m := g.metrics; m != nil { 
  58.     m.IterationsMissed.DeleteLabelValues(n) 
  59.     m.IterationsScheduled.DeleteLabelValues(n) 
  60.     m.EvalTotal.DeleteLabelValues(n) 
  61.     m.EvalFailures.DeleteLabelValues(n) 
  62.     m.GroupInterval.DeleteLabelValues(n) 
  63.     m.GroupLastEvalTime.DeleteLabelValues(n) 
  64.     m.GroupLastDuration.DeleteLabelValues(n) 
  65.     m.GroupRules.DeleteLabelValues(n) 
  66.     m.GroupSamples.DeleteLabelValues((n)) 
  67.    } 
  68.    wg.Done() 
  69.   }(n, oldg) 
  70.  } 
  71.  
  72.  wg.Wait() 
  73.     //更新規則管理器中的規則組 
  74.  m.groups = groups  
  75.  
  76.  return nil 

運行Rule組調度方法

規則組啟動流程(Group.run):進入Group.run方法后先進行初始化等待,以使規則的運算時間在同一時刻,周期為g.interval;然后定義規則運算調度方法:iter,調度周期為g.interval;在iter方法中調用g.Eval方法執行下一層次的規則運算調度。

規則運算的調度周期g.interval,由prometheus.yml配置文件中global中的 [ evaluation_interval:| default = 1m ]指定。實現如下:

  1. func (g *Group) run(ctx context.Context) { 
  2.  defer close(g.terminated) 
  3.  
  4.  // Wait an initial amount to have consistently slotted intervals. 
  5.  evalTimestamp := g.EvalTimestamp(time.Now().UnixNano()).Add(g.interval) 
  6.  select { 
  7.  case <-time.After(time.Until(evalTimestamp))://初始化等待 
  8.  case <-g.done: 
  9.   return 
  10.  } 
  11.  
  12.  ctx = promql.NewOriginContext(ctx, map[string]interface{}{ 
  13.   "ruleGroup": map[string]string{ 
  14.    "file": g.File(), 
  15.    "name": g.Name(), 
  16.   }, 
  17.  }) 
  18.     //定義規則組規則運算調度算法 
  19.  iter := func() { 
  20.   g.metrics.IterationsScheduled.WithLabelValues(GroupKey(g.file, g.name)).Inc() 
  21.  
  22.   start := time.Now() 
  23.     //規則運算的入口 
  24.   g.Eval(ctx, evalTimestamp) 
  25.   timeSinceStart := time.Since(start) 
  26.  
  27.   g.metrics.IterationDuration.Observe(timeSinceStart.Seconds()) 
  28.   g.setEvaluationTime(timeSinceStart) 
  29.   g.setLastEvaluation(start) 
  30.  } 
  31.  
  32.  // The assumption here is that since the ticker was started after having 
  33.  // waited for `evalTimestamp` to pass, the ticks will trigger soon 
  34.  // after each `evalTimestamp + N * g.interval` occurrence. 
  35.  tick := time.NewTicker(g.interval) //設置規則運算定時器 
  36.  defer tick.Stop() 
  37.  
  38.  defer func() { 
  39.   if !g.markStale { 
  40.    return 
  41.   } 
  42.   go func(now time.Time) { 
  43.    for _, rule := range g.seriesInPreviousEval { 
  44.     for _, r := range rule { 
  45.      g.staleSeries = append(g.staleSeries, r) 
  46.     } 
  47.    } 
  48.    // That can be garbage collected at this point. 
  49.    g.seriesInPreviousEval = nil 
  50.    // Wait for 2 intervals to give the opportunity to renamed rules 
  51.    // to insert new series in the tsdb. At this point if there is a 
  52.    // renamed rule, it should already be started. 
  53.    select { 
  54.    case <-g.managerDone: 
  55.    case <-time.After(2 * g.interval): 
  56.     g.cleanupStaleSeries(ctx, now) 
  57.    } 
  58.   }(time.Now()) 
  59.  }() 
  60.     //調用規則組規則運算的調度方法 
  61.  iter() 
  62.  if g.shouldRestore { 
  63.   // If we have to restore, we wait for another Eval to finish. 
  64.   // The reason behind this is, during first eval (or before it) 
  65.   // we might not have enough data scraped, and recording rules would not 
  66.   // have updated the latest valueson which some alerts might depend. 
  67.   select { 
  68.   case <-g.done: 
  69.    return 
  70.   case <-tick.C: 
  71.    missed := (time.Since(evalTimestamp) / g.interval) - 1 
  72.    if missed > 0 { 
  73.     g.metrics.IterationsMissed.WithLabelValues(GroupKey(g.file, g.name)).Add(float64(missed)) 
  74.     g.metrics.IterationsScheduled.WithLabelValues(GroupKey(g.file, g.name)).Add(float64(missed)) 
  75.    } 
  76.    evalTimestamp = evalTimestamp.Add((missed + 1) * g.interval) 
  77.    iter() 
  78.   } 
  79.  
  80.   g.RestoreForState(time.Now()) 
  81.   g.shouldRestore = false 
  82.  } 
  83.  
  84.  for { 
  85.   select { 
  86.   case <-g.done: 
  87.    return 
  88.   default
  89.    select { 
  90.    case <-g.done: 
  91.     return 
  92.    case <-tick.C: 
  93.     missed := (time.Since(evalTimestamp) / g.interval) - 1 
  94.     if missed > 0 { 
  95.      g.metrics.IterationsMissed.WithLabelValues(GroupKey(g.file, g.name)).Add(float64(missed)) 
  96.      g.metrics.IterationsScheduled.WithLabelValues(GroupKey(g.file, g.name)).Add(float64(missed)) 
  97.     } 
  98.     evalTimestamp = evalTimestamp.Add((missed + 1) * g.interval) 
  99.      //調用規則組規則運算的調度方法 
  100.     iter() 
  101.    } 
  102.   } 
  103.  } 

運行Rule調度方法

規則組對具體規則的調度在Group.Eval中實現,在Group.Eval方法中會將規則組下的每條規則通過QueryFunc將(promQL)放到查詢引擎(queryEngine)中執行,如果被執行的是AlertingRule類型,那么執行結果指標會被NotifyFunc組件發送給告警服務;如果是RecordingRule類型,最后將改結果指標存儲到Prometheus的儲存管理器中,并對過期指標進行存儲標記處理。

  1. // Eval runs a single evaluation cycle in which all rules are evaluated sequentially. 
  2. func (g *Group) Eval(ctx context.Context, ts time.Time) { 
  3.  var samplesTotal float64 
  4.     遍歷當前規則組下的所有規則 
  5.  for i, rule := range g.rules { 
  6.   select { 
  7.   case <-g.done: 
  8.    return 
  9.   default
  10.   } 
  11.  
  12.   func(i intrule Rule) { 
  13.    sp, ctx := opentracing.StartSpanFromContext(ctx, "rule"
  14.    sp.SetTag("name"rule.Name()) 
  15.    defer func(t time.Time) { 
  16.     sp.Finish() 
  17.       //更新服務指標-規則的執行時間 
  18.     since := time.Since(t) 
  19.     g.metrics.EvalDuration.Observe(since.Seconds()) 
  20.     rule.SetEvaluationDuration(since) 
  21.       //記錄本次規則執行的耗時 
  22.     rule.SetEvaluationTimestamp(t) 
  23.    }(time.Now()) 
  24.      //記錄規則運算的次數 
  25.    g.metrics.EvalTotal.WithLabelValues(GroupKey(g.File(), g.Name())).Inc() 
  26.      //運算規則 
  27.    vector, err := rule.Eval(ctx, ts, g.opts.QueryFunc, g.opts.ExternalURL) 
  28.    if err != nil { 
  29.       //規則出現錯誤后,終止查詢 
  30.     rule.SetHealth(HealthBad) 
  31.     rule.SetLastError(err) 
  32.      //記錄查詢失敗的次數 
  33.     g.metrics.EvalFailures.WithLabelValues(GroupKey(g.File(), g.Name())).Inc() 
  34.  
  35.     // Canceled queries are intentional termination of queries. This normally 
  36.     // happens on shutdown and thus we skip logging of any errors here. 
  37.     if _, ok := err.(promql.ErrQueryCanceled); !ok { 
  38.      level.Warn(g.logger).Log("msg""Evaluating rule failed""rule"rule"err", err) 
  39.     } 
  40.     return 
  41.    } 
  42.    samplesTotal += float64(len(vector)) 
  43.             //判斷是否是告警類型規則 
  44.    if ar, ok := rule.(*AlertingRule); ok { 
  45.                 發送告警 
  46.     ar.sendAlerts(ctx, ts, g.opts.ResendDelay, g.interval, g.opts.NotifyFunc) 
  47.    } 
  48.    var ( 
  49.     numOutOfOrder = 0 
  50.     numDuplicates = 0 
  51.    ) 
  52.     //此處為Recording獲取存儲器指標 
  53.    app := g.opts.Appendable.Appender(ctx) 
  54.    seriesReturned := make(map[string]labels.Labels, len(g.seriesInPreviousEval[i])) 
  55.    defer func() { 
  56.     if err := app.Commit(); err != nil { 
  57.      rule.SetHealth(HealthBad) 
  58.      rule.SetLastError(err) 
  59.      g.metrics.EvalFailures.WithLabelValues(GroupKey(g.File(), g.Name())).Inc() 
  60.  
  61.      level.Warn(g.logger).Log("msg""Rule sample appending failed""err", err) 
  62.      return 
  63.     } 
  64.     g.seriesInPreviousEval[i] = seriesReturned 
  65.    }() 
  66.  
  67.    for _, s := range vector { 
  68.     if _, err := app.Append(0, s.Metric, s.T, s.V); err != nil { 
  69.      rule.SetHealth(HealthBad) 
  70.      rule.SetLastError(err) 
  71.  
  72.      switch errors.Cause(err) { 
  73.                         儲存指標返回的各種錯誤碼處理 
  74.      case storage.ErrOutOfOrderSample: 
  75.       numOutOfOrder++ 
  76.       level.Debug(g.logger).Log("msg""Rule evaluation result discarded""err", err, "sample", s) 
  77.      case storage.ErrDuplicateSampleForTimestamp: 
  78.       numDuplicates++ 
  79.       level.Debug(g.logger).Log("msg""Rule evaluation result discarded""err", err, "sample", s) 
  80.      default
  81.       level.Warn(g.logger).Log("msg""Rule evaluation result discarded""err", err, "sample", s) 
  82.      } 
  83.     } else { 
  84.       //緩存規則運算后的結果指標 
  85.      seriesReturned[s.Metric.String()] = s.Metric 
  86.     } 
  87.    } 
  88.    if numOutOfOrder > 0 { 
  89.     level.Warn(g.logger).Log("msg""Error on ingesting out-of-order result from rule evaluation""numDropped", numOutOfOrder) 
  90.    } 
  91.    if numDuplicates > 0 { 
  92.     level.Warn(g.logger).Log("msg""Error on ingesting results from rule evaluation with different value but same timestamp""numDropped", numDuplicates) 
  93.    } 
  94.  
  95.    for metric, lset := range g.seriesInPreviousEval[i] { 
  96.     if _, ok := seriesReturned[metric]; !ok { 
  97.       //設置過期指標的指標值 
  98.      // Series no longer exposed, mark it stale. 
  99.      _, err = app.Append(0, lset, timestamp.FromTime(ts), math.Float64frombits(value.StaleNaN)) 
  100.      switch errors.Cause(err) { 
  101.      case nil: 
  102.      case storage.ErrOutOfOrderSample, storage.ErrDuplicateSampleForTimestamp: 
  103.       // Do not count these in logging, as this is expected if series 
  104.       // is exposed from a different rule
  105.      default
  106.       level.Warn(g.logger).Log("msg""Adding stale sample failed""sample", metric, "err", err) 
  107.      } 
  108.     } 
  109.    } 
  110.   }(i, rule
  111.  } 
  112.  if g.metrics != nil { 
  113.   g.metrics.GroupSamples.WithLabelValues(GroupKey(g.File(), g.Name())).Set(samplesTotal) 
  114.  } 
  115.  g.cleanupStaleSeries(ctx, ts) 

然后就是規則的具體執行了,我們這里先只看AlertingRule的流程。首先看下AlertingRule的結構:

  1. // An AlertingRule generates alerts from its vector expression. 
  2. type AlertingRule struct { 
  3.     // The name of the alert. 
  4.     name string 
  5.     // The vector expression from which to generate alerts. 
  6.     vector parser.Expr 
  7.     // The duration for which a labelset needs to persist in the expression 
  8.     // output vector before an alert transitions from Pending to Firing state. 
  9.     holdDuration time.Duration 
  10.     // Extra labels to attach to the resulting alert sample vectors. 
  11.     labels labels.Labels 
  12.     // Non-identifying key/value pairs. 
  13.     annotations labels.Labels 
  14.     // External labels from the global config. 
  15.     externalLabels map[string]string 
  16.     // true if old state has been restored. We start persisting samples for ALERT_FOR_STATE 
  17.     // only after the restoration. 
  18.     restored bool 
  19.     // Protects the below. 
  20.     mtx sync.Mutex 
  21.     // Time in seconds taken to evaluate rule
  22.     evaluationDuration time.Duration 
  23.     // Timestamp of last evaluation of rule
  24.     evaluationTimestamp time.Time 
  25.     // The health of the alerting rule
  26.     health RuleHealth 
  27.     // The last error seen by the alerting rule
  28.     lastError error 
  29.     // A map of alerts which are currently active (Pending or Firing), keyed by 
  30.     // the fingerprint of the labelset they correspond to
  31.     active map[uint64]*Alert 
  32.     logger log.Logger 

這里比較重要的就是active字段了,它保存了執行規則后需要進行告警的資源,具體是否告警還要執行一系列的邏輯來判斷是否滿足告警條件。具體執行的邏輯如下:

  1. func (r *AlertingRule) Eval(ctx context.Context, ts time.Time, query QueryFunc, externalURL *url.URL) (promql.Vector, error) { 
  2.     res, err := query(ctx, r.vector.String(), ts) 
  3.     if err != nil { 
  4.         r.SetHealth(HealthBad) 
  5.         r.SetLastError(err) 
  6.         return nil, err 
  7.     } 
  8.     // ...... 

這一步通過創建Manager時傳入的QueryFunc函數執行規則配置中的expr表達式,然后得到返回的結果,這里的結果是滿足表達式的指標的集合。比如配置的規則為:

  1. cpu_usage > 90 

那么查出來的結果可能是

  1. cpu_usage{instance="192.168.0.11"} 91 
  2. cpu_usage{instance="192.168.0.12"} 92 

然后遍歷查詢到的結果,根據指標的標簽生成一個hash值,然后判斷這個hash值是否之前已經存在(即之前是否已經有相同的指標數據返回),如果是,則更新上次的value及annotations,如果不是,則創建一個新的alert并保存至該規則下的active alert列表中。然后遍歷規則的active alert列表,根據規則的持續時長配置、alert的上次觸發時間、alert的當前狀態、本次查詢alert是否依然存在等信息來修改alert的狀態。具體規則如下:

如果alert之前存在,但本次執行時不存在

  • 狀態是StatePending或者本次檢查時間距離上次觸發時間超過15分鐘(15分鐘為寫死的常量),則將該alert從active列表中刪除
  • 狀態不為StateInactive的alert修改為StateInactive

如果alert之前存在并且本次執行仍然存在

  • alert的狀態是StatePending并且本次檢查距離上次觸發時間超過配置的for持續時長,那么狀態修改為StateFiring

其余情況修改alert的狀態為StatePending

上面那一步只是修改了alert的狀態,但是并沒有真正執行發送告警操作。下面才是真正要執行告警操作:

  1. // 判斷規則是否是alert規則,如果是則發送告警信息(具體是否真正發送由ar.sendAlerts中的邏輯判斷) 
  2. if ar, ok := rule.(*AlertingRule); ok { 
  3.     ar.sendAlerts(ctx, ts, g.opts.ResendDelay, g.interval, g.opts.NotifyFunc) 
  4. // ....... 
  5. func (r *AlertingRule) sendAlerts(ctx context.Context, ts time.Time, resendDelay time.Duration, interval time.Duration, notifyFunc NotifyFunc) { 
  6.     alerts := []*Alert{} 
  7.     r.ForEachActiveAlert(func(alert *Alert) { 
  8.         if alert.needsSending(ts, resendDelay) { 
  9.             alert.LastSentAt = ts 
  10.             // Allow for two Eval or Alertmanager send failures. 
  11.             delta := resendDelay 
  12.             if interval > resendDelay { 
  13.                 delta = interval 
  14.             } 
  15.             alert.ValidUntil = ts.Add(4 * delta) 
  16.             anew := *alert 
  17.             alerts = append(alerts, &anew) 
  18.         } 
  19.     }) 
  20.     notifyFunc(ctx, r.vector.String(), alerts...) 
  21. func (a *Alert) needsSending(ts time.Time, resendDelay time.Duration) bool { 
  22.     if a.State == StatePending { 
  23.         return false 
  24.     } 
  25.     // if an alert has been resolved since the last send, resend it 
  26.     if a.ResolvedAt.After(a.LastSentAt) { 
  27.         return true 
  28.     } 
  29.     return a.LastSentAt.Add(resendDelay).Before(ts) 

概括一下以上邏輯就是:

  1. 如果alert的狀態是StatePending,則不發送告警
  2. 如果alert的已經被解決,那么再次發送告警標記該條信息已經被解決
  3. 如果當前時間距離上次發送告警的時間大于配置的重新發送延時時間(ResendDelay),則發送告警,否則不發送

以上就是prometheus的告警流程。學習這個流程主要是問了能夠對prometheus的rules相關的做二次開發。我們可以修改LoadGroups()方法,讓其可以動態側加載定義在mysql中定義的規則,動態實現告警規則更新。

參考: 

《深入淺出prometheus原理、應用、源碼與拓展詳解》

 

責任編輯:武曉燕 來源: 運維開發故事
相關推薦

2023-03-26 08:41:37

2024-07-31 08:02:26

Prometheus服務器代碼

2021-03-31 08:02:34

Prometheus 監控運維

2021-02-18 15:36:13

PrometheusAlertmanageGrafana

2023-09-12 07:11:33

Prometheus聚合告警GPT

2025-04-09 08:05:00

運維告警Prometheus

2025-01-17 09:54:54

2022-07-29 21:23:54

Grafana微服務

2014-06-16 11:17:12

入侵檢測OSSEC日志分析

2023-04-20 07:12:33

夜鶯監控夜鶯

2022-08-30 13:03:39

prometheusAlert

2023-11-24 16:57:53

2022-09-04 17:53:20

Prometheus開源

2020-12-30 05:34:25

監控PrometheusGrafana

2021-08-26 11:30:54

AlertManage阿里云

2025-03-05 07:00:00

Grafana可視化Kubernetes

2023-11-13 08:15:36

2025-01-21 11:18:46

2011-07-18 17:14:16

Objective-C 內存 Cocoa

2010-04-20 13:59:30

Oracle管理規則
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 欧美精品中文字幕久久二区 | 欧美精品一二三区 | 日韩中文字幕在线观看 | 久草成人 | 国产精品久久久久久久久免费软件 | 不卡av在线 | 日日干日日操 | 欧美精品中文字幕久久二区 | 国产色爽 | 日韩精品在线看 | 色视频www在线播放国产人成 | www.97zyz.com| 久久久精品一区二区三区 | 亚洲精品电影网在线观看 | 中文字幕在线视频观看 | 一区二区三区在线播放视频 | 亚洲成人一区二区三区 | 国产色片在线 | 国产传媒在线播放 | 亚洲免费在线 | 日韩视频在线一区 | 一区二区三区四区免费观看 | 一区二区三区免费 | 国产精品视频网站 | 色啪网| 亚洲字幕在线观看 | 久久国产一区 | 国产激情99| 伊人伊人伊人 | 国产精品久久午夜夜伦鲁鲁 | 特黄毛片 | 欧美不卡一区二区三区 | 欧美自拍网站 | 中文字幕国产视频 | 国产欧美在线一区 | 日日夜夜天天综合 | 色婷婷一区二区三区四区 | 精品在线一区二区 | 亚洲精品久久久久久久久久吃药 | 中文字幕精品视频 | 蜜桃官网|