42.2 告警触发trigger模块单点问题和高可用解决方案

本节重点介绍 :

  • prometheus告警trigger单点问题
  • trigger模型简化 海量的job交给有限的work执行
  • 动态分片方案
  • 改造之前的prome-shard代码

trigger单点问题

  • 我们知道prometheus 如果配置了rule就充当trigger角色了

  • prometheus实例可以用来做下列用途

对应的配置段 用途
采集配置段 做采集器,数据保存在本地
采集配置段 + 远程写入段 做采集器+传输器,数据保存在本地+远端存储
远程查询段 做查询器,查询远端存储数据
采集配置段 + 远程查询段 做采集器+查询器,查询本地数据+远端存储数据
采集配置段 + Alertmanager信息段 + 告警配置文件段 做采集器+告警触发器,查询本地数据生成报警发往Alertmanager
远程查询段 + Alertmanager信息段 + 告警配置文件段 做远程告警触发器,查询远端数据生成报警发往Alertmanager
远程查询段+远程写入段 + 预聚合配置文件段 做预聚合指标,生成的结果集指标写入远端存储

trigger模型简化 海量的job交给有限的work执行

  • job就相当于用户配置的rule规则,规则的触发
  • 海量的意思是,规则非常多
  • 那么交给一个work执行就会有单点问题
  • 解决方案就是静态分片和动态分片

静态分片解决方案

  • 通过confd分片
  • 具体方案可以看41.4章节

静态分片方案弊端

  • 还是老问题,某个分片挂了之后没有其它分片接管
  • 损失1/n的job

动态分片方案

需要解决下面的问题

  • 如何解决静态分片中分片挂掉的问题
  • 如何统一采集器配置
  • 如何将采集的target分发给采集器
  • 如何降低分片变化是target的迁移

改造我们之前的prome-shard代码 27.9章

扩展target字段

  • D:\go_path\src\prome-shard\target\target.go
  • 添加alert rule所需要的
  • 并添加Type字段用于判断类型
  • json和yaml不需要的字段 在标签中 设置为 -,避免后面写文件有多余的字段
go 复制代码
package target

import (
	"prome-shard/common"
)

type ScrapeTarget struct {
	Type             string            `json:"-" yaml:"-"`
	Targets          []string          `json:"targets" yaml:"-"`
	Labels           map[string]string `json:"labels" yaml:"-"`
	AlertName        string            `yaml:"alert,omitempty" json:"-"`       //alert name
	Expr             string            `yaml:"expr" json:"-"`                  // alert表达式
	For              string            `yaml:"for,omitempty" json:"-"`         // alert for 时间
	AlertLabels      map[string]string `yaml:"labels,omitempty" json:"-"`      // alert的标签
	AlertAnnotations map[string]string `yaml:"annotations,omitempty" json:"-"` //alert的注释map
}

var (
	AvaiableGetTargetFuncs = map[string]GetTargetFunc{
		common.ScrapePromeJobPrefix + "node_exporter": GetTargetNodeExporter,
		common.ScrapePromeJobPrefix + "get_alert":     GetTargetAlertRule,
	}
)

type GetTargetFunc func() []ScrapeTarget

添加获取alert 的func

  • D:\go_path\src\prome-shard\target\get_alert.go
go 复制代码
package target

import (
	"fmt"
	"math/rand"
	"prome-shard/common"
)

func GetTargetAlertRule() []ScrapeTarget {

	qls := []string{
		`node_cpu_seconds_total >0`,
		`node_memory_Active_bytes !=0`,
		`node_load1 * 100 > 10`,
		`node_disk_writes_completed_total>0`,
	}

	randMapKeys := []string{"arch", "idc", "os", "job"}
	randMapValues := []string{"linux", "beijing", "centos", "arm64"}
	frn := func(n int) int {
		return rand.Intn(n)
	}

	targets := make([]ScrapeTarget, 0)
	for index, ql := range qls {
		num := len(randMapKeys)
		m := make(map[string]string, num)
		for i := 0; i < num; i++ {
			m[randMapKeys[frn(len(randMapKeys)-1)]] = randMapValues[frn(len(randMapValues)-1)]
		}
		t := ScrapeTarget{

			Type:        common.TargetAlert,
			Expr:        ql,
			For:         "10s",
			AlertName:   fmt.Sprintf("test_alert_name_%d", index),
			AlertLabels: m,
		}
		targets = append(targets, t)
	}
	return targets
}

修改Dispath函数,根据类型做判断

  • 如果是alert的就用 name做hash
  • 如果是 scrape就用 ip做hash
  • 配置文件中新添加最后写文件的类型,json或者yaml
go 复制代码
func (this *ShardService) Dispatch() {
	// 执行这个对应的获取target函数
	targets := this.TargetGetFunc()
	if len(targets) == 0 {
		level.Warn(this.logger).Log("msg", "Dispatch.empty.targets")
		return
	}
	// 先初始化一个map ,key是 节点,value是分配给这个节点的targets
	nodeMap := make(map[string][]target.ScrapeTarget)

	// 遍历target,
	for _, t := range targets {

		switch t.Type {
		case common.TargetScrape:
			t := t
			if len(t.Targets) != 1 {
				continue
			}
			// 对target的地址 在哈希环中寻找节点
			// 要求每个target的地址都是1个
			// 然后根据node塞入map中
			node := this.GetNode(t.Targets[0])

			preTs, loaded := nodeMap[node]
			if !loaded {
				preTs = make([]target.ScrapeTarget, 0)

			}
			preTs = append(preTs, t)
			nodeMap[node] = preTs
		case common.TargetAlert:
			t := t

			// 对target的地址 在哈希环中寻找节点
			// 要求每个target的地址都是1个
			// 然后根据node塞入map中
			node := this.GetNode(t.AlertName)

			preTs, loaded := nodeMap[node]
			if !loaded {
				preTs = make([]target.ScrapeTarget, 0)

			}
			preTs = append(preTs, t)
			nodeMap[node] = preTs
		}

	}
	index := 1
	allNum := len(nodeMap)
	for node, ts := range nodeMap {
		// 拼接一个json文件的名字
		// 服务名_节点ip_索引_分片总数_target总数.json
		dstFileName := ""
		// 写json文件
		switch this.FileType {
		case "json":
			dstFileName = fmt.Sprintf("%s_%s_%d_%d_%d.json",
				this.SrvName,
				node,
				index,
				allNum,
				len(ts),

			)

			writeJsonFile(dstFileName, ts)
		case "yaml":
			dstFileName = fmt.Sprintf("%s_%s_%d_%d_%d.yaml",
				this.SrvName,
				node,
				index,
				allNum,
				len(ts),

			)
			writeYamlFile(dstFileName, ts)

		}

		extraVars := make(map[string]interface{})
		extraVars["src_sd_file_name"] = dstFileName
		extraVars["dest_sd_file_name"] = this.DestSdFileName
		extraVars["service_port"] = this.Port
		level.Info(this.logger).Log(
			"msg", "goansiblerun.run",

			"this.SrvName", this.SrvName,
			"jsonFileName", dstFileName,
			"node", node,
			"index", index,
			"all", allNum,
			"targetNum", len(ts),

		)
		go goansiblerun.AnsiRunPlay(this.logger, this.SrvName, node, extraVars, this.YamlPath)
		index++
	}

}

写yaml的函数

go 复制代码
type RuleGroup struct {
	Name  string `yaml:"name"`
	Rules []Rule `yaml:"rules"`
}

// Rule describes an alerting or recording rule.
type Rule struct {
	Record      string            `yaml:"record,omitempty"`
	Alert       string            `yaml:"alert,omitempty"`
	Expr        string            `yaml:"expr"`
	For         string            `yaml:"for,omitempty"`
	Labels      map[string]string `yaml:"labels,omitempty"`
	Annotations map[string]string `yaml:"annotations,omitempty"`
}

func writeYamlFile(fileName string, ts []target.ScrapeTarget) {
	gs := make([]RuleGroup, 0)

	for _, t := range ts {
		rules := make([]Rule, 0)
		r := Rule{
			Alert:       t.AlertName,
			Expr:        t.Expr,
			For:         t.For,
			Labels:      t.AlertLabels,
			Annotations: t.AlertAnnotations,
		}

		rules = append(rules, r)
		g := RuleGroup{
			Name:  t.AlertName,
			Rules: rules,
		}
		gs = append(gs, g)
	}

	bs, _ := yaml.Marshal(gs)

	err := ioutil.WriteFile(fileName, bs, 0644)
	fmt.Println(err)
}
  • yaml文件格式为
yaml 复制代码
groups:
- name: test_alert_name_0
  rules:
  - alert: test_alert_name_0
    expr: node_cpu_seconds_total >0
    for: 10s
    labels:
      arch: centos
      idc: linux
      os: linux
- name: test_alert_name_1
  rules:
  - alert: test_alert_name_1
    expr: node_memory_Active_bytes !=0
    for: 10s
    labels:
      os: beijing
- name: test_alert_name_3
  rules:
  - alert: test_alert_name_3
    expr: node_disk_writes_completed_total>0
    for: 10s
    labels:
      arch: beijing
      os: beijing

修改配置文件,运行

  • 新增 scrape_prometheus_get_alert 这个job
  • 文件路径设置为 rule/
yaml 复制代码
shard_service:
  - name:   scrape_prometheus_node_exporter
    file_type: json
    desc: inf ecs 监控
    nodes:
      - 172.20.70.205
      - 172.20.70.215

    port: 9090
    dest_sd_file_name: file_sd_by_prome_shared.json
    yaml_path: ./copy_file_and_reload_prome.yaml

  - name:   scrape_prometheus_get_alert
    file_type: yaml
    desc: alert rule文件
    nodes:
      - 172.20.70.205
      - 172.20.70.215

    port: 9090
    dest_sd_file_name: ../rule/file_sd_by_prome_shared.yaml
    yaml_path: ./copy_file_and_reload_prome.yaml



http:
  port: 8801
consul_server:
  # consul api 地址
  addr: 172.20.70.205:8500
  username:
  password:

本节重点总结 :

  • prometheus告警trigger单点问题
  • trigger模型简化 海量的job交给有限的work执行
  • 动态分片方案
  • 改造之前的prome-shard代码
相关推荐
程序员谷美几秒前
给你的小秘密加点隐私——Java实现AES加密全攻略
java
袁庭新16 分钟前
Maven仓库如何搭建?保姆级教程
java·maven
Stimd18 分钟前
【重写SpringFramework】第三章小结(chapter 3-16)
java·后端·spring
潜洋27 分钟前
Spring Boot教程之五十二:CrudRepository 和 JpaRepository 之间的区别
java·大数据·数据库·spring boot
KXue070335 分钟前
【Linux 之一 】Linux常用命令汇总
linux·ubuntu·shell
TENET信条43 分钟前
代码随想录 day55 第十一章 图论part05
java·开发语言·图论
xiangzhihong81 小时前
Spring Boot整合Minio实现文件上传
java·spring boot·后端
diaya1 小时前
clickhouse 离线包安装(ubuntu)
linux·服务器·网络
涔溪1 小时前
如何在 IDEA 中配置 npm ?
java·npm·intellij-idea
V+zmm101341 小时前
基于微信小程序疫苗预约系统ssm+论文源码调试讲解
java·微信小程序·小程序·毕业设计·ssm