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 上呈现。

指标采集与可视化核心链路 设备身份治理平台 业务逻辑层 指标类型 Counter (累计量) Gauge (瞬时量) Histogram (分布量) Prometheus Exporter /metrics 端点 Prometheus Server 抓取 + 存储 + 告警 Grafana 仪表盘 + 告警 仪表盘分层 L1 概览层 L2 业务层 L3 诊断层 核心原则 选对指标类型 > 精确数值

4.5 本章小结

指标采集与可视化,说白了就是三个步骤:定义指标 → 暴露端点 → 绘制图表。每一步都有坑,但只要你理解了每种指标类型的本质,选对了工具,设计好了分层,这套体系就能稳定运行很久。

我个人经验是:先跑通最小闭环。哪怕只采集三个指标、只画一张仪表盘,也比追求大而全但没人维护的监控体系强。你想想看,一个没人看的监控,跟没有监控有什么区别?