变量与模板
前几篇做好的仪表盘有个通病:面板查询里把 instance、namespace 这些标签值写死了。机器一扩、环境一多,就得复制一堆「长得差不多」的仪表盘,改一处要同步 N 份,迟早漏改。变量(Variables,老版本叫 Templating)就是治这个的:把标签值抽成顶部的下拉框,一张仪表盘看所有实例、所有环境。这篇记录 Grafana 10/11/12 上变量的类型、引用语法和级联联动的实战写法,示例都可以直接照抄。仪表盘本身的做法见仪表盘制作,查询里用到的 PromQL 语法见PromQL 查询语言。
变量解决什么问题
没有变量时,「看某台机器的 CPU」意味着面板查询里写死 instance="10.0.1.11:9100";想看 12 号机器,要么改查询,要么复制一个面板。五十台机器、三套环境,排列组合出来的仪表盘数量是灾难——更要命的是它们会慢慢漂移,每张长得都不一样。
引入变量后的形态:仪表盘顶部一排下拉框(环境、集群、实例),面板查询里写 instance=~"$instance",切一下下拉框,所有面板跟着变。一份仪表盘服务所有对象,改一处全局生效。Grafana 官方模板大盘都是这个思路——node_exporter 全套仪表盘就是靠 job、instance 两个变量撑起来的。
变量类型逐个讲
入口固定:仪表盘右上角齿轮(Dashboard settings)→ Variables → New variable。六种类型先有个全貌:
| 类型 | 选项从哪来 | 典型用途 |
|---|---|---|
| Query | 查数据源实时生成 | instance、namespace、pod 等标签值 |
| Custom | 手写逗号分隔枚举 | 环境、业务线等固定维度 |
| Constant | 建变量时定死,下拉框不显示 | 环境前缀、SLO 阈值等占位值 |
| Interval | 手写时间间隔枚举 | rate 窗口、聚合步长 |
| Datasource | 某类型的数据源列表 | 多套 Prometheus / 多集群切换 |
| Textbox | 自由输入,没有选项 | 临时过滤,如 trace id、容器名 |
每种类型共用几个字段:Name(引用时用 $名字,建完别轻易改,改了下拉框和引用全失效)、Label(下拉里显示的文案)、Hide(是否藏起下拉框)。下面逐个说要点。
Query:查数据源生成选项
最常用的一种:选项列表不手写,查数据源实时生成。Prometheus 数据源下用 label_values 系列函数,比如列出所有实例:
label_values(node_uname_info, instance)
适用场景:选项来自指标标签本身——实例、命名空间、Pod、磁盘挂载点,数据变了选项自动跟上。几个要点:
- Query 写法:直接写
label_values(指标名, 标签名)。Grafana 10 之后界面拆成 Query type = Label values 的表单(填 label 和 metric 两个框),底层是一回事,表单写不顺手切回 Text 模式写函数。 - Refresh 决定选项何时重查:On dashboard load(打开仪表盘时,默认)或 On time range changed(时间范围一变就重查)。Pod 这类快变对象选后者,instance 用默认就够。
- Regex 对选项做二次过滤:只想留物理盘写
/^sd[a-z]$/,想排除回环网卡写/^(?!lo$).*/。 - Sort 选 Alphabetical,不然选项顺序随缘,几百个实例里找一台得翻半天。
Custom:手写静态选项
选项是固定枚举、指标里又不带这个标签时用。Values 一栏逗号分隔:prod, staging, dev,打开 Multi-value 就能多选。典型场景:
- 环境维度:指标里 env 标签打得不规范(有的服务没打),不如手工枚举干净;
- 业务线、机房:变化极慢的维度,为它们起一次查询不值当。
Constant:藏起来的固定值
值在建变量时定死,下拉框不显示(Hide 默认就是 Constant)。听着没用,实际两个用途:
- 给指标名或标签值里的「环境前缀」占位:指标叫
prod_app_requests_total还是staging_app_requests_total,面板里用$env_prefix拼装,切环境只改变量值; - 把魔法值集中管理:SLO 阈值、告警水位线做成
$slo_target,十几个面板引用同一个变量,调整时只动一处。
Interval:时间间隔选项
给查询里的时间窗口做选项,Values 填 1m, 5m, 10m, 30m,然后在 PromQL 里引用:
rate(node_network_receive_bytes_total{instance="$instance"}[$interval])
比写死 [5m] 灵活:看长期趋势切 30m 求平滑,排障切 1m 看毛刺。不想造变量的话,Grafana 有内置的 $__interval(随时间范围和面板宽度自动算)和 $__rate_interval(自动对齐抓取间隔,比前者更稳),多数面板用内置的就够了。
Datasource:切换数据源
选项是「某类型的所有数据源」,比如所有 Prometheus 数据源。面板查询的 Data source 选这个变量,就能用一个下拉框在 prod 和 staging 的 Prometheus 之间切。前提是两套数据源里指标结构一致——多集群联邦、双机房各一套 Prometheus 的场景标配。数据源本身的接入和权限见数据源管理。
Textbox:自由输入框
一个文本框,输什么引用什么。适合临时过滤:查某个 trace id、某个容器名,不值得为它维护选项列表。代价是没有选项也就没有「选完自动刷新」的体验,输完要回车生效。
引用语法
两种写法,区别在定界:
$instance:简洁,日常够用;${instance}:花括号定界,变量名旁边紧跟其他字符时必须用。比如拼指标名app_${tier}_requests_total——写成app_$tier_requests_total,Grafana 会把tier_requests_total整体当变量名去找,找不到就渲染成空串,查询静默查不出数据。
引用有三个使用位置。
查询里,最常规:
100 - (avg by (instance) (rate(node_cpu_seconds_total{instance=~"$instance", mode="idle"}[5m])) * 100)
面板标题里:标题写 CPU 使用率 - $instance,切变量时标题跟着变,一眼知道当前看的是谁。多选时标题会列出所有选中的值,太长会被截断,所以标题里别堆太多变量。
URL 里:变量值会同步进地址栏,格式是 var-变量名=值:
https://grafana.example.com/d/node-exporter/host?var-env=prod&var-instance=10.0.1.11:9100&var-instance=10.0.1.12:9100
多值就重复同一个参数。两个实用玩法:从 CMDB、工单系统拼好 URL,一点直达「这台机器」的仪表盘;排障群里发链接自带上下文,别人点开就是同一台机器、同一时间范围。
多值与 All
变量设置里两个相关开关:
- Multi-value:允许下拉多选;
- Include All option:加一个 All 选项,一键全选。
坑在展开方式:多选后 $instance 展开成 10.0.1.11:9100|10.0.1.12:9100 这样的正则串,All 默认展开成 .*。所以引用处必须配 =~,而且习惯上给变量套一层括号:instance=~"($instance)"。
用 = 接多值变量,查询直接报错或查空;不套括号时,如果正则前后还拼了别的内容,优先级会咬人。这是变量使用的第一大坑,新面板查不出数据先查这个。
All 展开成 .*,意味着查询打在所有序列上。rate(node_cpu_seconds_total{instance=~"$instance"}[5m]) 在几百台机器、十几核的规模下是几万条序列的聚合,Prometheus 和浏览器一起喘。大基数仪表盘慎用 Include All,或者用 Custom all value 圈定范围——写一个收敛后的正则(比如只覆盖生产网段),比裸 .* 安全得多。
级联联动
真实场景几乎都要级联:选了集群,namespace 下拉只列这个集群的;选了 namespace,pod 下拉只列这个命名空间的。做法很直接——在子级变量的 query 里引用父级变量。以 cluster → namespace → pod 三级为例:
# 变量 cluster(前提是多集群指标带 cluster 标签,联邦或 remote_write 打 external_labels)
label_values(kube_pod_info, cluster)
# 变量 namespace,query 里引用 $cluster
label_values(kube_pod_info{cluster="$cluster"}, namespace)
# 变量 pod,query 里同时引用 $cluster 和 $namespace
label_values(kube_pod_info{cluster="$cluster", namespace="$namespace"}, pod)
要点三条:
- 顺序即依赖:变量列表里父级在上、子级在下,Grafana 按顺序渲染。子级 query 里引用了父级,父级一变子级自动重查——这是变量间引用带来的行为,不用额外配置。
- Refresh 各管各的:父级变化触发子级重查,和 Refresh 设置无关;Refresh 管的是「时间范围变了要不要重查选项」。
- 层级别贪多:级联超过三层,用户找东西比直接写查询还累。cluster → namespace → pod 三层是上限的舒适区。
实战:把「实例+环境」变量加进仪表盘
拿一张按仪表盘制作做好的主机监控盘练手,目标:顶部出现 env、instance 两个下拉,切了面板跟着变。
第 1 步,建 env 变量。 Settings → Variables → New variable:Type 选 Custom,Name 填 env,Values 填 prod, staging。指标里 env 标签不规范,手写枚举最省心。
第 2 步,建 instance 变量。 New variable:Type 选 Query,Name 填 instance,Data source 选你的 Prometheus,Query 写:
label_values(up{env=~"$env"}, instance)
打开 Multi-value 和 Include All,Refresh 保持 On dashboard load。instance 顺带级联了 env——切环境,实例列表自动换。
第 3 步,面板查询换变量。 把面板里写死的:
100 - (avg by (instance) (rate(node_cpu_seconds_total{instance="10.0.1.11:9100", mode="idle"}[5m])) * 100)
改成:
100 - (avg by (instance) (rate(node_cpu_seconds_total{instance=~"($instance)", mode="idle"}[5m])) * 100)
第 4 步,保存回到仪表盘。 顶部出现 env 和 instance 两个下拉。切 instance 到某台机器,所有面板曲线应只剩这台;切 env,instance 下拉的选项跟着换。
验证清单:
- 切变量后每个面板都有数据——没数据的面板,九成是查询里漏改了引用;
- 地址栏出现
var-env、var-instance参数,复制链接换个浏览器打开,下拉值应保持; - 图例(Legend)里引用了变量的面板同步检查一遍,确认显示的是切换后的值。
常见坑
- 变量查询超时:
label_values(node_cpu_seconds_total, instance)这种 query 打在高基数指标上,生成选项要扫大量序列,打开仪表盘先白屏几秒。label_values 第一个参数选基数小但带目标标签的指标——up、xxx_build_info、kube_pod_info 这类 info 指标是常客;实在不行用label_values({__name__=~"node_load1"}, instance)这种带过滤的写法收窄范围。 - All 引发聚合爆炸:前面 warning 说过了,补一句——All 选项还会让 per-instance 面板一次画几百条线,浏览器往往比 Prometheus 先倒下。
- 改了变量面板没反应:九成是面板查询里根本没引用
$变量名,变量建好了、查询还写死着,回面板编辑页把标签值换成变量引用再试。剩下一成是引用了,但面板用的数据源和变量 query 的数据源不是同一个。 - 变量值里的特殊字符:instance 值是
10.0.1.11:9100,点号在正则里是通配符,多数情况多匹配一两条无关紧要;真出问题时把引用写成${instance:regex}让 Grafana 显式按正则处理,或用:csv、:pipe这类格式化后缀控制展开形态(完整列表见官方文档 Advanced variable format options)。
小结
- 变量把标签值从查询里抽出来:一张仪表盘服务所有实例和环境,入口在 Dashboard settings → Variables。
- 六种类型记三个常用的就够:Query(选项查出来)、Custom(手写枚举)、Interval(时间窗口);Datasource 和 Constant 在特定场景救场。
- 引用用
$var,紧贴其他字符时用${var};查询、面板标题、URL(var-xxx=参数)三处都能用。 - 多值和 All 展开成正则,引用处必须
=~并套括号写成($var);All 等于全量聚合,大基数慎用。 - 级联靠子级 query 引用父级变量,顺序父上子下,层数控制在三层以内。
- 面板查不出数据先查三件事:引用写没写、
=~配没配、变量 query 的数据源对不对。
图会画了、变量会用了,下一篇把闭环补上:没人盯盘的时候,让 Grafana 主动喊人——内置告警引擎与通知策略。