ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

Grafana自定义面板与变量体系:从能看到好用的完整实战指南

Grafana自定义面板与变量体系:从能看到好用的完整实战指南

最近帮一个小团队梳理监控体系,发现一个很有意思的现象:Grafana 装好、数据源接好,默认面板也能出图,但真正到业务复盘、故障定位的时候,大家还是得去翻原始 PromQL,一张张图拼凑信息。问题不是出在 Grafana 不好用,而是大多数人只在用它自带的模板,根本没把“自定义面板”和“变量”这两个核心能力用起来。

这篇东西我拖了两周才写完,不是因为难写,而是我想把一个监控面板从“能看”到“好用”的整个过程,包括变量体系设计、多级联动、跳转传参、模板化交付,以及一堆只有实际部署才会踩到的坑,都尽量交代清楚。如果你正在搭业务监控大盘、基础设施总览,或者想把团队里散落的临时图表收敛成一套标准面板,这篇文章应该能帮你省下不少时间。

1. 自定义监控面板的定位与设计思路

1.1 默认面板解决不了什么

自带面板,比如 Node Exporter Full、Kubelet 之类的官方模板,价值在于“开箱即用”。装完就能看到 CPU、内存、磁盘、网络的基础曲线,适合第一眼确认系统活着。但只要你多问几个业务问题,它们立刻露馅:

第一个问题:这些指标属于哪个环境?默认模板通常面向单机或单集群设计,无法按 region、环境、应用做区分。你的生产、预发、测试机器混在一张图里,所有曲线绞成一团,根本没法看。

第二个问题:指标和业务标签的关联在哪里?线上一个服务有多个实例,每个实例还有机房、版本、所属应用等标签。默认模板里根本没有变量来承载这些维度,排查时只能靠手动切换数据源,或者人肉拼接 PromQL。

第三个问题:故障场景下的信息组织。故障复盘时,你最想看的是“这个服务的请求量、错误率、延迟在时间轴上怎么互相影响”,而不是一堆独立图表堆在一起。默认面板不会替你组织这些视角。

所以自定义面板的第一个价值,是把指标从“机器视角”切换成“业务视角”。这也是为什么我在搭建之前,第一件事不是打开 Gradana 拖图表,而是先列出要回答的几个业务问题。

1.2 面向业务视角选面板类型

Grafana 8 以后,图表类型经历了一次比较大的整合,老式的 Graph 和 Singlestat 已经废了,现在统一用 Time series、Stat、Table 这几个核心类型。选型看诉求,不要啥都用折线图。

我一般会这样分:

  • 大盘顶部用 Stat 面板,展示当前时刻的整体状态,比如总的 QPS、错误率、平均响应时间。每个数字配一个阈值色,绿黄红一眼可见。
  • 中部核心趋势用 Time series 面板,展示一段时间内的变化曲线,适合观察突增、突降、周期性波动。它是最常用的面板,但也是最容易被滥用的面板。
  • 需要看多维度明细时,用 Table 面板,按标签展开实例、域名、接口等维度,支持排序和搜索。
  • 页面底部放告警状态列表,比如当前处于 Pending 和 Firing 状态的规则列表。

上面这套组合,其实就是监控大屏最常见的“总—分—细”结构。把“当前状态”“历史趋势”“明细列表”三张视图放在同一屏,信息密度和阅读效率都会高很多。

1.3 布局经验与信息密度

很多第一次做面板的人,喜欢一张 Dashboard 塞三四十个图,密密麻麻铺满整屏。实际用起来完全不是那么回事——屏幕上全是曲线的结果是,你什么也看不清,而且每次调整一个变量,三四十个查询同时去跑,页面卡得没法操作。

我的经验是:一个 Dashboard 聚焦一个主题,比如“某某服务的业务全景”或“某集群的基础设施总览”。单屏面板数量控制在 6 到 10 个之间,其他细化的内容通过下一层面板或者跳转链接去承载。

布局上还有个容易被忽略的点:按 Row 分组。Grafana 的 Row 不只是视觉上的分隔线,它还承担折叠功能。把“数据接入层”“计算层”“存储层”这类不同模块的图表分到不同 Row 里,平时可以折叠,排查时只展开当前关注的模块,既清爽又能减少无效查询。

2. 变量体系:让面板“活”起来

2.1 六类变量类型怎么选

变量是 Grafana 面板的灵魂。没有变量的面板,就像写死了参数的脚本,换一套环境就得复制一份,维护成本直线上升。变量类型有六种,但实际常用的其实只有两三种。

类型作用常用场景
Query从数据源查询返回动态选项列表实例列表、job 列表、集群名称,最常用
Custom手动定义固定选项环境名(生产/预发/测试)、固定的业务分组
Constant定义常量写死在查询里的公共参数,如 namespace
Interval定义时间间隔图表粒度切换,如 1m/5m/15m
Data source切换数据源多个 Prometheus 或 Loki 实例切换
Ad hoc filters动态加过滤条件临时筛选,加了即生效,不需改查询

六种类型里,Query 变量的优先级最高,因为监控数据天生就是带标签的,通过label_values()拉一下,标签值就能直接变成下拉框选项,这种动态联动是 Grafana 最提效的地方。

2.2 创建变量的完整操作流程

创建变量不复杂,但细节里有不少门道。

打开 Dashboard Settings,切到 Variables 页签,点 Add variable。关键字段依次说:

  • Name:变量名。强烈建议只用小写字母、数字和下划线。不要用中文,不要用带空格的名字。否则在 URL 传参、PromQL 拼接、模板 JSON 编辑时你会被各种转义问题折磨。
  • Type:选 Query。
  • Data source:选对应的 Prometheus。
  • Query:这里填 PromQL 风格的查询,但不用返回具体数值,而是返回标签值列表。

最常用的查询是:

label_values(up, instance)

这行的意思是:从up指标里取instance标签的所有值,去重后返回。如果你想要 job 列表:

label_values(up, job)

如果你的指标不是所有序列都有某个标签,加上一个限定会更精确。比如你想取当前job="node-exporter"下的实例,但不想把其它序列的 instance 混进来,可以写成:

label_values(node_load1{job="node-exporter"}, instance)

除了label_values,还有两个常用函数:

query_result(topk(5, count by (instance)(up)))

query_result能执行任意查询并返回结果列表,适合做更复杂的过滤。比如你想列“最近 5 分钟内有数据上报的实例”,而不是取所有曾经存在过的标签值,用query_result会准确很多。

Preview of values 会实时预览查询结果。这里我建议养成习惯:每次填完变量查询,先看一眼预览结果是否符合预期,再去面板里引用。变量查询写错时不会报错,只是下拉是空的,后面排查很头疼。

还有三个选项需要理解:

  • Include All option:在下拉菜单里加一个“All”选项,对应查询时不过滤该维度。配合Multi-value使用,可以一次选择多个值。
  • Multi-value:允许多选。开启后,变量在 PromQL 里会变成一个数组,需要用正则或=~语法去匹配。
  • Custom all value:当勾选 All 时实际传什么值。默认是.*,适合正则匹配的场景。

2.3 正则清理与分组显示

变量查询返回的原始标签值不一定是用户友好的。典型例子:instance标签的值是10.0.0.1:9100,带上端口号,下拉框长得难看。可以在变量配置里加正则表达式,把端口过滤掉。

正则写在 Regex 字段,比如:

/([^:]+):\d+/

这会把10.0.0.1:9100提取为10.0.0.1。但要注意的是,如果 PromQL 查询里用$host去做标签匹配,实际传的也是10.0.0.1,不带端口,必须保证指标里的标签值确实是10.0.0.1而不是带端口。所以正则过滤只适用在标签值本身不含端口的数据源里。

另一个有用功能是分组显示。当实例数量特别多时,下拉菜单全平铺很难选。可以利用label_values返回的多个标签构造嵌套分组,用分隔符/来实现层级下拉:

label_values(up{cluster="$cluster"}, instance)

比如先选集群,再选实例,两级联动下拉。这在多集群环境下非常有帮助。

2.4 多级联动:让下拉框像过滤漏斗

变量之间是可以互相引用的。你在定义一个变量时,查询里可以直接使用前面定义的变量。

典型场景是“环境 -> 应用 -> 实例”三层联动:

第一步,定义环境变量:

label_values(up, env)

第二步,应用列表只取当前环境下的应用:

label_values(up{env="$env"}, job)

第三步,实例列表基于选中的应用获取:

label_values(up{env="$env", job="$job"}, instance)

注意变量定义顺序很重要。Grafana 会按定义顺序解析变量,后面的变量可以引用前面的变量。如果报错或者下拉为空,多半是引用了还没有定义的变量,或者变量名拼写不一致。

多级联动的真正价值在于:一张面板,通过三个下拉框,可以精确锁定“生产环境 -> 订单服务 -> 具体某台实例”上的指标,而不需要为每个实例单独做一套面板。这就是模板化监控的核心。

3. 面板配置进阶:变量怎么用出彩

3.1 查询编辑器内的变量用法

变量定义好了,真正引入到面板查询里才是重头戏。

在 PromQL 查询里使用变量有两种语法:$varname和${varname}。大多数场景用$varname就够了,但在字符串拼接场景(比如正则匹配)里,${varname}更安全,因为它能明确区分变量名边界。

举一个实际的例子,展示“请求错误率按实例聚合”的查询:

sum( rate(http_requests_total{job="$job", instance=~"$instance"}[$__rate_interval]) by (instance) )

这里有几个细节:

  • instance=~"$instance":当不勾选 Multi-value 时,单个值直接匹配;勾选后,Grafana 会把多个值拼成(a|b|c)的形式,配合=~正则匹配,非常方便。
  • 如果变量里包含正则特殊字符,比如点号、括号,建议先通过变量 Regex 清洗掉,否则拼接出来的正则可能不生效。
  • $__rate_interval是 Grafana 内置的自动区间变量,它会根据面板时间范围和绘图密度自动计算一个适合rate函数的步长。比手写5m、1m更科学,能有效避免“步长太小导致每个点都为空”或“步长太大导致曲线过于平滑”的问题。

3.2 Legend 格式与单位显示

很多面板做得糙,一个重要原因是图例不清晰。在 Time series 面板的 Legend 字段里,可以通过模板语法展示你想看的标签维度。比如:

{{instance}} {{job}}

这样每一根曲线都会显示它的实例和所属 job。如果你的查询里做了分组,例如by (instance),图例模板也可以写成:

{{instance}}

直观看到每一根线是谁,排查时少很多鼠标悬停操作。

单位设置同样重要。如果指标是字节数,默认显示的是一串天文数字,不方便阅读。在 Field 配置里把 Unit 改成bytes(SI),Grafana 会帮你自动换算成 KB、MB、GB。如果指标是秒数但显示的是毫秒,注意单位换算,Grafana 不会自动猜单位,一直显示裸数字会让人误读。单位选对了,面板的专业感立刻上一个台阶。

3.3 阈值与告警联动

面板上画一条报警线,能让人一眼看出是否超过安全水位。Grafana 的 Threshold 功能在 Panel 的 Field 页签里配。

给一个 CPU 使用率面板配两个阈值:

  • 80% 为黄色警告
  • 95% 为红色严重

操作过程是:在 Thresholds 设置里点 Add threshold,输入 80 和 95,选择对应的颜色。要注意的是,阈值判断的方向取决于设置顺序,默认是从低到高依次匹配。先加一个基数值(比如Base),再逐步往上加,不要倒着配。

面板值可以是查询返回的瞬时值,也可以是聚合值。如果你想看“P95 响应时间”,查询里写:

histogram_quantile( 0.95, sum by (le) ( rate(http_request_duration_seconds_bucket{job="$job"}[$__rate_interval]) ) )

再把 Unit 设为seconds。阈值设为 0.2、0.5 等单位换算后的值,配合颜色变化,基本就是一个轻量告警面板。

真正的告警规则建议还是由 Grafana Alerting 去承担,面板阈值只是给人看的。

3.4 Data links 与跨面板跳转

面板之间如果只是平铺,信息层级仍然不够。通过 Data links 实现“从总览面板下钻到实例面板”,是一种极少人用但极其有效的设计。

在 Time series 面板的 Data links 配置里,可以设置一个链接。点击曲线上的某个点,Grafana 会弹出一个链接跳转到另一个面板,并把当前面板的变量值通过参数传过去。

URL 模板示例:

/grafana/d/bd2f6f0d-example/instance-detail?var-env=${env}&var-job=${job}&var-instance=${instance}

这相当于实现了从总览面板到详情面板的参数传递。你在一张大屏上看到某个实例的 CPU 异常,点一下图,直接跳到该实例的详细面板,省去了手动修改下拉框的操作。这里的变量名要保持和详情面板里定义的变量名一致,否则参数传不过去。

我实际使用中最爽的场景是:总览面板里看到错误率飙升,点击时间轴的异常区域,自动带着当时的env、job、instance跳转到下方的详细面板,直接分析该实例的日志和性能指标。排障效率的提升是肉眼可见的。

4. 把面板模板化:一次开发,到处复用

4.1 Dashboard JSON 结构入门

Grafana 里的所有 Dashboard,本质上是一个 JSON 文件。UI 上的每一次改动,最终都会反映到这个 JSON 里。理解了这一点,很多看起来高级的操作其实就变得很简单。

一个 Dashboard JSON 的基本结构包含几块:

  • dashboard元信息:uid、title、tags、timezone。
  • panels:数组,每个元素对应一个面板,包含面板类型、字段配置、数据源、查询目标。
  • templating:变量定义数组,对应你在设置页配的变量列表。
  • annotations:注解事件,比如部署标记、告警事件。

直接编辑 JSON 是一个高风险操作。我在开发时偶尔会手动改 JSON,比如批量替换面板的 title、修改所有面板的 datasource,但这种操作一定在改动前先备份原文件。

推荐的做法是:在 UI 里把面板调好,然后导出 JSON,把它当作基础模板存到版本管理里。后续新环境需要时,把 JSON 里的 uid、datasource 占位符替换一下,重新导入即可。

4.2 参数化变量与导入导出

模板化的核心思想是把“环境差异”全部收敛到变量里。拿一个 Node Exporter 基础监控面板举例,模板好之后,新环境部署时只需要改三个变量:

  • 数据源名称
  • 集群标签值
  • 实例过滤条件

其它全部复用。我把这种做法叫作“模板变量驱动部署”。具体操作是:

先做一个基础面板,把数据源用变量$DS引用(需要开启 Data source 类型变量),实例过滤用$instance引用,然后导出 JSON。在新环境导入时,只需要创建对应的变量加一两个必要修改,无需改动面板内容。

导入导出的坑也不少。最常见的是导入使用时,Grafana 要求填数据源名称,填错下位会导致面板全部显示No data。我建议在模板 JSON 里直接用数据源名硬编码,不要用 uid。因为不同环境的 uid 不同,但名字一般是固定的。

4.3 团队协作的规范建议

面板一旦多起来,运维团队里经常会出现“这个面板是谁建的、数据源怎么用了另一个 Prometheus、变量名为什么有的叫 host 有的叫 node”之类的混乱。

我的建议是:

  • 面板命名统一用“主题 + 用途”的格式。比如订单服务-业务全景、订单服务-实例详情。
  • 每个面板至少设置一个 Owner 标签,归属于具体团队或个人。
  • 变量命名全局统一,比如环境固定叫$env,实例固定叫$instance,集群固定叫$cluster。不要在不同面板里用不同叫法。
  • 模板化面板 JSON 必须放在版本管理里,改动走审批流程,不要在线上环境里直接乱改。

4.4 快速搭建成套面板的套路

如果你想快速搭一套能用的面板,不要从零开始。先去目标系统的官方模板库或 Grafana 官方市场找同类型面板,导入后按自己的业务加变量和调整查询。

我常用的改造路径是:

  • 导入官方模板,先跑通数据,确认指标名是否一致。
  • 把硬编码的查询改成变量引用。
  • 删掉与本业务无关的面板,收敛图表数量。
  • 增加跨面板跳转和 Data links。
  • 最后再做单位、阈值、图例的细节打磨。

这种方式比纯手写从零建要快得多,而且不容易漏掉关键指标。

5. 踩坑实录:变量与面板的十个坑

5.1 变量下拉框为空的排查

这个问题几乎每个人都遇到过。明明查询没错,但下拉就是不出东西。按优先级排查:

  • 确认变量查询里的数据源和面板当前使用的一致。
  • 确认标签名的大小写。Prometheus 标签名区分大小写,Instance和instance完全不同。
  • 确认时间范围。默认变量查询会受面板时间范围影响,如果时间范围太小,某些早先存在的标签值不会出现在列表里。
  • 确认权限。数据源目录权限是否授予当前用户,尤其使用 Grafana 多租户时。

5.2 聚合后曲线数量爆炸

查询里忘记加by分组时,Grafana 会返回大量序列,面板直接卡死,图例也根本没法看。

sum(rate(http_requests_total{job="$job"}[$__rate_interval]))

这段查询出来的是一条所有实例相加后的总曲线。如果你想要每实例一条曲线,需要写成:

sum by (instance) (rate(http_requests_total{job="$job"}[$__rate_interval]))

反之,如果只想看总 QPS,就不该在外部再做一次sum()包裹,而是直接用sum(rate(...))。哪一种写法符合预期,取决于你要展示的业务视角。

5.3 跳转链接完全不生效

配置好的 Data links 点击没反应或者跳到空白面板,常见原因有三个。

  • 变量名大小写不一致。跳转 URL 里定义的var-env要和目标面板里的变量名env严格匹配。
  • Dashboard uid 错误。详情面板的 uid 被改了,跳转还是指向旧的 uid。
  • 链接里的值没有 URL encode。实例名含特殊字符时,直接用变量拼 URL 容易出错。

5.4 查询太慢,页面卡成 PPT

指数级增长的查询数是性能杀手。一个 10 个图的面板,如果每个图都有多个查询,页面加载时的查询压力会打满数据源。

优化手段依次考虑:

  • 严格控制面板数量,删掉不用的图表。
  • 让面板时间范围与刷新率匹配,不要用 1s 刷新配 7 天数据。
  • 将百毫秒级的高频指标放在独立的窄时间面板里,基础大盘统一使用 30s 以上时间间隔。
  • 在查询前限制标签匹配范围,比如加{cluster="$cluster"},减少扫描数据量。

5.5 时区显示偏移

很多团队第一次搭面板会疑惑:为什么数据在 Prometheus 里看是对的,到 Grafana 上就偏了 8 小时?因为 Grafana 默认用 UTC 时间展示,不会自动按浏览器时区转。

解决办法很简单:在 Dashboard Settings 里把 Timezone 设为browser time,或者在用户偏好里设置默认时区。这个是纯显示层面的问题,数据本身没有错。

6. 落地经验:从能用走向好用

6.1 先定义问题,再谈面板

无论是自定义面板还是变量技巧,最核心的起点是业务问题。一个监控面板如果只是把指标堆上去,那它只是一个仪表盘;真正的价值在于能否回答这些问题:

  • 系统的健康度是多少?
  • 最近一小时内哪些服务有异常波动?
  • 某个实例故障时,影响面有多大?

我在梳理面板前,会先花半小时和业务方对一遍他们要关心的指标。宁可指标少一点,也要保证贴切。

6.2 给面板写说明和责任人

这个小细节没太多人做,但它能让你三个月后再看面板时不用重新猜。加一个 Text 面板,写清楚这个 Dashbord 的定位、指标来源、联系方式,有些团队甚至会在面板里写明“本面板由某某模块负责维护,改动需通知”。

这样做还有一个好处:当业务方来问“你这个数值是怎么算出来的”时,你能直接指给对方看说明,省去反复解释的成本。

6.3 我的使用习惯

不少人做面板喜欢追新,官方模板一更新就导,结果连自己改过什么都忘了。我自己的习惯是:面板选定后,除非有明确需求变更,否则不会频繁动它。每次改动只控制在一个小范围内,改完就导出备份到版本管理里。

这次在整理这套自定义面板方法的时候,最大的体会是:变量的设计决定了面板的上限。把变量设计做扎实了,一个团队能基于一套模板覆盖所有环境,维护成本低、使用体验高、扩展灵活。如果你也正在为监控面板的“不可维护”头疼,从这个方向入手,效果一定比再套一个新模板来得快。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进