4. 指标采集与可视化:从原始数据到决策仪表盘
监控体系里最核心的一环,就是把设备身份治理平台里那些看不见的状态,变成可量化的数字,再变成一眼能看懂的图表。说白了,就是回答三个问题:测什么?怎么测?怎么看?
这一章,我带你把这套链路走通。咱们从指标类型讲起,再到采集工具,最后落到 Grafana 仪表盘上。嗯,这里面的坑我踩过不少,后面会一一说到。
4.1 指标类型:Counter、Gauge、Histogram
Prometheus 的指标模型其实就四种,但日常用得最多的是三种。我刚开始接触时也觉得分类麻烦,后来发现——选对类型,比选对数值更重要。
| 指标类型 | 典型场景 | 特点 | 我踩过的坑 |
|---|---|---|---|
| Counter | 请求总数、错误次数、证书签发量 | 只增不减,重启归零 | 用 Counter 统计在线人数,结果永远在涨 |
| Gauge | 当前在线设备数、CPU 使用率、队列深度 | 可增可减,反映瞬时值 | 用 Gauge 统计累计错误,重启后数据丢了 |
| Histogram | API 响应延迟、证书验证耗时 | 分桶统计,可算百分位 | 桶范围设得太宽,P99 完全不准 |
核心原则:Counter 用于累计量,Gauge 用于瞬时量,Histogram 用于分布量。混用会导致告警规则和趋势分析全部失效。
4.1.1 Counter:只增不减的计数器
Counter 是最简单的指标类型。每次发生事件就加 1,或者加 N。你想想看,设备注册成功、证书签发完成、API 调用返回 200——这些场景天然适合 Counter。
但有个细节:Counter 在进程重启后会归零。所以你在写告警规则时,不能直接看绝对值,要用 rate() 或 increase() 函数算速率。我曾经见过有人直接对 Counter 设阈值,结果重启后触发一堆误报。
# 正确的用法:算每秒速率
rate(device_registration_total[5m]) > 100
# 错误的用法:直接比较绝对值
device_registration_total > 10000 # 重启后必然触发
4.1.2 Gauge:可上可下的仪表盘
Gauge 反映的是「现在怎么样」。设备在线数、内存使用量、证书剩余有效期——这些值随时可能上升或下降。
我个人习惯给 Gauge 指标加上 _current 后缀,方便一眼识别。比如 device_online_current、cert_expiry_days_current。这样团队其他人看指标名就知道该用什么函数处理。
小技巧:Gauge 指标配合 avg_over_time() 函数,可以算出过去一段时间的平均值,用来消除毛刺。比如监控队列深度时,用 avg_over_time(queue_depth[5m]) 比直接用原始值更稳定。
4.1.3 Histogram:分布才是真相
平均值会骗人。这是我在做延迟监控时最深的体会。假设 99% 的请求都在 10ms 内完成,但 1% 的请求耗时 10 秒——平均值可能只有 110ms,看起来一切正常。但实际用户体验已经崩了。
Histogram 通过分桶(bucket)来解决这个问题。你把可能的取值范围分成若干区间,每个区间统计一个计数。然后就能算出 P50、P90、P99 这些百分位。
# 定义 Histogram 指标
cert_verify_duration_seconds_bucket{le="0.01"} 100
cert_verify_duration_seconds_bucket{le="0.05"} 950
cert_verify_duration_seconds_bucket{le="0.1"} 990
cert_verify_duration_seconds_bucket{le="0.5"} 999
cert_verify_duration_seconds_bucket{le="+Inf"} 1000
# 计算 P99
histogram_quantile(0.99, rate(cert_verify_duration_seconds_bucket[5m]))
注意:桶的范围要覆盖实际业务场景。太宽了算不出精确百分位,太窄了指标数量爆炸。我一般按数量级分桶:0.01、0.05、0.1、0.5、1、5、+Inf。这样既能覆盖大多数场景,指标数量也可控。
4.2 采集工具:Prometheus Exporter
指标定义好了,怎么让 Prometheus 拿到数据?答案是 Exporter。它本质上是一个 HTTP 服务,把内部状态转换成 Prometheus 能抓取的格式。
在设备身份治理平台里,我通常会部署三类 Exporter:
- Node Exporter:采集服务器层面的指标(CPU、内存、磁盘、网络)
- 应用自定义 Exporter:采集业务层面的指标(注册量、验证耗时、证书状态)
- 数据库 Exporter:采集数据库层面的指标(连接数、慢查询、主从延迟)
4.2.1 自定义 Exporter 的写法
很多时候,现成的 Exporter 满足不了业务需求。比如你需要统计「过去 5 分钟证书过期数量」,这个逻辑只能自己写。
我推荐用 Prometheus 官方提供的 client_golang 库。下面是一个简化版的自定义 Exporter 示例:
package main
import (
"net/http"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promhttp"
)
var (
// Counter:累计注册设备数
deviceRegTotal = prometheus.NewCounterVec(
prometheus.CounterOpts{
Name: "device_registration_total",
Help: "Total number of device registrations.",
},
[]string{"status"}, // 成功/失败
)
// Gauge:当前在线设备数
deviceOnline = prometheus.NewGauge(
prometheus.GaugeOpts{
Name: "device_online_current",
Help: "Current number of online devices.",
},
)
// Histogram:证书验证耗时
certVerifyDuration = prometheus.NewHistogramVec(
prometheus.HistogramOpts{
Name: "cert_verify_duration_seconds",
Help: "Certificate verification duration in seconds.",
Buckets: []float64{0.01, 0.05, 0.1, 0.5, 1, 5},
},
[]string{"cert_type"},
)
)
func init() {
prometheus.MustRegister(deviceRegTotal)
prometheus.MustRegister(deviceOnline)
prometheus.MustRegister(certVerifyDuration)
}
func main() {
http.Handle("/metrics", promhttp.Handler())
http.ListenAndServe(":8080", nil)
}
部署建议:Exporter 最好和业务进程同机部署,通过 localhost 访问。不要跨网络抓取,否则网络抖动会影响指标准确性。我习惯用 systemd 管理 Exporter 进程,加上自动重启策略。
4.2.2 采集配置与抓取策略
Prometheus 通过 scrape_config 来配置抓取目标。这里有几个参数需要关注:
| 参数 | 默认值 | 我的建议 | 原因 |
|---|---|---|---|
scrape_interval |
1m | 15s | 设备身份治理场景需要更细粒度 |
scrape_timeout |
10s | 5s | 超时太长会阻塞抓取循环 |
honor_labels |
false | true | 保留 Exporter 自带的标签,避免覆盖 |
scrape_configs:
- job_name: 'device-identity'
scrape_interval: 15s
scrape_timeout: 5s
honor_labels: true
static_configs:
- targets: ['localhost:8080']
labels:
service: 'device-identity'
env: 'production'
4.3 可视化工具:Grafana 与仪表盘设计
数据采回来了,存在 Prometheus 里,但一堆数字没人看得懂。这时候 Grafana 就派上用场了。我见过太多人把 Grafana 用成了「截图工具」——堆了一堆图表,但没人知道怎么看。
好的仪表盘设计,应该让一个新人走进来,30 秒内就能回答三个问题:现在正常吗?哪里有问题?趋势在变好还是变坏?
4.3.1 仪表盘分层设计
我个人习惯把仪表盘分成三层:
- L1 - 概览层:全局健康状态,适合投到大屏上。放 4-6 个核心指标,比如总设备数、在线率、错误率、平均延迟。
- L2 - 业务层:按模块或功能拆分。比如注册模块、认证模块、证书管理模块。每个模块一张独立仪表盘。
- L3 - 诊断层:用于排查问题。包含原始日志、详细指标、对比视图。通常只有运维人员会看。
设计原则:L1 用 Gauge 和 Stat 面板展示关键数字;L2 用 Time Series 展示趋势;L3 用 Table 和 Logs 展示明细。不要在一张仪表盘上混用太多面板类型。
4.3.2 核心面板示例
下面是我在设备身份治理平台里常用的几个面板配置:
| 面板名称 | 指标 | 面板类型 | 告警阈值 |
|---|---|---|---|
| 设备在线率 | device_online_current / device_total |
Stat | < 95% 告警 |
| 注册成功率 | rate(device_registration_total{status="success"}[5m]) / rate(device_registration_total[5m]) |
Time Series | < 99% 告警 |
| 证书验证 P99 | histogram_quantile(0.99, rate(cert_verify_duration_seconds_bucket[5m])) |
Time Series | > 1s 告警 |
| 错误分布 | rate(device_registration_total{status="error"}[5m]) |
Bar Gauge | 按错误类型分级 |
4.3.3 变量与模板化
Grafana 的变量功能非常强大。你可以通过变量实现动态切换环境、服务、时间范围。比如:
# 定义变量 $env,取值来自 Prometheus 标签
label_values(up, env)
# 在查询中使用变量
rate(device_registration_total{env="$env"}[5m])
这样一套仪表盘模板,就能复用到开发、测试、生产环境。我一般会预置三个变量:$env(环境)、$service(服务名)、$instance(实例地址)。
避坑:变量不要太多,超过 5 个就会让用户选择困难。另外,变量的默认值要设成「All」,避免用户打开仪表盘时看到空白。
4.4 知识体系总览
下面这张图,把指标采集与可视化的核心链路串起来了。你可以看到数据从业务系统流出,经过 Exporter 转换,被 Prometheus 抓取,最终在 Grafana 上呈现。
4.5 本章小结
指标采集与可视化,说白了就是三个步骤:定义指标 → 暴露端点 → 绘制图表。每一步都有坑,但只要你理解了每种指标类型的本质,选对了工具,设计好了分层,这套体系就能稳定运行很久。
我个人经验是:先跑通最小闭环。哪怕只采集三个指标、只画一张仪表盘,也比追求大而全但没人维护的监控体系强。你想想看,一个没人看的监控,跟没有监控有什么区别?