关于Prometheus告警的一些使用心得
之前一直有在用Prometheus的规则引擎配一些告警,感觉内容还是比较多,做一下整理,方便以后用的时候有的查,一些基础的点官方文档有写,这里就不做赘述,主要是记录一些实用技巧和避开一些坑。
指标规整及联动判定
首先是指标规整,从长期使用Prometheus的经验来看,同一个服务器上,我们可能部署多个exporter(尽管我在实际的生产环境尽可能的在进行exporter的合并),因此,同一个节点上的采集端点拥有一个label能够进行联结是很有必要的,比如,在所有的job配置中添加以下内容:
- job_name: Node
...
relabel_configs:
- source_labels: [__address__]
separator: ;
regex: (.*):([0-9]+)
target_label: ipaddress
replacement: $1
action: replace
这样就会生成一个ipaddress
标签,这将会方便我们进行指标的联动,比如,使用doris
自带的metrics时,存在一个叫做doris_be_memory_allocated_bytes
的指标,这个指标的含义是be
角色当前使用了多少内存,实际使用中,我们更多的是需要知道内存使用率,而不是值,因此,在对指标进行统一label的配置后,我们可以这样进行内存使用率的查询:
doris_be_memory_allocated_bytes / on(organization, cluster, area, ipaddress) node_memory_MemTotal_bytes * 100 > 80
这个公式将会给出be
组件的内存使用率。
告警多条件判断
这里说一个场景,针对于NIFI组件,我们有这样的一个判定规则:
- 任一集群的
RootProcessGroup
的队列文件数大于10000时 - 对应集群的存在队列文件数大于3000时
当这两个条件同时满足时,告警需要通报对应集群下每个节点的文件积压数量,如果要按照上述的逻辑编写表达式的话,是这样的:
(count((sum by(component_name) (nifi_amount_items_queued{component_type="RootProcessGroup"}) > 2000 ) and on(component_name) (max by(component_name, instance) (nifi_amount_items_queued{component_type="RootProcessGroup"}) > 500)) by(component_name, instance)) * on(component_name) group_right() sum by(component_name, instance) (nifi_amount_items_queued{component_type="RootProcessGroup"})
表达式非常冗长,可读性很差,不论是维护还是后期交接,都会很麻烦,换个人来看表达式的count部分根本不知道想干什么,因此我们可以设置多个告警,然后组合不同的告警来实现这个目标,首先,我们配置一个NIFI集群告警的判定规则,这会对应我们的第一个判定逻辑:
- alert: NIFIClusterFlowWarn
expr: sum(nifi_amount_items_queued{component_type="RootProcessGroup"}) by (component_name,province)>10000
for: 5m
labels:
# HIGH > MEDIUM > LOW
serverity: MEDIUM
alertgroup: NIFI
annotations:
summary: "NIFIClusterFlowWarn"
description: 'NIFIClusterFlowWarn'
随后我们编写一个对应于第二个判定逻辑的规则:
- alert: NIFINodeFlowWarn
expr: max by(component_name, province, instance) (nifi_amount_items_queued{component_type="RootProcessGroup"}) > 3000
for: 5m
labels:
# HIGH > MEDIUM > LOW
serverity: MEDIUM
alertgroup: NIFI
annotations:
summary: "NIFINodeFlowWarn"
description: 'NIFINodeFlowWarn'
最后我们只需要写一个一目了然的判定规则作为新告警即可:
- alert: NIFI积压详情
expr: '((ALERTS{alertname="NIFIClusterFlowWarn"} == 1) and on(component_name, province) (ALERTS{alertname="NIFINodeFlowWarn"}==1)) * on(component_name, province) group_right() sum by(component_name, province, instance) (nifi_amount_items_queued{component_type="RootProcessGroup",project="qz_caiji"})'
for: 10m
labels:
# HIGH > MEDIUM > LOW
serverity: MEDIUM
alertgroup: NIFI
annotations:
summary: "NIFI积压告警"
description: '{{ $labels.instance }}-{{ $value }}'
告警路由分组和接受者
在Alertmanager
那边,主要配置的就是告警的路由分组以及接受者了,实际使用中这块是比较让人头疼的,如果路由没有分好,可能会出现告警没被匹配从而没发送,或者发送到奇怪的接收渠道去了,对于一个新的Alertmanager
,我们要先配置响应的告警receivers
,这里我们配置的一个webhook,一个空接收者
receivers:
- name: 'webhook'
webhook_configs:
- url: 'http://10.0.0.1:6000/api/v1/alertpush'
send_resolved: true
- name: 'nobody'
实际使用中,我们最好都配置一个空的接收者,这样,一些不需要人员接收的告警,我们就可以直接发送到nobody去。接下来,我们编写一个根路由:
route:
group_by: ['organization', 'cluster', 'area', 'alertgroup']
group_wait: 30s
group_interval: 1m
repeat_interval: 24h
receiver: 'nobody'
基于根路由的现有配置,所有的告警都会依赖['organization', 'cluster', 'area', 'alertgroup']
四个标签进行分组,没有下级路由匹配的情况下,所有告警被分发到空接收者,在这个大的根路由下,我们可以编写灵活的匹配规则,比如我们分两个子组:
route:
...
# 省略了部分配置
routes:
- receiver: 'webhook1'
match:
alertgroup: Node
group_by: ['instance']
group_wait: 5s
group_interval: 1m
repeat_interval: 24h
- receiver: 'webhook2'
match_re:
alertgroup: "HDFS|YARN|DORIS"
group_by: ['instance']
group_wait: 5s
group_interval: 1m
repeat_interval: 24h
这里将大数据组件和服务器指标做了路由分发,因为需要发给不同的接收者,如果想要匹配多个值,就用match_re
,如果匹配单个值就用match
。
子路由中又使用了instance作为分组,这样做是为了单个采集端点发生多个告警的时候,做一次性发送,不过在使用过程中,会有一种情况,那就是我们还是希望不同的告警彼此之间不要影响,即使是一个采集端点下的,这种时候可以在用告警名称做区分:
- receiver: 'webhook2'
match_re:
alertgroup: "HDFS|YARN|DORIS"
group_by: ['instance', 'alertname']
group_wait: 5s
group_interval: 1m
repeat_interval: 24h
这样设置的话,一个类型的告警就会单独认为是一个组,而不会再去做等待了。
以URL参数区分的多个Webhook的配置
实际使用过程中,我们有通过URL参数来区分多个webhook的需求,举个例子,现在我们希望大数据组件以及服务器告警分发给不同的企业微信机器人,但是我们希望在/api/v1/alertpush
这个路由下做处理,这样的好处是,不需要每次新增一个机器人接收者就要重新增加一个路由,那么我们可以这样配webhook:
receivers:
- name: 'node'
webhook_configs:
- url: 'http://10.0.0.1:8010/api/v1/alertpush?group=bigdata'
send_resolved: true
- name: 'bigdata'
webhook_configs:
- url: 'http://10.0.0.1:8010/api/v1/alertpush?group=bigdata'
send_resolved: true
这样,在webhook那边只要根据传入的group参数进行一个区分即可,这样做的应用场景是,我们可以通过配置文件动态的新增不同的告警接收者,只需要在Alertmanager里面配置receiver即可,一定程度上减少了配置的繁琐程度。
当然这只是一种可用的思路,直接全部告警发送到默认的webhook然后在这个webhook做统一的业务逻辑处理也是可以的
使用Alertmanger的API
既然Prometheus
可以给Alertmanager
发送告警,我们可不可以也依赖Alertmanager
发送自己的自定义告警?答案是肯定的,只要向Alertmanager
推送我们的告警信息即可,这是一个curl样例:
curl --location 'http://10.0.0.1:9093/api/v2/alerts' \
--header 'Content-Type: application/json' \
--data '[
{
"labels": {
"alertgroup": "TEST",
"alertname": "xxxx",
"area": "Area",
"cluster": "Cluster",
"instance": "10.0.0.1:9100",
"job": "Nodes",
"organization": "XXXX",
"serverity": "HIGH",
"up_type": "hardware"
},
"annotations": {
"description": "服务器探测异常, 请关注",
"summary": "TEST警"
},
"startsAt": "2024-04-02T01:37:03.071Z",
"endsAt": "2024-04-02T01:50:03.071Z"
}
]'
需要注意的是,任何创建的告警都遵循Alertmanager
的判定规则,即会考虑group_wait
等参数的影响,另外,如果不配置endsAt
或者为0值,则判断告警是发生的,否则认为告警是恢复的,在判定告警状态为resolved
时,endsAt
必须是startsAt
之后的时间