前面八篇把指标采集、告警规则和 Alertmanager 路由全梳理了一遍,但若全配置成面板,只能看见满屏的曲线和一堆意义不明的下拉框。
这是可视化层面最典型的问题:数据全了,但信息没出来。
本篇集中解决三个事——变量(让面板动态起来)、下钻(从宏观到微观的一条路)、以及统一 Dashboard(一张屏看完所有层)。本篇不是教 Grafana 所有功能,而是提供一套经过验证的「从 Overview 到根因」的面板结构。
本篇基于 Grafana 13.0.7,PromQL 指标名沿用本系列第3~7篇,变量与标签约定沿用第2篇的
job/svc/type标签体系。
一、变量:让面板不再一把死尺
没有变量的 Dashboard 是一幅静态截图。有了变量,同一个面板可以根据选中的 job、svc、ip、instance 切换数据源,不需要为每种组合重复建图。
1、变量的类型选择
Grafana 支持七种变量类型,日常监控用得最多的只有三种:
| 变量类型 | 作用 | 典型用法 |
|---|---|---|
| Query | 从数据源实时查出一组值作为选项 | 查所有 job、svc、instance |
| Custom | 手写固定选项组 | 环境快捷切换、固定标签值 |
| Interval | 按时间范围自动匹配间隔 | 给 $__rate_interval 或 $interval 传参 |
其余类型(Constant、Ad hoc filters、Data sources、Text box)在监控场景下用得少:
- Ad hoc 适合临时过滤但不可控,
- Text box 需要用户手输——值班场景下「能选就不让输」。
2、一套实用的变量体系
变量之间不追求多层级联,而是按维度拆平。
全局变量推荐清单(按选值粒度从粗到细):
| 变量名 | 类型 | 定义或 PromQL | 用途 |
|---|---|---|---|
$job | Query | label_values(up, job) | 系统/应用标识,最顶层筛选 |
$svc | Query | label_values(up{job=~"$job"}, svc) | 服务标识,第二层,一个系统/应用一般包含多组服务 |
$type | Query | label_values(up{job=~"$job", svc=~"$svc"}, type) | 资源类型,如 node / jvm / ...,在告警抑制中用于跨层压制(见第8篇),一组服务涉及多种资源 |
$ip | Query | label_values(up{job=~"$job", svc=~"$svc"}, ip) | 服务器 IP,一台服务器部署了多个实例时有用,或同时查看一台服务器多层监控数据时有用,一组服务一般涉及多台服务器 |
$instance | Query | label_values(up{job=~"$job", svc=~"$svc", ip=~"$ip"}, instance) | 具体实例,一组服务涉及多个实例 |
$env | Custom | prod, emergency | 一般分为生产、应急;一个系统/应用可能会部署多套环境,但只有一套时该变量无意义 |
$interval | Interval | auto | 自适应时间窗口 |
联动逻辑:每一级变量在 PromQL 中引用了上一级的值作为过滤条件,切换 $job 时 $svc 自动刷新,切换 $svc 时 $type、$ip、$instance 自动刷新。
# 定义 $svc 的查询语句(关联 $job)
label_values(up{job=~"$job"}, svc)
# 定义 $type 的查询语句(关联 $job、$svc)
label_values(up{job=~"$job", svc=~"$svc"}, type)
# 定义 $ip 的查询语句
label_values(up{job=~"$job", svc=~"$svc"}, ip)
# 定义 $instance 的查询语句(关联 $job、$svc、$ip)
label_values(up{job=~"$job", svc=~"$svc", ip=~"$ip"}, instance)
⚠️ $ip 和 $instance 的区别在于:$ip 是服务器维度的筛选,当一台服务器上同时跑了多个实例(例如同一台机器上既跑 node_exporter 又跑 jvm exporter)时,用 $ip 可以把这台机器的主机层和 JVM 层数据同时展示出来;$instance 是具体实例维度的筛选,只看某个单一进程或 exporter。两者不可互相替代。
变量间的层级关系:
$job(系统/应用,最顶层)
├── $svc(服务,第二层,一个 job 包含多组服务)
│ ├── $type(资源类型,一组服务涉及多种资源)
│ ├── $ip(服务器 IP,一组服务涉及多台服务器)
│ └── $instance(具体实例,一组服务涉及多个实例)
└── $env(环境,一个系统/应用可能部署多套)
变量体系控制在三层以内($job → $svc → 其余同级),$svc 是 Dashboard 中最核心的筛选维度,下钻链路中按 $svc 聚合、按 $ip 和 $instance 展开。
3、$type 在告警抑制中的关键作用
抑制规则依赖 type 标签:
# 第8篇 inhibit_rules - 跨层抑制(依赖 type 标签)
inhibit_rules:
# 规则 1:主机层 critical 压住同 job+svc 的应用层 warning
- source_matchers:
- severity = "critical"
- type = "node"
target_matchers:
- severity = "warning"
equal: ['job', 'svc']
$type 变量在 Dashboard 中不仅用于过滤面板数据,更是值班人员通过面板上的 $type 能一眼看出当前的资源归属哪一层,结合告警面板确认抑制链路是否按预期工作。
4、变量的三个常见坑
坑一:label_values() 与 query_result() 的区别
label_values(up, job)直接从指标返回的标签取值,速度快。query_result(count by (job) (up))会执行完整查询再提取结果,慢一到两个数量级。- 能用
label_values就别用query_result。
坑二:标签值不存在时面板报错
当 $job 选了某个应用而 $svc 下的 $type 没有「node」时,面板会显示空白并报 No data。这不是故障,但值班的人看到会慌。
解法:在面板的查询选项里加过滤条件,或者在变量定义时增加正则排除。
坑三:Multi-value 开启后的曲线显示问题
当变量允许多选(Multi-value)且面板查询中用了 svc=~"$svc",如果用户没选任何值,默认会匹配全部。这个行为在小环境里没事,但在有几十个服务的大环境里,一次全选会让 Dashboard 加载慢到不可用。
建议只对 $ip 和 $instance 开多选,$job、$svc、$type 保持单选。
二、下钻:从 Overview 到根因的一条路
变量解决了「同一面板看不同对象」的问题,但值班的人面对 Dashboard 时的真实操作路径常常是这样的:
- 入口面板发现「应用层错误率 > 5%」
- 点进去看具体是哪个 svc 在报错
- 再点进去看这个 svc 对应的主机资源(通过
$svc传递) - 最后定位到是某台服务器的磁盘 I/O 排队了
每一步都需要从一个更宏观的面板「钻进」一个更微观的面板。Grafana 里实现这个链路不靠插件,只靠链接。
1、三种下钻方式对比
| 方式 | 实现 | 如何传递上下文 | 适用场景 |
|---|---|---|---|
| Dashboard 链接 | 在面板配置里加 Link → Dashboard | 手动映射变量名(var-svc=$svc) | 跨 Dashboard 下钻 |
| 数据链接 | 在面板配置里加 Link → 自定义 URL 或字段 | 提取当前数据点的标签值 | 从曲线点到日志、追踪系统 |
| Explore 跳转 | 面板上选「Inspect → Query → Run in Explore」 | 自动携带 PromQL 和变量 | 临时排查,不需要长期保留 |
日常用最多的是 Dashboard 链接:从入口 Overview 跳到主机的 USE 面板,跳转时把 job、svc、type、instance 带过去,到目标面板后变量自动选好,不用再手选一次。
2、配置一个标准的 Dashboard 链接
以「从总览面板点击某个 instance 跳转到主机 USE 面板」为例:
类型:Dashboard
目标 Dashboard:选择「主机 USE 面板」
在 URL 参数里添加:
var-job → ${__field.labels.job}
var-svc → ${__field.labels.svc}
var-instance → ${__field.labels.instance}
var-type → node # 固定值,因为目标面板只看主机层
关键在这个 var- 前缀:Grafana 的 Dashboard 链接中,var-变量名=值 的格式会把值自动填入目标面板的同名变量里。
如果目标面板里有多个变量(job、svc、type、instance),链接里至少要把必需的传过去,否则目标面板打开后变量是空的,图也空着。
⚠️ 注意: 使用 Dashboard 链接时,目标面板的变量定义和源面板的标签命名必须对齐。
3、下钻链路设计
一张 Dashboard 里塞进所有指标,不是「统一」,是「堆砌」。建议按下钻深度拆成三层,核心传递标识是 $svc:
第一层:服务 Overview(入口)
- 一行:延迟 + 流量 + 错误 + 饱和度(黄金信号四条曲线)
- 第二行:按 svc 拆开的小图阵列(每个 svc 一张 RED 三图 Row)
- 变量:
$job、$svc、$instance - 下钻路径:点击 instance → 跳转到对应的资源 USE 面板
第二层:资源 USE(下钻一层)
- CPU、内存、磁盘、网络 各一块面板
- 变量:
$job、$svc、$type、$ip、$instance - 下钻路径:点击「磁盘 I/O 高」→ 跳转到专项磁盘面板
第三层:专项深度(下钻两层)
- 只有特定场景才需要看,不在主面板中展示
- 例如:磁盘延迟分布、CPU 各核心使用率、网络重传率具体趋势
- 变量:
$svc、$ip、$instance为主
下钻链表示例:
[服务 Overview] ──点击 instance──→ [资源 USE 面板] ──点击高磁盘→ [磁盘专项面板]
│ │
└──点击 instance──→ [JVM 面板] ──────┘ (如果 type=jvm,跳 JVM)
下钻链路一旦固定,排查路径就是条件反射。不需要每次出故障重新想「该看哪个面板」。
4、数据链接:从曲线点到根因
Dashboard 链接跳的是另一个 Dashboard,数据链接跳的是某个数据点关联的外部系统。
// Grafana 面板 → 数据链接配置示例
// 链接类型:自定义 URL
// URL 模板:
https://your-jaeger.example.com/search?service=$svc&tags=instance%3D$instance
这个链接会被渲染成:点击曲线上的任意数据点 → 弹出菜单 → 一键跳转到 Jaeger 搜索页,自动带上当前 svc 和 instance。(前提是你已经部署了链路追踪。)
告警面板也可以用同样的思路:从告警规则直接链到 Alertmanager 的 UI,查看当前告警是否被抑制或静默。
三、统一 Dashboard:一张屏看完所有层
有了变量和下钻,下面再聊一聊 Layout 。
一张合格的监控 Dashboard 不是把图全部平铺,而是按层组织、按行对齐、从左到右流动。
1、Row 组织原则
推荐按「监控对象的层次」排 Row,而不是按「指标类型」:
Row 1: 入口层(黄金信号) —— 延迟、流量、错误、饱和度
Row 2: 应用层(RED) —— QPS、错误率、响应时间
Row 3: 运行时层(JVM 等) —— 堆内存、GC、线程池
Row 4: 主机层(USE) —— CPU、内存、磁盘、网络
Row 5: 依赖层(探活/外部) —— probe_success、证书过期
每一行内部再按「左状态 / 中趋势 / 右下钻」来布局:
- 左侧:当前时刻的 Stat 或 Gauge(一眼知道「现在好不好」)
- 中间:Time Series 趋势曲线(看「什么时候开始变差的」)
- 右侧(有需要时):最近告警列表或表格
布局的好处:从上往下看就是一次排查路径。入口层发现延迟高 → 到应用层看哪个 svc 慢了 → 到运行时看 GC 是否频繁 → 到主机层看哪台 ip 的资源有没有打满。
2、一个可复制的 Row 模板
以下是第7篇的分层指标清单表对应的 Dashboard Row 骨架,所有 PromQL 中的 $job / $svc / $instance 均由 Dashboard 面板变量传入:
Row 1 - 入口层(黄金信号)
| 面板 | 指标 / PromQL | 位置 |
|---|---|---|
| 存活状态 | up{job=~"$job"} | 左上 |
| 黄金延迟 | histogram_quantile(0.99, sum(rate(http_server_requests_seconds_bucket[5m])) by (le)) | 中上 |
| 黄金流量 | sum(rate(http_server_requests_seconds_count[5m])) | 中 |
| 黄金错误 | sum(rate(http_server_requests_seconds_count{status=~"5.."}[5m])) / sum(rate(http_server_requests_seconds_count[5m])) * 100 | 中下 |
| 告警列表 | — | 右下 |
Row 2 - 应用层(RED)
| 面板 | 指标 / PromQL | 位置 |
|---|---|---|
| 总 QPS | sum(rate(http_server_requests_seconds_count{job=~"$job", svc=~"$svc"}[5m])) by (instance) | 左 |
| 5xx 错误率 | sum(rate(http_server_requests_seconds_count{job=~"$job", svc=~"$svc", status=~"5.."}[5m])) by (instance) / sum(rate(http_server_requests_seconds_count{job=~"$job", svc=~"$svc"}[5m])) by (instance) * 100 | 中 |
| 平均响应时间 | sum(rate(http_server_requests_seconds_sum{job=~"$job", svc=~"$svc"}[5m])) by (instance) / sum(rate(http_server_requests_seconds_count{job=~"$job", svc=~"$svc"}[5m])) by (instance) | 右 |
指标来源:第4篇 Spring Boot 应用监控 / 第5篇 Nginx 监控。如果监控对象是 Nginx(OSS 版),RED 只能做到半个,只有 QPS 完整,状态码和延迟需要看应用层。
Row 3 - 主机层(USE)
# CPU 使用率
100 - avg(rate(node_cpu_seconds_total{job=~"$job", svc=~"$svc", mode="idle"}[5m])) by (instance) * 100
# 内存使用率
(node_memory_MemTotal_bytes{job=~"$job", svc=~"$svc"} - node_memory_MemAvailable_bytes{job=~"$job", svc=~"$svc"}) / node_memory_MemTotal_bytes{job=~"$job", svc=~"$svc"} * 100
# 磁盘空间使用率
100 - (node_filesystem_avail_bytes{job=~"$job", svc=~"$svc", fstype!~"tmpfs|overlay|devtmpfs"} / node_filesystem_size_bytes{job=~"$job", svc=~"$svc", fstype!~"tmpfs|overlay|devtmpfs"}) * 100
# 磁盘 I/O 延迟(读写平均)
rate(node_disk_io_time_seconds_total{job=~"$job", svc=~"$svc"}[5m]) / rate(node_disk_io_time_weighted_seconds_total{job=~"$job", svc=~"$svc"}[5m])
主机层面板里的 $type 已经被 Dashboard 链接固定为 node(其实不固定也可以,因为指标名与其他层的不一样),但 $svc 需要保留联动。
3、$env 变量的处理
$env 只推荐在以下场景使用:
- 同一套监控系统覆盖了生产与应急(emergency)环境
- 不同环境的指标需要严格隔离展示
如果只有一套环境,建议直接省略 $env 变量,减少 Dashboard 加载时的额外查询。
当有 $env 时,建议在 Prometheus 的 file_sd 或 relabel 配置中增加 env 标签,然后在 Grafana 变量定义中用 label_values(up, env) 拉取值。
# 定义 $env 的查询语句(当 env 标签存在时)
label_values(up, env)
# 面板 PromQL 中使用(带环境过滤)
sum(rate(http_server_requests_seconds_count{job=~"$job", svc=~"$svc", env=~"$env"}[5m])) by (instance)
4、时间轴同步
Grafana 默认所有面板共享右上角的全局时间选择器——不要对单个面板设独立时间范围,除非是在做对比。
唯一可以单独设置的是 $__rate_interval,它由 Grafana 根据抓取间隔自动计算,在 Dashboard 的 PromQL 查询里代替硬编码的 [5m]:
# 用 $__rate_interval 代替固定 [5m]
sum(rate(http_server_requests_seconds_count[$__rate_interval]))
好处是:如果后续在 Prometheus 端调整了 scrape_interval,Dashboard 不需要手动改窗口。
5、导入导出与版本管理
一个生产级 Dashboard 不是一次配好的,会随着监控目标变更、指标增减不断调整。建议:
- Dashboard 的 JSON 模型纳入 Git 管理
- 每次修改后用 Grafana 的「Save → Copy JSON」导出替换
// 在 Dashboard 的 description 字段里标注
{
"description": "统一监控 Dashboard v2\n"
"依赖标签规范:第2篇 Prometheus 服务发现(job/svc/type 标签体系)\n"
"指标来源:第3篇 node_exporter / 第4篇 Spring Boot / 第5篇 Nginx\n"
"变量约定:$job=系统/应用、$svc=服务、$type=资源类型、$ip=服务器IP、$instance=具体实例\n"
"下钻链路:本面板 → 资源 USE 面板 → 磁盘专项面板"
}
四、常见问题
1、图太多,Dashboard 加载慢
- 优先用 Stat 面板代替 Time Series(一张 Stat 相当于一条 Gauge,加载开销一个数量级)
- 变量不超过三层,减少
label_values()的查询次数 - 同一 Row 内不要放超过 4 张 Time Series 图,水平滚动不解决问题
- 考虑用 Dashboard 的「Refresh」间隔避开 1s 刷新,生产环境 30s~1m 完全够用
2、变量下拉框里的值太多(几十个 ip 或 instance 时)
- 给
$ip和$instance加搜索提示:变量配置里启用「Include All option」+ 搜索即可 - 或者用 Ad hoc filter 代替,但风险是值班的人不一定知道怎么用
3、下钻跳转后目标面板空白
- 确认目标面板的变量名和源链接中的
var-参数完全一致 - 确认目标面板里的 PromQL 使用了
$svc/$type/$instance等变量 - 使用 Debug 模式:在目标面板 URL 后面加
&debug=1,查看传入的变量值是否符合预期
4、$env 只有一套环境时意义不大
如果生产环境只有一套,$env 变量不产生任何实际过滤效果,反而增加了 Dashboard 加载时的查询开销。建议在这种情况下直接移除 $env 变量,用固定配置替代。
五、小结
监控体系的核心组件包括 Prometheus Server、Exporters、Alertmanager 和 Grafana,前面把前三块全拆了一遍,本篇补上可视化这一环。
变量让面板不再一把死尺,下钻从 Overview 到根因只有三次点击,统一 Dashboard 让值班的人从上往下扫一遍就知道该看哪一层。
三件事做完之后,这套监控体系才真正从「指标能采到」走到了「人拿到信息就能做决策」。