如何复用PromQL中重复的标签关联等查询片段?
我有大量用于告警和仪表盘的PromQL查询,里面重复了很多过滤和标签增强的模板代码。有没有类似SQL视图的服务端复用方式,比如服务端函数、宏之类的,能复用这些重复的查询片段?
我知道可以用录制规则(recording rule),但做标签关联(join)的时候效率极低:录制规则得暴露所有可能用到的标签,会导致基数爆炸,生成的指标既占存储又耗内存。云环境里靠加内存解决很常见,但太浪费资源了。
示例1:存储卷指标的标签增强查询
下面是基于kube-state-metrics的查询,用来给存储卷指标补充业务标签和集群关联信息:
sum without(job,instance,service,endpoint,metrics_path,prometheus) ( kubelet_volume_stats_available_bytes{kube_cluster="$kube_cluster"} # 补充业务自定义标签 * on (namespace,persistentvolumeclaim) group_left(some_org_specific_label,other_org_specific_label) group by (namespace,persistentvolumeclaim,some_org_specific_label,other_org_specific_label) ( kube_persistentvolumeclaim_labels{kube_cluster="$kube_cluster"} ) # 关联PVC与PV名称(处理标签名不一致问题) * on (namespace,persistentvolumeclaim) group_left(persistentvolume) group by (namespace,persistentvolumeclaim,persistentvolume) ( # kube_persistentvolumeclaim_info用volumename标签,而persistentvolume_info用persistentvolume标签 label_replace( kube_persistentvolumeclaim_info{kube_cluster="$kube_cluster"}, "persistentvolume", "$1", "volumename", "^(.*)$" ) ) # 补充PV的CSI驱动、卷句柄和存储类信息 * on (persistentvolume) group_left(csi_driver,csi_volume_handle,storageclass) group by (persistentvolume,csi_driver,csi_volume_handle,storageclass) ( kube_persistentvolume_info ) )
该查询逻辑:
- 查询指定集群
$kube_cluster下的存储卷可用空间指标 - 关联
kube_persistentvolumeclaim_labels添加业务标签 - 转换PVC标签名,实现与PV的关联匹配
- 补充PV的CSI相关信息
- 移除冗余标签
如果要写磁盘空闲百分比、IO阈值告警等查询,必须重复上述所有模板代码,非常繁琐。而且Prometheus没有SQL式的下推过滤逻辑,需手动添加过滤条件才能保证效率。
示例2:工作负载指标的Pod信息增强查询
另一个场景是给工作负载指标补充Pod的标签、注解和容器镜像信息,查询如下:
# 聚合时丢弃无关标签,PromQL无原生label_drop操作 sum without(endpoint,instance,job,prometheus,container,uid) ( some_workload_specific_metric{kube_cluster="$kube_cluster"} # 关联kube_pod_labels获取业务标签 * on (uid) group_left(org_specific_label_1, org_specific_label_2, org_specific_annotation_1) # 用group by明确指定所需标签,避免无关标签波动影响结果 group by (uid, org_specific_label_1, org_specific_label_2) ( kube_pod_labels{kube_cluster="$kube_cluster"} ) # 关联kube_pod_info获取Pod节点与IP * on (uid) group_left(pod_ip,node) group by (uid, pod_ip, node) ( kube_pod_info{kube_cluster="$kube_cluster"} ) # 关联kube_pod_container_info获取容器镜像 * on (uid,container_id) group_left(image_spec,image_id) group by (uid,container_id,image_spec,image_id) ( kube_pod_container_info{kube_cluster="$kube_cluster"} ) # 关联kube_pod_annotations获取业务注解 * on (uid) group_left(org_specific_annotation_1) group by (uid,org_specific_annotation_1) ( kube_pod_annotations{kube_cluster="$kube_cluster"} ) )
每个Alertmanager告警都重复这套逻辑,维护成本极高。
可行的替代方案
1. Prometheus客户端宏
Prometheus原生支持客户端宏,通过配置文件定义重复查询片段,在查询中引用。需开启--web.enable-lifecycle参数,然后在prometheus.yml中配置:
macros: enrich_volume_labels: | * on (namespace,persistentvolumeclaim) group_left(some_org_specific_label,other_org_specific_label) group by (namespace,persistentvolumeclaim,some_org_specific_label,other_org_specific_label) ( kube_persistentvolumeclaim_labels{kube_cluster="$kube_cluster"} ) * on (namespace,persistentvolumeclaim) group_left(persistentvolume) group by (namespace,persistentvolumeclaim,persistentvolume) ( label_replace( kube_persistentvolumeclaim_info{kube_cluster="$kube_cluster"}, "persistentvolume", "$1", "volumename", "^(.*)$" ) ) * on (persistentvolume) group_left(csi_driver,csi_volume_handle,storageclass) group by (persistentvolume,csi_driver,csi_volume_handle,storageclass) ( kube_persistentvolume_info )
简化后的查询:
sum without(job,instance,service,endpoint,metrics_path,prometheus) ( kubelet_volume_stats_available_bytes{kube_cluster="$kube_cluster"} $enrich_volume_labels )
宏由客户端解析,不增加服务端存储开销,能大幅减少代码重复。
2. Thanos/Cortex服务端查询模板
如果使用Thanos或Cortex等分布式Prometheus方案,它们支持服务端查询模板,可将重复逻辑存储在服务端,通过模板名称调用并传入参数,实现类似SQL视图的复用能力。
3. 自定义查询中间层
开发轻量API服务,封装常用的标签增强逻辑。比如接收基础指标名和集群参数,返回拼接好的完整PromQL查询,再转发给Prometheus。这种方式完全自定义,灵活度最高。
4. 优化kube-state-metrics输出
若标签增强逻辑固定,可修改kube-state-metrics配置,在指标生成阶段直接添加所需标签。比如调整--metric-labels-allowlist参数,将PVC/PV的业务标签/注解同步到目标指标中,避免在PromQL中做关联操作。
5. 轻量录制规则(按需聚合)
若必须用录制规则,避免暴露所有标签,而是按需聚合:只保留业务必需的标签,提前通过sum by或avg by聚合,降低指标基数,减少存储和内存消耗。
内容的提问来源于stack exchange,提问作者Craig Ringer

