文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

深入理解Prometheus:在告警时附带指标趋势图

2024-12-01 15:33

关注

Prometheus是一个开源系统监控和告警工具包,于 2016 年加入云原生计算基金会,成为继Kubernetes之后的第二个托管项目。

本篇文章将结合告警信息来一起了解下如何实现在告警时附带指标趋势图,以便能更好的定位告警原因和发生时间。

在告警时附带指标趋势图的难点

在Prometheus中提供了三种查看指标出图的方式,分别是

我们通常会更推荐使用Grafana,拥有EXPRESSION BROWSER的所有能力,同时还支持令人映像深刻的出图效果和友好的使用体验。

node内存使用指标趋势图

Prometheus支持基于PromQL的告警规则,通常如下所示

groups:
- name: example
rules:
- alert: NodeHighMemoryUsed
expr: node_memory_used_percent{job="myjob"} > 0.8
for: 10m
labels:
severity: warn
annotations:
summary: node high memory used

其中最重要的就是expr字段,它的值是一个表达式PromQL表达式,如果表达式的结果为true,也即能过滤到数据,那么就会对每个时序metric生成一条告警,默认的告警信息中通常会包含labels和annotations内地信息,这些信息通常是预置的文本和文本模版(annotations内支持将labels作为变量的模版,最后告警时会渲染为实际的值,最后也是文本)

基于告警文本的消息只能获得告警当时的状态信息,告警只是第一步,如果要定位告警原因,通常需要告警发生前的历史累积状态信息,那就需要针对每个具体告警,生成对应的专有指标趋势图。

默认的告警表达式,如上图所示,会展示所有符合条件的数据,但有两个问题:

  1. 每个告警其实只需要自己的趋势图,不需要展示别的对象的趋势图。
  2. 希望展示完整的趋势图,更便于发现趋势问题。

如下图所示,只针对某个告警对象,且既有告警趋势,也有基线,这样的图更加方便定位。

通常我们的规则都是形式如下:

"node_cpu_usage > 0"
"rate(node_cpu_total{node=\"n1\"}[1m]) > rate(node_cpu_total{node=\"n2\"}[1m])"
"container_cpu_limit_usage / avg_over_time(container_cpu_limit_usage[1d] offset 1d) > 1.01"
"container_cpu_limit_usage > 0 and container_memory_limit_usage > 0"
"container_cpu_limit_usage > 0.5 and container_memory_limit_usage > 0.5 or container_cpu_limit_usage > 0.8"
`sum(rate(apiserver_request_duration_seconds_sum{subresource!="log",verb!~"LIST|WATCH|WATCHLIST|DELETECOLLECTION|PROXY|CONNECT"}[5m])) without(instance, pod)
/
sum(rate(apiserver_request_duration_seconds_count{subresource!="log",verb!~"LIST|WATCH|WATCHLIST|DELETECOLLECTION|PROXY|CONNECT"}[5m])) without(instance, pod)`

这些规则的基本规律就是基于metric name和labels筛选数据,并做一些函数处理,最后与某个阈值行比较,在PromQL中,比较操作其实会被转换为过滤操作,如果能过滤出符合的数据,就触发告警。

如果要查明白为啥告警,首先需要对每个表达式进行拆分,然后分别查询每个拆分后的表达式,看看到底是哪个数据有问题,同时也需要看看每个表达式在数据异常前后的一些情况,也就需要比告警的时间范围大一些。

另外一点是,通常告警规则是适用于一类对象的,比如都是node的cpu告警,或者内存告警,但实际告警时是会给出每个具体的node信息,其他告警也类似,如果要查询告警数据,也只需要查发生告警的对象的指标数据,其他对象不需要查。

为了实现上述的能力,通常需要解决两个问题:

  1. 将Prometheus alert rules拆分成多个查询表达式。
  2. 在Prometheus alert rules中增加本次告警专属的用于区分告警对象的labels。

解决思路

上述两个功能,在prometheus中并没有提供直接可用的能力,但可以根据代码分析出可以使用和借鉴的能力。

func InjectSelectors(expr Expr, selectors []*labels.Matcher) error {
Inspect(expr, func(node Node, _ []Node) error {
vs, ok := node.(*VectorSelector)
if ok {
vs.LabelMatchers = append(vs.LabelMatchers, selectors...)
}
return nil
})
return nil
}

对于问题1,在上述文章的分析过程中,也发现了对应的解决办法,但当时没有意识到,直到后续debug一步一步测试中才发现规律,同时也加深了对PromQL设计的理解。

PromQL是一门函数嵌套语言,同时也支持基本的算数和逻辑运算,既然是这样,那么我们就可以像解数学计算题一样,一步一步的解,而且组成PromQL表达式的元素的固定的,有11种类型(详情见上面引用的文章,这里不再赘述),每个表达式都可以拆解为这些Type的实例的组合。

对于我们的告警表达式来说,绝大多数都是左右两个数据进行算数或者逻辑运算,这种都是BinaryExpr类型,也即由更下层的类型组合成的新的表达式类型。我们可以直接识别BinaryExpr类型,如果是BinaryExpr,那就进行拆分,就得到两个表达式用于绘图,如果不是,跳过即可。

实战验证

拆分测试代码:

func TestSplitQuery(t *testing.T) {
testExprs := []string{
"node_cpu_usage > 0",
"rate(node_cpu_total{node=\"n1\"}[1m]) > rate(node_cpu_total{node=\"n2\"}[1m])",
"container_cpu_limit_usage / avg_over_time(container_cpu_limit_usage[1d] offset 1d) > 1.01",
"container_cpu_limit_usage > 0 and container_memory_limit_usage > 0",
"container_cpu_limit_usage > 0.5 and container_memory_limit_usage > 0.5 or container_cpu_limit_usage > 0.8",
`sum(rate(apiserver_request_duration_seconds_sum{subresource!="log",verb!~"LIST|WATCH|WATCHLIST|DELETECOLLECTION|PROXY|CONNECT"}[5m])) without(instance, pod)
/
sum(rate(apiserver_request_duration_seconds_count{subresource!="log",verb!~"LIST|WATCH|WATCHLIST|DELETECOLLECTION|PROXY|CONNECT"}[5m])) without(instance, pod)`,
}
for _, qs := range testExprs {
expr, err := parser.ParseExpr(string(qs))
if err != nil {
t.Fatal(err)
}
parser.Inspect(expr, func(node parser.Node, _ []parser.Node) error {
switch T := node.(type) {
case *parser.VectorSelector:
t.Log("VectorSelector", T.String())
case *parser.BinaryExpr:
t.Log("BinaryExpr", T.String(), T.Op.String(), T.LHS.String(), T.RHS.String())
return nil
}
return nil
})
}
}

结果:

Running tool: /usr/local/go/bin/go test -timeout 30s -run ^TestSplitQuery$ xxx/pkg/xxx -v
=== RUN TestSplitQuery
xxx/pkg/xxx_test.go:77: BinaryExpr node_cpu_usage > 0 > node_cpu_usage 0
xxx/pkg/xxx_test.go:75: VectorSelector node_cpu_usage
xxx/pkg/xxx_test.go:77: BinaryExpr rate(node_cpu_total{node="n1"}[1m]) > rate(node_cpu_total{node="n2"}[1m]) > rate(node_cpu_total{node="n1"}[1m]) rate(node_cpu_total{node="n2"}[1m])
xxx/pkg/xxx_test.go:75: VectorSelector node_cpu_total{node="n1"}
xxx/pkg/xxx_test.go:75: VectorSelector node_cpu_total{node="n2"}
xxx/pkg/xxx_test.go:77: BinaryExpr container_cpu_limit_usage / avg_over_time(container_cpu_limit_usage[1d] offset 1d) > 1.01 > container_cpu_limit_usage / avg_over_time(container_cpu_limit_usage[1d] offset 1d) 1.01
xxx/pkg/xxx_test.go:77: BinaryExpr container_cpu_limit_usage / avg_over_time(container_cpu_limit_usage[1d] offset 1d) / container_cpu_limit_usage avg_over_time(container_cpu_limit_usage[1d] offset 1d)
xxx/pkg/xxx_test.go:75: VectorSelector container_cpu_limit_usage
xxx/pkg/xxx_test.go:75: VectorSelector container_cpu_limit_usage offset 1d
xxx/pkg/xxx_test.go:77: BinaryExpr container_cpu_limit_usage > 0 and container_memory_limit_usage > 0 and container_cpu_limit_usage > 0 container_memory_limit_usage > 0
xxx/pkg/xxx_test.go:77: BinaryExpr container_cpu_limit_usage > 0 > container_cpu_limit_usage 0
xxx/pkg/xxx_test.go:75: VectorSelector container_cpu_limit_usage
xxx/pkg/xxx_test.go:77: BinaryExpr container_memory_limit_usage > 0 > container_memory_limit_usage 0
xxx/pkg/xxx_test.go:75: VectorSelector container_memory_limit_usage
xxx/pkg/xxx_test.go:77: BinaryExpr container_cpu_limit_usage > 0.5 and container_memory_limit_usage > 0.5 or container_cpu_limit_usage > 0.8 or container_cpu_limit_usage > 0.5 and container_memory_limit_usage > 0.5 container_cpu_limit_usage > 0.8
xxx/pkg/xxx_test.go:77: BinaryExpr container_cpu_limit_usage > 0.5 and container_memory_limit_usage > 0.5 and container_cpu_limit_usage > 0.5 container_memory_limit_usage > 0.5
xxx/pkg/xxx_test.go:77: BinaryExpr container_cpu_limit_usage > 0.5 > container_cpu_limit_usage 0.5
xxx/pkg/xxx_test.go:75: VectorSelector container_cpu_limit_usage
xxx/pkg/xxx_test.go:77: BinaryExpr container_memory_limit_usage > 0.5 > container_memory_limit_usage 0.5
xxx/pkg/xxx_test.go:75: VectorSelector container_memory_limit_usage
xxx/pkg/xxx_test.go:77: BinaryExpr container_cpu_limit_usage > 0.8 > container_cpu_limit_usage 0.8
xxx/pkg/xxx_test.go:75: VectorSelector container_cpu_limit_usage
xxx/pkg/xxx_test.go:77: BinaryExpr sum without(instance, pod) (rate(apiserver_request_duration_seconds_sum{subresource!="log",verb!~"LIST|WATCH|WATCHLIST|DELETECOLLECTION|PROXY|CONNECT"}[5m])) / sum without(instance, pod) (rate(apiserver_request_duration_seconds_count{subresource!="log",verb!~"LIST|WATCH|WATCHLIST|DELETECOLLECTION|PROXY|CONNECT"}[5m])) / sum without(instance, pod) (rate(apiserver_request_duration_seconds_sum{subresource!="log",verb!~"LIST|WATCH|WATCHLIST|DELETECOLLECTION|PROXY|CONNECT"}[5m])) sum without(instance, pod) (rate(apiserver_request_duration_seconds_count{subresource!="log",verb!~"LIST|WATCH|WATCHLIST|DELETECOLLECTION|PROXY|CONNECT"}[5m]))
xxx/pkg/xxx_test.go:75: VectorSelector apiserver_request_duration_seconds_sum{subresource!="log",verb!~"LIST|WATCH|WATCHLIST|DELETECOLLECTION|PROXY|CONNECT"}
xxx/pkg/xxx_test.go:75: VectorSelector apiserver_request_duration_seconds_count{subresource!="log",verb!~"LIST|WATCH|WATCHLIST|DELETECOLLECTION|PROXY|CONNECT"}
--- PASS: TestSplitQuery (0.00s)
PASS
ok xxx/pkg/xxx 0.025s

总结

本文主要探讨如何在告警信息中,加入拆分后的、专属告警本身的指标趋势图,来更加方便的定位告警原因。主要解决两个问题:如何拆分告警表达式和对表达式加入新的label matchers。本篇继续深入分析,并给出了如何拆分告警表达式的详细分析和代码示例;这样结合两者我们就能完整的实现我们的需求了。

来源:今日头条内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯