工程实践 · 2026年8月20日
把系统命令变成指标:一次自定义监控采集实践
当 Node Exporter 覆盖不到业务引擎时,如何设计自定义采集器、处理累计计数器并完成无中断验证。

最近的一项监控工作,是为一组 Linux 服务补充标准系统指标之外的数据:特定目录的存储占用、数据面吞吐、并发连接,以及来自多个引擎的丢包计数。
下文隐去了真实主机地址、业务名称和现场数据,只保留可以复用的技术判断。
标准 Exporter 不够时怎么办
CPU、内存、磁盘和网络等指标可以直接由 Node Exporter 提供,但一些业务引擎只通过命令行输出状态。例如某条命令返回当前连接数,另一条命令返回从进程启动以来累计的丢包数。
这里不适合让 Prometheus 直接执行系统命令。更稳妥的结构是:
业务命令
-> 自定义采集脚本
-> Node Exporter textfile 文件
-> Prometheus 定时抓取
-> Grafana 查询与展示
采集脚本由受限的 systemd timer 每 15 秒执行一次,把结果写成 Prometheus 文本格式。Node Exporter 只负责读取文件,不获得额外执行权限。
先分清 Gauge 和 Counter
这项工作里最重要的判断不是正则表达式,而是指标语义。
当前连接数、目录占用率属于 Gauge。它们表示采样时刻的状态,可以升高,也可以降低。
从进程启动后持续累加的收包、发包和丢包数量属于 Counter。Dashboard 上不能直接把累计值当作当前丢包速度,否则图表只会不断上升,无法回答“最近是否正在丢包”。
对于 Counter,PromQL 应使用:
rate(packet_drop_total[5m])
它表示最近窗口内的平均每秒增量。若要看所选时间段新增了多少,则使用:
increase(packet_drop_total[$__range])
正确区分指标类型,往往比采到数据本身更重要。错误的语义会产生看似精确、实际误导的图表。
缺失不是零
采集目录占用率时,有一个目标路径在实际服务器上并不存在。最省事的做法是输出 0,但这会让 Grafana 看起来像“目录存在且完全未使用”。
最终采用的规则是:路径存在才输出占用率,同时单独输出采集状态。这样 Dashboard 可以明确区分:
- 当前值确实是 0;
- 路径不存在;
- 命令执行失败;
- 采集器已经停止更新。
监控系统不应该把“不知道”伪装成“没有问题”。
采集器需要像服务一样被验证
采集脚本完成后,没有立刻修改生产面板,而是按顺序验证:
- 在只读条件下确认每条命令的真实输出格式;
- 用样本测试解析器是否能提取目标字段;
- 临时执行采集器,检查耗时和生成文件;
- 安装 systemd service 与 timer;
- 等待至少两个周期,确认时间戳持续变化;
- 从 Prometheus 查询新指标;
- 最后更新 Grafana provisioning Dashboard。
整个过程没有重启 Prometheus、Grafana、Docker 或业务服务。新图表上线后,再检查原有 Targets 是否仍然全部在线。
写 textfile 文件时还应采用临时文件加原子替换,避免 Node Exporter 在写入中途读到半份内容。采集失败时不要覆盖最后一份有效数据,同时必须暴露失败状态和最后成功时间,否则“旧数据仍在显示”很容易被误判为系统正常。
Dashboard 不是采集配置
后续增加磁盘 I/O 吞吐、IOPS 和平均等待时,曾出现一个容易混淆的问题:到底要不要修改 prometheus.yml?
答案是不需要。这些面板使用的是 Node Exporter 已经提供的 node_disk_* 指标,现有抓取任务早已包含它们。缺失的是 Dashboard 查询和展示,不是 Prometheus 数据源。
判断改哪一层,可以依次问:
- Exporter 是否已经产生指标?
- Prometheus 是否已经抓到指标?
- 只有 Grafana 没有查询它吗?
沿数据链逐层确认,比看到“没图”就修改抓取配置更可靠。
从现场快照到可部署项目
完成现场监控不等于项目已经可复用。后来复查仓库时发现,它仍然包含固定地址、旧的探头目录假设,以及未自动 provisioning 的告警配置。
这揭示了工程交付的另一层标准:一套系统在当前服务器上工作,只能证明实现有效;只有完成参数化、安装脚本、告警 provisioning、敏感信息扫描和全新环境演练,才能称为可重复部署。
下一步不是继续增加面板,而是把当前环境中的隐式知识写进配置和自动化里。监控的目标本来就是减少未知,监控项目自身也不应依赖只有维护者记得的操作步骤。