不用AI,如何使用Golang工程化实现多源告警去重、告警收敛引擎的实现

287 阅读4分钟

如何对多个告警源的告警内容,进行统一收集、处理

目标:

   设计一套通用框架,实现多源告警内容的 告警处理,包括优先级、白名单、复杂表达式处理,以及去重、聚合等

规则库

   构建规则库,用于处理告警、对告警执行规则过滤

定义数据模型

   Alert:告警内容

   Condition:条件接口 ;主要方法:Evaluate;评估告警

   Rule:定义告警优先级、标签、互斥标签、条件『组合』、动作、白名单、链式调用、表达式

package service

type Alert struct {
	Source   string
	Type     string
	Severity string
	CPUUsage float64
	MemUsage float64
	Message  string
}

type Condition interface {
	`Evaluate`(alert Alert) bool
}

type Rule struct {
	Priority    int
	Label       string
	MutexLabels []string
	Conditions  []Condition
	Action      func(Alert)
	WhiteList   []string
	Next        *Rule
	Expression  string
}

实现告警内容 Evaluate 接口

以CPU使用率过高 HighCPUUsageCondition 为例,实现 Evaluate 接口

用于实现评估逻辑:

  • 比如 告警是否统一关联的service name
  • 包括去重计数器处理、聚合逻辑执行 等

以及用于判断是否继续往下一规则处理

「扩展」

  • 这里可以引入自己实现的去重算法实现,最常见的参考alertmanager基于时间hash的去重算法
  • 多重策略算法时,可以使用策略模式设计模式
package service

import (
	"context"
	"fmt"
	"sort"
	"github.com/PaesslerAG/gval"
)


func handleHighCPUUsage(alert Alert) {
	// 处理高 CPU 使用率告警
}

func handleHighMemoryUsage(alert Alert) {
	// 处理高内存使用率告警
}

type HighCPUUsageCondition struct {
	Threshold float64
}

func (c HighCPUUsageCondition) Evaluate(alert Alert) bool {
	return alert.CPUUsage > c.Threshold
}


type DatabaseDownCondition struct{}

func (c DatabaseDownCondition) `Evaluate`(alert Alert) bool {
	return alert.Type == "Database" && alert.Message == "Down"
}

type HighMemoryUsageCondition struct {
	Threshold float64
}

func (c HighMemoryUsageCondition) Evaluate(alert Alert) bool {
	return alert.MemUsage > c.Threshold
}


实现告警 Action 回调函数 func(Alert)

用于执行 告警处理后的后续动作,比如发送告警、静默等

func handleHighResourceUsage(alert Alert) {
	// 处理资源使用高的情况
}

func handleDatabaseDown(alert Alert) {
	// 处理数据库宕机情况
}

告警内容表达式处理

使用规则 处理 告警内容

引入表达式,场景:

   对告警指标值,做逻辑判断,比如 多个告警指标间逻辑判断,包括跨告警内容间

「扩展」

  • 这里的规则表达式,很容易和前端整合,实现复杂告警规则处理
package service

import (
	"context"
	"fmt"
	"sort"

	"github.com/PaesslerAG/gval"
)

func evaluateRule(rule Rule, alert Alert) (bool, error) {
	evaluation, err := gval.Full().NewEvaluable(rule.Expression)
	if err != nil {
		return false, err
	}

	result, err := evaluation(context.Background(), map[string]interface{}{
		"Type":     alert.Type,
		"CPUUsage": alert.CPUUsage,
		"MemUsage": alert.MemUsage,
		// 其他字段...
	})

	if err != nil {
		return false, err
	}

	matched, ok := result.(bool)
	if !ok {
		return false, fmt.Errorf("expression does not evaluate to a boolean")
	}

	return matched, nil
}


多规则间处理

比如:处理规则间 优先级排序

将规则 构建为 规则链

DefineRules:演示了 如何对规则 进行排序、以及构建为 规则链

  • 以及如何构建 规则,包括 定义优先级、条件组合、动作、白名单、标签、互斥标签等

DefineRulesV2 与 DefineRules区别是:

  • 引入 表达式,支持复杂表达式间逻辑处理

func DefineRules() *Rule {

	rule1 := &Rule{
		Priority: 1,
		Conditions: []Condition{
			HighCPUUsageCondition{Threshold: 80},
			HighMemoryUsageCondition{Threshold: 75},
		},
		Action:    handleHighResourceUsage,
		WhiteList: []string{"MonitoringSystem"},

		Label:       "HighResourceUsage",
		MutexLabels: []string{"DatabaseDown"},
	}

	rule2 := &Rule{
		Priority: 2,
		Conditions: []Condition{
			DatabaseDownCondition{},
		},
		Action:    handleDatabaseDown,
		WhiteList: []string{},

		Label:       "DatabaseDown",
		MutexLabels: []string{"HighResourceUsage"},
	}

	// 根据优先级排序规则
	sortedRules := sortRulesByPriority([]*Rule{rule1, rule2})
	buildRuleChain(sortedRules)

	return sortedRules[0]
}


func sortRulesByPriority(rules []*Rule) []*Rule {
	// 实现优先级排序逻辑
	sort.Slice(rules, func(i, j int) bool {
		return rules[i].Priority < rules[j].Priority
	})
	return rules
}

func buildRuleChain(sortedRules []*Rule) {
	// 将规则链接成链

	for i := 0; i < len(sortedRules)-1; i++ {
		sortedRules[i].Next = sortedRules[i+1]
	}
}

func DefineRulesV2() []Rule {
	return []Rule{
		{
			Expression: "Type == 'CPU' && CPUUsage > 80",
			Action:     handleHighCPUUsage,
		},
		{
			Expression: "Type == 'Memory' && MemUsage > 75",
			Action:     handleHighMemoryUsage,
		},
		// 更多规则...
	}
}

规则处理引擎

ProcessAlert 引擎 主要 方法,

  • 对引擎规则进行 链式调用,持续处理引擎的规则,依次匹配命中

evaluateConditions 会对告警进行判断

  • 若规则已经处理过告警,是否允许继续被其他规则处理

ProcessAlert 主函数

  • 将告警内容扔至 evaluateRule ,依次被规则链 处理
package service

type Engine struct {
	FirstRule *Rule
}

func NewEngine(firstRule *Rule) *Engine {
	return &Engine{FirstRule: firstRule}
}

func isInWhiteList(alert Alert, whiteList []string) bool {
	// 实现白名单检查逻辑

	return true
}

func evaluateConditions(alert Alert, conditions []Condition) bool {
	// 实现条件评估逻辑

	return true
}

func (e *Engine) ProcessAlert(alert Alert) {

	triggeredLabels := make(map[string]bool)

	for r := e.FirstRule; r != nil; r = r.Next {
		// if isInWhiteList(alert, r.WhiteList) {
		//     r.Action(alert)
		//     return
		// }

		// if evaluateConditions(alert, r.Conditions) {
		//     r.Action(alert)
		//     return
		// }

		// 检查白名单
		if isInWhiteList(alert, r.WhiteList) {
			r.Action(alert)
			triggeredLabels[r.Label] = true
			continue
		}

		// 检查互斥
		if isMutex(triggeredLabels, r.MutexLabels) {
			continue
		}

		// 检查条件
		if evaluateConditions(alert, r.Conditions) {
			/*
				告警在rule1处理过,不允许在rule2再次处理
			*/

			// r.Action(alert)
			// triggeredLabels[r.Label] = true

			/*
				告警在rule1处理过,允许在rule2再次处理
			*/
			r.Action(alert)
			// 不再跳出循环,允许进一步检查其他规则
		}
	}

	for r := e.FirstRule; r != nil; r = r.Next {
		allConditionsMet := true
		for _, condition := range r.Conditions {
			if !condition.Evaluate(alert) {
				allConditionsMet = false
				break
			}
		}

		if allConditionsMet {
			r.Action(alert)
			break
		}
	}
}

// 互斥检查
func isMutex(triggeredLabels map[string]bool, mutexLabels []string) bool {
	for _, label := range mutexLabels {
		if triggeredLabels[label] {
			return true
		}
	}
	return false
}

func ProcessAlert(alert Alert, rules []Rule) {
	for _, rule := range rules {
		matched, err := evaluateRule(rule, alert)
		if err != nil {
			// 处理错误...
			continue
		}
		if matched {
			rule.Action(alert)
		}
	}
}

调用处理引擎

「扩展」

  • 实际处理中,可以在引擎上封装,构建超级引擎

func TestProcessAlert(t *testing.T){
    firstRule := service.DefineRules()
    engine := service.NewEngine(firstRule)

    alert := service.Alert{
        Source:   "System",
        Type:     "CPU",
        Severity: "High",
        CPUUsage: 85.0,
        MemUsage: 80.0,
    }

    engine.ProcessAlert(alert)
}


func TestProcessAlertV2(t *testing.T) {
    rules := service.DefineRulesV2()

    alert := service.Alert{
        Type:     "CPU",
        CPUUsage: 85.0,
    }

    service.ProcessAlert(alert, rules)
}