前言
这个礼拜一面在找那个CrashLoopBackoff的原因,另一个是在尝试修复es报错Data too large的问题,还有一个任务就是解析告警规则了,因为新需求加了很多类型的告警,支持的维度也更多了,我们的告警使用的Prometheus + Alertmanager,之前的几篇博客也提到过,这里主要想记录一下我在解析规则这里,遇到的一些问题以及解法。
抛出问题
Counter类型计算过去一个时间段内的增量
举例问题:计算revision_request_count过去五分钟的请求次数?
问题分析:revision_request_count是queue-proxy上报的指标,用于记录knative创建的pod请求次数,熟悉Prometheus的同学都知道,prometheus有四种数据类型,其中counter是只增不减的,比如http请求次数,一分钟内请求一次是1,第二分钟再请求一次是2,然后第三分钟一直不请求,那么值就一直是2,这时候在第四分钟的时刻,计算过去一分钟内,请求的次数,就是0。首先遇到问题不要怕,翻开Promemtheus的官方手册可以看见有个函数叫做increase(),可以计算counter类型过去时间的增量
解法:
increase(revision_request_count{pod=~"api.*"}[5m])
PromSQL是支持正则的,因此可以使用正则表示式的语法,这里使用了约等于。同时需要注意一点的是,incease()函数是基于rate()函数得来的,因此这里可能会有小数点的情况,因此,可以使用round()函数进行取整:
round(increase(revision_request_count{pod=~"api.*"}[5m]))
Counter类型画增量折线图
举例问题:查询revision_request_count过去一小时内,每五分钟的请求次数?
分析:这个问题是可以在上一个问题的基础上引申,上面已经做到了对过去5分钟的请求次数,这里只需要为这个过去指定一个时刻,就可以计算出一个小段一个小段的值。promehteus的查询语句分为两种,一种是query,一种是query_range,query_range的语法:
curl 'http://localhost:9090/api/v1/query_range?query=up&start=2015-07-01T20:10:30.781Z&end=2015-07-01T20:11:00.781Z&step=15s'
那么问题的答案也出来了
答案:
query_range?query=round(increase(revision_request_count{pod=~"api.*"}[5m]))&start=2021-02-27T16:00:00.000Z&end=2021-02-27T17:00:00.000Z&step=5m'
注意这里的step=5m需要和[5m]保持一值。
根据已有标签,按规则增加新的标签
举例问题:由于knative带有扩容特性,使用了kpa或者hpa都可以对pod按照cpu或者concurrency进行扩容,也就是会生成许多类似于如下这样的pod:
test-001-deployment-bf7dc8f95-xadjk
test-001-deployment-bf7dc8f95-gfd5b
test-001-deployment-bf7dc8f95-mgxtv
test-002-deployment-5fcfdbfc4-xx6cs
test-002-deployment-5fcfdbfc4-bw6rd
test-003-deployment-68f88c67c-6ng7n
test-003-deployment-68f88c67c-aferd
其中test为服务名称,001表示第一个版本,002表示第二个版本,003表示为第三个版本,deployment是自定义的后缀,第一个随机段是replicaset的标识,第二个随机段是pod的标识,
那么revison_request_count的值就会是这样的:
revision_request_count(pod="test-001-deployment-bf7dc8f95-xadjk", namespace="test")
revision_request_count(pod="test-001-deployment-bf7dc8f95-gfd5b", namespace="test")
revision_request_count(pod="test-001-deployment-bf7dc8f95-mgxtv", namespace="test")
revision_request_count(pod="test-002-deployment-5fcfdbfc4-xx6cs", namespace="test")
revision_request_count(pod="test-002-deployment-5fcfdbfc4-bw6rd", namespace="test")
revision_request_count(pod="test-003-deployment-68f88c67c-6ng7n", namespace="test")
revision_request_count(pod="test-003-deployment-68f88c67c-aferd", namespace="test")
需求是能够按照不同版本或者不同服务对revision_request_count进行聚合
分析:这里想到的第一个解决方式是使用group分组(Java的stream用的太多的后遗症),而prometheus也有相应用法,比如 sum by (namespace, pod)(),但这里by的是label,无法支持正则分组,因次此法不通。一度难倒我了,有点儿手足无措的感觉,不过还好我翻看prometheus官方文档的时候,找到这样一个函数label_replace(),可以将label按照正则表达式切分,然后取其中的一段,作为新的标签加入到指标中。举例:
label_replace(revision_request_count{namespace="test",container="user-container", pod=~"test.deployment.*|.*"}[5m]), "service","$1","pod","(.*).[0-9]{3}.deployment.*")
解释一下,label_replace()函数,会对pod标签,按照 "(.*).[0-9]{3}.deployment.*"模式进行匹配,如果匹配不到,则无变化,如果匹配到了,那么就将service=$1(正则匹配的第一个串,这里是test)加入到label中,在本例中,也就是会增加service=test这个标签。细心的小伙伴会发现,pod=~“test.deployment.*|.*”,这里使用了竖线|这个语法是指或者的意思,约等于a,或者约定于b,取的是并集。同时支持正则,如果使用.*则标识所有,注意不是一个星*,而是.*,这里只是演示|的作用。
返回的结果:可以看见增加了一个标签
revision_request_count(service="test", pod="test-001-deployment-bf7dc8f95-xadjk", namespace="test")
revision_request_count(service="test", pod="test-001-deployment-bf7dc8f95-gfd5b", namespace="test")
revision_request_count(service="test", pod="test-001-deployment-bf7dc8f95-mgxtv", namespace="test")
revision_request_count(service="test", pod="test-002-deployment-5fcfdbfc4-xx6cs", namespace="test")
revision_request_count(service="test", pod="test-002-deployment-5fcfdbfc4-bw6rd", namespace="test")
revision_request_count(service="test", pod="test-003-deployment-68f88c67c-6ng7n", namespace="test")
revision_request_count(service="test", pod="test-003-deployment-68f88c67c-aferd", namespace="test")
以上是对服务进行聚合的,要对于服务的不同版本聚合,可以稍微更改一下正则表达式
label_replace(revision_request_count{namespace="test",container="user-container", pod=~"test.deployment.*|.*"}[5m]), "service","$1","pod","(.*).deployment.*")
数据就是这样的:可以看见增加了一个标签revision
revision_request_count(revision="test-001", pod="test-001-deployment-bf7dc8f95-xadjk", namespace="test")
revision_request_count(revision="test-001", pod="test-001-deployment-bf7dc8f95-gfd5b", namespace="test")
revision_request_count(revision="test-001", pod="test-001-deployment-bf7dc8f95-mgxtv", namespace="test")
revision_request_count(revision="test-002", pod="test-002-deployment-5fcfdbfc4-xx6cs", namespace="test")
revision_request_count(revision="test-002", pod="test-002-deployment-5fcfdbfc4-bw6rd", namespace="test")
revision_request_count(revision="test-003", pod="test-003-deployment-68f88c67c-6ng7n", namespace="test")
revision_request_count(revision="test-003", pod="test-003-deployment-68f88c67c-aferd", namespace="test")
按照版本聚合
这里需要基于上面那个例子,不过指标名称可能会发生少许变化,revsion_request_count改成kube_pod_container_status_running,正在运行的实例数,细心的小伙伴会发现这里使用到了一个函数,max_over_time(),这里是取过去一段时间内最大的值,相应的还有max_over_time(),avg_over_time(),感兴趣的小伙伴可以可以参考官方文档
sum by(revision)(label_replace(max_over_time(kube_pod_container_status_running{namespace="test", pod=~"test.[0-9]{3}.deployment.*|.*"}[5m]), "revison","$1","pod","(.*).deployment.*"))
按照服务聚合
这时候就可以按照服务名进行聚合了,比如:
sum by(service)(label_replace(max_over_time(kube_pod_container_status_running{namespace="test", pod=~"test.[0-9]{3}.deployment.*|.*"}[5m]), "service","$1","pod","(.*).[0-9]{3}.deployment.*"))
后记
其实knative中还有许许多多的指标,比如:
可以按照http状态码拿到不同服务的访问次数:
sum by (revision_name)(sum_over_time(revision_request_count{namespace="test",service_name=~"test",revision_name=".*", response_code=~"302|304"}[5m]))
拿到服务过去5分钟内的由于oom导致的重启次数:
sum by (service)(label_replace(sum_over_time(kube_pod_container_status_terminated_reason{namespace=~test,pod=~".*",container="user-container",reason="OOMKilled"}[5m]), "service","$1","pod","(.*).[0-9]{3}.deployment.*"))
拿到某服务某版本过去5m cpu使用率:
sum by (revision)(label_replace(max_over_time(pod_cpu_limit_usage{namespace="test",pod=~".*"}[5m]),"revision","$1", "pod","(.*).deployment.*"))
拿到某服务某版本过去5m memory使用率:
sum by (revision)(label_replace(max_over_time(pod_memory_limit_usage{namespace="test",pod=~".*"}[5m]),"revision","$1", "pod","(.*).deployment.*"))
prometheus能拿到的信息简直是太多了,我这里只是列举了我工作中常用到的一些指标,希望对大家有所帮助。