Zabbix 7.0 采集 Prometheus 指标并在 Grafana 中可视化

一、背景

在业务系统监控中,Prometheus 指标越来越常见。很多应用、Exporter 或中间件都会直接暴露 /metrics 接口,格式类似:

text 复制代码
xxxx_function_call_count{func_id="L0100000",host="192.168.30.175",instance="",job="xxxx_jzjy",xxxx_user="xxxx01"} 219310
xxxx_function_total_time{func_id="L0100000",host="192.168.30.175",instance="",job="xxxx_jzjy",xxxx_user="xxxx01"} 658011

这类指标直接接入 Prometheus 是比较自然的,但在很多企业现场,已有监控体系还是以 Zabbix 为主,告警、通知、权限、资产管理、运维流程也都围绕 Zabbix 建设。

所以就会出现一个实际需求:

应用暴露的是 Prometheus 指标,但需要接入 Zabbix 统一监控,并最终在 Grafana 中统一展示。

本文以 XXXX 系统的函数调用监控为例,介绍如何在 Zabbix 7.0 中采集 Prometheus 格式指标,并通过 Grafana 进行可视化。

本次重点采集两个指标:

text 复制代码
xxxx_function_call_count
xxxx_function_total_time

其中:

text 复制代码
xxxx_function_call_count:函数调用次数
xxxx_function_total_time:函数耗时

二、原始 Prometheus 指标示例

应用暴露的 /metrics 中,部分原始数据如下:

text 复制代码
xxxx_function_total_time{func_id="L0100000",host="192.168.30.175",instance="",job="xxxx_jzjy",xxxx_user="xxxx01"} 658011
xxxx_function_total_time{func_id="L0100001",host="192.168.30.175",instance="",job="xxxx_jzjy",xxxx_user="xxxx01"} 3.354317e+06
xxxx_function_total_time{func_id="L0107011",host="192.168.30.175",instance="",job="xxxx_jzjy",xxxx_user="xxxx01"} 1.442499e+07
xxxx_function_total_time{func_id="L0107011",host="192.168.30.175",instance="",job="xxxx_rzrq",xxxx_user="xxxxrzrq"} 5.386871e+06

xxxx_function_call_count{func_id="L0100000",host="192.168.30.175",instance="",job="xxxx_jzjy",xxxx_user="xxxx01"} 219310
xxxx_function_call_count{func_id="L0100001",host="192.168.30.175",instance="",job="xxxx_jzjy",xxxx_user="xxxx01"} 1.080552e+06
xxxx_function_call_count{func_id="L0107011",host="192.168.30.175",instance="",job="xxxx_jzjy",xxxx_user="xxxx01"} 334051
xxxx_function_call_count{func_id="L0107011",host="192.168.30.175",instance="",job="xxxx_rzrq",xxxx_user="xxxxrzrq"} 42173

这里有一个很重要的点:

text 复制代码
同一个 func_id 可能在不同 job、xxxx_user 下重复出现。

例如:

text 复制代码
func_id="L0107011",job="xxxx_jzjy",xxxx_user="xxxx01"
func_id="L0107011",job="xxxx_rzrq",xxxx_user="xxxxrzrq"

因此,在 Zabbix 中设计监控项 key 时,不能只使用 func_id,否则会出现重复、冲突或取错值。

推荐唯一维度为:

text 复制代码
func_id + xxxx_user

如果一个 Zabbix 主机采集多个业务主机的数据,还需要加上:

text 复制代码
host

三、整体设计思路

最终设计如下:

text 复制代码
HTTP 主监控项
  ↓
Prometheus 转 JSON,过滤出目标指标
  ↓
自动发现规则 1:发现 call_count
  ↓
监控项原型 1:函数调用次数

自动发现规则 2:发现 total_time
  ↓
监控项原型 2:函数耗时

可选:
  ↓
计算类型监控项:函数平均耗时

目标是:

text 复制代码
一次采集 Prometheus 数据
自动发现多个 func_id
自动生成调用次数和耗时监控项
Grafana 根据 Zabbix 数据源做可视化

四、Zabbix 主监控项配置

1. 新建 HTTP agent 主监控项

在 Zabbix 中创建一个主监控项,例如:

text 复制代码
名称:XXXX Prometheus Raw
键值:xxxx.prometheus.raw
类型:HTTP agent
信息类型:文本
URL:http://<业务地址>:<端口>/metrics
更新间隔:60s

URL 示例:

text 复制代码
http://192.168.30.175:9100/metrics

实际端口按现场应用暴露的 Prometheus 端口填写。


五、Prometheus 转 JSON 只保留目标指标

Zabbix 7.0 的 Prometheus 转 JSON 预处理支持 Prometheus pattern,不是完整 PromQL,但可以使用类似 Prometheus label matcher 的写法。

我们只需要采集两个指标:

text 复制代码
xxxx_function_call_count
xxxx_function_total_time

因此主监控项的预处理可以配置为:

text 复制代码
Prometheus 转 JSON

参数填写:

text 复制代码
{__name__=~"xxxx_function_call_count|xxxx_function_total_time"}

这行配置的含义是:

text 复制代码
只匹配指标名为 xxxx_function_call_count 或 xxxx_function_total_time 的数据。

错误写法:

text 复制代码
{xxxx_function_call_count|xxxx_function_total_time}

这个会报错:

text 复制代码
无效的 Prometheus 模式

配置完成后,可以点击 Zabbix 预处理测试,确认返回 JSON 中包含如下两类数据:

text 复制代码
xxxx_function_call_count
xxxx_function_total_time

六、为什么不建议只写一个指标

一开始可能会这样配置:

text 复制代码
Prometheus 转 JSON:
xxxx_function_call_count

这样确实可以获取调用次数,但主监控项处理后的数据只剩:

text 复制代码
xxxx_function_call_count

后续如果再想从这个主项中获取:

text 复制代码
xxxx_function_total_time

就获取不到了。

因此,如果计划一个主项同时服务多个自动发现和多个监控项原型,主项预处理阶段就不能只保留一个指标。

这样主项处理后的 JSON 中同时包含两类指标,后续再通过不同自动发现规则区分。


七、自动发现规则设计

由于 call_counttotal_time 是两个不同指标,后续还要分别生成不同的监控项,建议创建两个自动发现规则。

自动发现规则 1:函数调用次数发现

text 复制代码
名称:XXXX Function Call Count Discovery
类型:相关项目
主项:xxxx.prometheus.raw
键值:xxxx.function.call_count.discovery

预处理使用 JSONPath,从主项 JSON 中筛选出调用次数指标:

text 复制代码
$[?(@.name=="xxxx_function_call_count")]

LLD 宏配置:

text 复制代码
{#FUNC_ID}     $.labels.func_id
{#JOB}         $.labels.job
{#xxxx_USER}   $.labels.xxxx_user

自动发现规则 2:函数耗时发现

text 复制代码
名称:XXXX Function Total Time Discovery
类型:相关项目
主项:xxxx.prometheus.raw
键值:xxxx.function.total_time.discovery

预处理 JSONPath:

text 复制代码
$[?(@.name=="xxxx_function_total_time")]

LLD 宏配置:

text 复制代码
{#FUNC_ID}     $.labels.func_id
{#JOB}         $.labels.job
{#xxxx_USER}   $.labels.xxxx_user

这样两个自动发现规则虽然使用同一个主监控项,但各自只处理自己的指标。


八、监控项原型配置

1. 调用次数监控项原型

放在 XXXX Function Call Count Discovery 下。

text 复制代码
名称:KCBP函数调用次数:{#JOB}/{#xxxx_USER}/{#FUNC_ID}
类型:相关项目
主项:xxxx.prometheus.raw
键值:xxxx.function.call_count[{#FUNC_ID},{#JOB},{#xxxx_USER}]
信息类型:数字(无正负)

主项已经是 Prometheus 转 JSON 后的 JSON,那么这里可以使用 JSONPath 提取,也可以调整结构为中间相关项。

json 复制代码
$.[?(@.labels.xxxx_user == "{#xxxx_USER}" && @.labels.func_id == "{#FUNC_ID}")].value.first()

2. 耗时监控项原型

放在 XXXX Function Total Time Discovery 下。

text 复制代码
名称:KCBP函数耗时:{#JOB}/{#xxxx_USER}/{#FUNC_ID}
类型:相关项目
主项:xxxx.prometheus.raw
键值:xxxx.function.total_time[{#FUNC_ID},{#JOB},{#xxxx_USER}]
信息类型:数字(浮点)

Json Path:

json 复制代码
$.[?(@.labels.xxxx_user == "{#xxxx_USER}" && @.labels.func_id == "{#FUNC_ID}")].value.first()

九、Grafana 可视化设计

Grafana 使用 Zabbix 数据源后,可以基于自动发现出来的监控项做面板展示。

十、常见问题

1. 为什么自动发现要分两个?

因为 call_counttotal_time 是两个不同指标,后续生成的监控项名称、key、单位、业务含义都不同。

虽然也可以一个发现规则里混合处理,但容易出现:

text 复制代码
同一个 func_id + job + xxxx_user 被发现两次

后续监控项原型如果设计不好,会导致重复、冲突或取值混乱。

因此推荐:

text 复制代码
call_count 一个发现规则
total_time 一个发现规则

2. 为什么 key 不能只写 func_id?

因为同一个 func_id 会出现在不同 job 和 xxxx_user 下。

错误示例:

text 复制代码
xxxx.function.call_count[{#FUNC_ID}]

推荐:

text 复制代码
xxxx.function.call_count[{#FUNC_ID},{#JOB},{#xxxx_USER}]

如果还有多个 host:

text 复制代码
xxxx.function.call_count[{#xxxx_HOST},{#FUNC_ID},{#JOB},{#xxxx_USER}]

3. 这种方式是否减少了 HTTP 拉取的数据量?

不一定。

如果 Zabbix 主项 URL 是:

text 复制代码
http://<exporter>:<port>/metrics

那么 Zabbix 仍然会先拉取完整 /metrics,然后通过 Prometheus 转 JSON 筛选目标指标。

这种方式减少的是:

text 复制代码
Zabbix 后续处理和保存的数据量

但不减少:

text 复制代码
HTTP 传输的数据量

如果要真正减少拉取数据量,可以考虑让 Zabbix 请求 Prometheus Server 的 /federate 接口,只返回指定指标。

示例:

text 复制代码
http://prometheus:9090/federate?match[]={__name__=~"xxxx_function_(call_count|total_time)"}

实际使用时需要注意 URL 编码。


十一、总结

本文介绍了一种在 Zabbix 7.0 中采集 Prometheus 指标,并通过 Grafana 进行可视化的实践方式。

核心经验有两点:

第一,Prometheus 转 JSON 中如果要匹配多个指标,应该使用:

text 复制代码
{__name__=~"xxxx_function_call_count|xxxx_function_total_time"}

而不是简单写:

text 复制代码
{xxxx_function_call_count|xxxx_function_total_time}

第二,自动发现时要注意指标维度,不能只用 func_id,而应该使用:

text 复制代码
func_id + job + xxxx_user

通过这种方式,可以在不改造业务系统的情况下,把 Prometheus 指标纳入 Zabbix 统一监控体系,同时借助 Grafana 实现更灵活的可视化展示。