4、指标采集:Exporter原理、常用Exporter部署与配置,自定义Exporter开发入门
Prometheus 采用“拉取(Pull)”模式获取监控数据,这意味着 Prometheus 服务端需要主动访问目标端点(Endpoint)的 HTTP 端口来采集指标。然而,业务系统、数据库、操作系统本身并不会直接暴露 Prometheus 格式的指标数据。Exporter 正是承担“翻译官”与“代理”角色的关键组件:它将目标系统的内部状态(如 CPU 使用率、数据库连接数、Redis 内存碎片率)转换为 Prometheus 能够识别的标准指标格式,并通过 HTTP 服务暴露给 Prometheus 服务端。本章将深入剖析 Exporter 的工作原理,详细讲解 node_exporter、mysqld_exporter、redis_exporter 的部署与配置实战,并带领读者完成一个自定义 Exporter 的开发入门,为构建完整的监控体系打下坚实基础。
4.1 Exporter 核心原理与指标暴露机制
4.1.1 核心原理:从“被监控”到“可拉取”的转换器
Exporter 的本质是一个小型的 HTTP 服务,它通过目标系统提供的接口(如 Linux 的 /proc 文件系统、MySQL 的 performance_schema、Redis 的 INFO 命令)获取原始状态数据,然后按照 Prometheus 的文本格式规范(Text-based Format)将这些数据组织成指标(Metric)。Prometheus 服务端通过 HTTP 请求访问 Exporter 的 /metrics 端点,获取这些指标并存入时序数据库。
一个标准的指标输出格式如下:
# HELP node_cpu_seconds_total Seconds the cpus spent in each mode.
# TYPE node_cpu_seconds_total counter
node_cpu_seconds_total{cpu="0",mode="idle"} 12345.67
node_cpu_seconds_total{cpu="0",mode="system"} 234.56
其中 # HELP 提供指标描述,# TYPE 声明指标类型(counter、gauge、histogram、summary),后续每一行是一个样本(Sample),由指标名、标签(Label)和样本值组成。Exporter 的核心职责就是高效、准确地生成这些文本数据。
4.1.2 判断方法:如何验证 Exporter 是否正常工作
部署 Exporter 后,需要从三个层面验证其健康状态:
- 进程层面:确认 Exporter 进程是否存活,端口是否监听。使用
ss -lntp | grep 9100或curl http://localhost:9100/metrics检查。 - 数据层面:访问 /metrics 端点,检查返回的指标是否包含预期的关键指标(如 node_cpu_seconds_total、node_memory_MemAvailable_bytes)。
- 集成层面:在 Prometheus 的 Target 页面(Status → Targets)中查看该 Exporter 的端点状态是否为 UP,且 Scrape Duration 正常。
Get "http://x.x.x.x:9100/metrics": dial tcp ... connect: connection refused,通常意味着 Exporter 未启动或防火墙拦截;如果看到 context deadline exceeded,则可能是网络延迟或 Exporter 响应过慢。
4.1.3 案例拆解:一次典型的指标采集链路
假设我们监控一台生产环境的 Linux 服务器(IP: 192.168.1.10)。管理员在服务器上部署了 node_exporter,监听 9100 端口。Prometheus 配置了如下抓取任务:
scrape_configs:
- job_name: 'linux-node'
static_configs:
- targets: ['192.168.1.10:9100']
当 Prometheus 执行抓取时,会向 http://192.168.1.10:9100/metrics 发送 HTTP GET 请求。node_exporter 收到请求后,会遍历注册的各个采集器(Collector),如 cpu、memory、diskstats、filesystem 等,读取 /proc 和 /sys 下的数据,并转换为 Prometheus 格式返回。Prometheus 解析响应体,将指标数据追加到本地存储,并更新该 Target 的抓取状态。整个过程通常在毫秒级完成,如果超过 10 秒(默认 scrape_timeout),则视为抓取失败。
4.1.4 常见误区与总结复盘
- 误区一:认为 Exporter 是守护进程。实际上 Exporter 只是一个普通的 HTTP 服务,需要借助 systemd、supervisor 或容器编排来保证其开机自启和异常重启。
- 误区二:忽略指标类型。Counter 只能递增,适合请求数;Gauge 可增可减,适合内存使用量。错误使用类型会导致 PromQL 查询结果无意义。
- 误区三:标签基数爆炸。在自定义 Exporter 中,如果标签值包含用户 ID、IP 等无限增长的维度,会严重消耗 Prometheus 内存和磁盘。
4.2 常用 Exporter 部署与配置实战
4.2.1 node_exporter:主机监控的基石
node_exporter 是 Prometheus 官方提供的主机监控 Exporter,用于采集 Linux/Unix 系统的硬件和内核指标。它通过读取 /proc、/sys 等虚拟文件系统获取数据,覆盖 CPU、内存、磁盘、网络、文件系统等维度。
部署步骤(以二进制方式为例):
# 1. 下载并解压
wget https://github.com/prometheus/node_exporter/releases/download/v1.7.0/node_exporter-1.7.0.linux-amd64.tar.gz
tar xvf node_exporter-1.7.0.linux-amd64.tar.gz
cd node_exporter-1.7.0.linux-amd64
# 2. 创建 systemd 服务
sudo useradd -M -s /bin/false node_exporter
sudo cp node_exporter /usr/local/bin/
sudo chown node_exporter:node_exporter /usr/local/bin/node_exporter
# 3. 创建 systemd unit 文件
sudo tee /etc/systemd/system/node_exporter.service <<EOF
[Unit]
Description=Node Exporter
After=network.target
[Service]
User=node_exporter
Group=node_exporter
Type=simple
ExecStart=/usr/local/bin/node_exporter --web.listen-address=:9100 --collector.systemd --collector.processes
[Install]
WantedBy=multi-user.target
EOF
# 4. 启动并验证
sudo systemctl daemon-reload
sudo systemctl enable --now node_exporter
curl http://localhost:9100/metrics | head -20
关键配置参数说明:
| 参数 | 默认值 | 说明 |
|---|---|---|
| --web.listen-address | :9100 | 监听地址和端口 |
| --collector.disable-defaults | false | 禁用默认采集器,需手动指定启用项 |
| --collector.systemd | disabled | 启用 systemd 服务状态采集,需显式开启 |
| --collector.filesystem.mount-points-exclude | ^/(dev|proc|sys|run|var/lib/docker) | 排除不需要监控的挂载点 |
| --collector.netdev.device-exclude | ^(veth.*|docker.*) | 排除虚拟网络设备 |
4.2.2 mysqld_exporter:数据库监控的利器
mysqld_exporter 用于采集 MySQL 服务器的运行状态,包括连接数、慢查询、缓冲池命中率、复制状态等。它通过连接 MySQL 实例执行 SHOW GLOBAL STATUS、SHOW GLOBAL VARIABLES 等 SQL 语句获取数据。
部署与配置要点:
# 1. 创建监控账号(在 MySQL 中执行)
CREATE USER 'exporter'@'localhost' IDENTIFIED BY 'StrongPass123' WITH MAX_USER_CONNECTIONS 3;
GRANT PROCESS, REPLICATION CLIENT, SELECT ON *.* TO 'exporter'@'localhost';
# 2. 创建配置文件 .my.cnf
cat > /etc/mysqld_exporter/.my.cnf <<EOF
[client]
user=exporter
password=StrongPass123
host=127.0.0.1
port=3306
EOF
# 3. 启动 Exporter(指定配置文件)
mysqld_exporter --config.my-cnf=/etc/mysqld_exporter/.my.cnf --web.listen-address=:9104
核心指标解读:
mysql_global_status_threads_connected:当前连接数,用于判断连接池是否耗尽。mysql_global_status_queries:累计查询次数(Counter),可用于计算 QPS。mysql_global_status_innodb_buffer_pool_read_requests与..._reads:缓冲池读请求与物理读次数,用于计算命中率。mysql_slave_status_slave_io_running与..._sql_running:复制线程状态,0 表示停止,1 表示运行。
4.2.3 redis_exporter:缓存监控的快速上手
redis_exporter 支持 Redis 和 Redis Cluster,通过调用 INFO 命令获取键空间命中率、内存碎片率、客户端连接数等关键指标。
# 启动方式(支持 Redis 密码)
redis_exporter --redis.addr=redis://localhost:6379 --redis.password=YourPass --web.listen-address=:9121
# 对于 Redis Cluster
redis_exporter --redis.addr=redis://cluster-node1:6379 --redis-cluster-mode=true
常用监控指标:
| 指标名 | 类型 | 含义 |
|---|---|---|
| redis_memory_used_bytes | Gauge | Redis 已用内存 |
| redis_memory_max_bytes | Gauge | Redis 最大内存配置 |
| redis_keyspace_hits_total | Counter | 键空间命中次数 |
| redis_keyspace_misses_total | Counter | 键空间未命中次数 |
| redis_connected_clients | Gauge | 当前客户端连接数 |
| redis_mem_fragmentation_ratio | Gauge | 内存碎片率,大于 1.5 需关注 |
4.3 自定义 Exporter 开发入门
4.3.1 开发框架与核心接口
Prometheus 官方为 Go 语言提供了 client_golang 库,这是开发自定义 Exporter 最主流的方式。核心是实现 prometheus.Collector 接口,该接口包含两个方法:
Describe(chan<- *prometheus.Desc):将指标描述符发送到 channel,用于告知 Prometheus 该 Exporter 会暴露哪些指标。Collect(chan<- prometheus.Metric):实际采集指标值,并将指标发送到 channel。
更简单的开发方式是使用 prometheus.NewGaugeVec 等向量指标,配合 prometheus.MustRegister 注册,然后通过 HTTP Handler 暴露。
4.3.2 实战案例:开发一个监控网站可用性的 Exporter
假设我们需要监控多个业务网站的健康状态(HTTP 状态码、响应时间),并暴露给 Prometheus。以下是核心代码逻辑:
package main
import (
"net/http"
"time"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promhttp"
)
var (
// 定义指标:网站可用性(1=正常,0=异常)
websiteUp = prometheus.NewGaugeVec(
prometheus.GaugeOpts{
Name: "website_up",
Help: "Whether the website is up (1) or down (0)",
},
[]string{"url"},
)
// 定义指标:HTTP 响应时间(毫秒)
websiteResponseTime = prometheus.NewHistogramVec(
prometheus.HistogramOpts{
Name: "website_response_time_ms",
Help: "HTTP response time in milliseconds",
Buckets: []float64{50, 100, 200, 500, 1000, 2000},
},
[]string{"url"},
)
)
func init() {
prometheus.MustRegister(websiteUp)
prometheus.MustRegister(websiteResponseTime)
}
func probeWebsite(url string) {
start := time.Now()
resp, err := http.Get(url)
duration := time.Since(start).Milliseconds()
if err != nil || resp.StatusCode >= 400 {
websiteUp.WithLabelValues(url).Set(0)
} else {
websiteUp.WithLabelValues(url).Set(1)
}
websiteResponseTime.WithLabelValues(url).Observe(float64(duration))
if resp != nil {
resp.Body.Close()
}
}
func main() {
// 每 15 秒采集一次目标网站
targets := []string{"https://www.example.com", "https://api.example.com"}
go func() {
for {
for _, url := range targets {
go probeWebsite(url)
}
time.Sleep(15 * time.Second)
}
}()
http.Handle("/metrics", promhttp.Handler())
http.ListenAndServe(":8080", nil)
}
开发要点与注意事项:
- 指标命名:遵循
namespace_subsystem_name_unit的规范,如website_response_time_ms。 - 标签设计:标签应保持低基数,避免将请求 URL 的完整路径作为标签值,应只保留域名或服务名。
- 并发安全:如果多个 goroutine 同时写入同一个指标,需要使用
prometheus.NewGaugeVec等线程安全的向量指标。 - 采集频率:Exporter 内部可以自行控制采集频率,但更推荐由 Prometheus 的
scrape_interval控制,Exporter 只负责在收到请求时返回最新数据。
4.3.3 测试与集成
开发完成后,编译并运行程序,访问 http://localhost:8080/metrics 验证输出。然后在 Prometheus 配置中添加抓取任务:
scrape_configs:
- job_name: 'website-probe'
static_configs:
- targets: ['localhost:8080']
4.4 本章知识结构总览
| Exporter 类型 | 默认端口 | 核心数据来源 | 典型部署方式 | 关键注意事项 |
|---|---|---|---|---|
| node_exporter | 9100 | /proc、/sys | 二进制 + systemd | 按需启用采集器,排除虚拟设备 |
| mysqld_exporter | 9104 | SHOW STATUS/VARIABLES | 二进制 + 配置文件 | 最小权限账号,限制最大连接数 |
| redis_exporter | 9121 | INFO 命令 | 二进制 / 容器 | 集群模式需指定 --redis-cluster-mode |
| 自定义 Exporter | 自定义 | 业务 API / 数据库 / 文件 | Go 编译 + systemd | 标签基数控制,指标命名规范 |
在实际生产环境中,Exporter 的部署应遵循“就近采集”原则,即 Exporter 尽量部署在被监控对象所在的机器上,避免跨网络抓取带来的延迟和故障域扩大。同时,建议将所有 Exporter 的端口统一规划,并在防火墙层面仅允许 Prometheus 服务器访问这些端口,以降低安全风险。