Skip to content

服务 Exporter

Node Exporter 能看到机器有没有压力,但它看不到 MySQL 当前有多少连接、Redis 淘汰了多少 key、Nginx 处理了多少请求。服务层的故障经常发生在主机资源看起来还挺正常的时候——连接池打满了,CPU 没多高,但新请求已经进不来了。

服务 Exporter 部署在服务旁边,通过本机端口、专用账号或管理接口读取服务内部状态,再暴露自己的 /metrics 给 Prometheus 来抓。

Exporter 和它所监控的服务是什么关系

Prometheus 抓的是 Exporter,不是直接抓 MySQL/Redis/Nginx。这个间接关系意味着排查时分两段链路:Prometheus 到 Exporter(看 Targets 页面的 up),和 Exporter 到真实服务(看各自的 *_up 指标)。

text
up{job="mysql"} == 1    → Prometheus 到 mysqld_exporter 通了
mysql_up == 1            → mysqld_exporter 到 MySQL 通了

如果第一个是 1 而第二个是 0,问题在 Exporter 到服务这一端——常见原因是数据库账号密码不对、权限不足、端口不通、MySQL 服务没启动。

当前实验接入的三类服务:

服务在哪台服务端口用的 ExporterExporter 端口
MariaDBdb013306mysqld_exporter9104
Rediscache016379redis_exporter9121
Nginxmon0180 / 8080nginx-prometheus-exporter9113

MySQL Exporter

mysqld_exporter 通过 MySQL 账号读取 SHOW GLOBAL STATUSSHOW GLOBAL VARIABLES、复制状态等内部信息。常用指标:

指标含义什么场景下会看它
mysql_upExporter 是否连上了 MySQLExporter 到数据库的链路确认
mysql_global_status_threads_connected当前连接数发布后连接数突然涨、连接池异常
mysql_global_status_max_used_connections历史最高连接数是否快接近 max_connections 天花板
mysql_global_status_slow_queries慢查询累计 Counter趋势有没有随时间变坏
mysql_global_status_aborted_connects连接失败累计 Counter账号、网络或握手阶段异常

在 db01 上创建采集账号。权限只给必要的——PROCESS 看线程状态,REPLICATION CLIENT 看复制信息,SELECT 看部分状态数据:

sql
CREATE USER IF NOT EXISTS 'exporter'@'127.0.0.1'
  IDENTIFIED BY 'exporter123';

GRANT PROCESS, REPLICATION CLIENT, SELECT ON *.*
  TO 'exporter'@'127.0.0.1';

FLUSH PRIVILEGES;

连接配置放在 /etc/.mysqld_exporter.cnf。文件名以 . 开头只是约定(隐藏文件),关键是文件权限——里面是明文密码,权限制到 600 只让 prometheus 用户读:

ini
[client]
user=exporter
password=exporter123
host=127.0.0.1
port=3306

systemd unit:

ini
[Unit]
Description=Prometheus MySQL Exporter
After=network-online.target mariadb.service
Wants=network-online.target

[Service]
User=prometheus
Group=prometheus
Type=simple
ExecStart=/usr/local/bin/mysqld_exporter \
  --config.my-cnf=/etc/.mysqld_exporter.cnf \
  --web.listen-address=:9104
Restart=on-failure
RestartSec=5s

[Install]
WantedBy=multi-user.target

验证 Exporter 到 MySQL 的链路:

bash
# mysql_up 为 1 → Exporter 能连数据库
curl -s http://127.0.0.1:9104/metrics | grep '^mysql_up'

# 连接数指标有返回 → 账号权限和读取内容正常
curl -s http://127.0.0.1:9104/metrics | grep '^mysql_global_status_threads_connected'

当收到"数据库连接数告警"时,只看 threads_connected 的数字不够。连接数打满可能是因为慢 SQL 把连接占住了、发布后连接池配置变化、锁等待导致请求堆积——指标上通常同时看连接数、QPS、慢查询增长速度、锁等待,再结合主机 CPU 和磁盘 IO。

Redis Exporter

redis_exporter 通过 Redis 协议读 INFOSLOWLOG 等信息。缓存类问题常见表现是命中率下降、内存快到上限、key 被淘汰、慢命令增多:

指标含义什么场景下会看它
redis_upExporter 是否连上了 RedisExporter 到 Redis 的链路确认
redis_connected_clients当前客户端连接数连接泄漏、连接突增
redis_memory_used_bytes / redis_memory_max_bytes已用 / 最大内存内存使用率趋势
redis_evicted_keys_total因内存不足被淘汰的 key 累计数缓存空间不足——这是坏信号
redis_keyspace_hits_total / _misses_total命中 / 未命中累计数算命中率

systemd unit:

ini
[Unit]
Description=Prometheus Redis Exporter
After=network-online.target redis.service
Wants=network-online.target

[Service]
User=prometheus
Group=prometheus
Type=simple
ExecStart=/usr/local/bin/redis_exporter \
  --redis.addr=redis://127.0.0.1:6379 \
  --web.listen-address=:9121
Restart=on-failure
RestartSec=5s

[Install]
WantedBy=multi-user.target
bash
# redis_up 为 1 → 链路正常
curl -s http://127.0.0.1:9121/metrics | grep '^redis_up'

# 连接数指标有返回 → INFO 数据正常被转换
curl -s http://127.0.0.1:9121/metrics | grep '^redis_connected_clients'

Redis 命中率的 PromQL(用两个 Counter 的速率相除):

promql
rate(redis_keyspace_hits_total[5m])
/
(
  rate(redis_keyspace_hits_total[5m])
  +
  rate(redis_keyspace_misses_total[5m])
)

命中率突然掉的时候,常见原因可能是缓存 key 的规则改了导致大量 miss、过期时间调短了 key 被提前清掉、热点数据没预热、或者内存满了淘汰增加。Redis 端口能通不等于缓存效果好——要同时看命中率、内存和淘汰这三个维度。

Nginx Exporter

开源 Nginx 只能通过 stub_status 模块暴露很基础的状态——活跃连接数、累计请求数、reading/writing/waiting。这些信息的粒度只能回答"入口大概多忙",看不到每个接口的状态码分布、响应耗时、upstream 后端慢不慢。

先配 Nginx 暴露 stub_status:

nginx
server {
    listen 127.0.0.1:8080;
    server_name localhost;

    location /stub_status {
        stub_status;
        allow 127.0.0.1;    # 只允许本机 exporter 访问这个页面
        deny all;
    }
}

nginx-prometheus-exporter 的 systemd unit:

ini
[Unit]
Description=Prometheus Nginx Exporter
After=network-online.target nginx.service
Wants=network-online.target

[Service]
User=prometheus
Group=prometheus
Type=simple
ExecStart=/usr/local/bin/nginx-prometheus-exporter \
  --nginx.scrape-uri=http://127.0.0.1:8080/stub_status \
  --web.listen-address=:9113
Restart=on-failure
RestartSec=5s

[Install]
WantedBy=multi-user.target

验证:

bash
# nginx_up 为 1 → Exporter 成功读到 stub_status
curl -s http://127.0.0.1:9113/metrics | grep '^nginx_up'

# 直接看 Nginx stub_status 原始输出,区分是状态页问题还是 exporter 问题
curl -s http://127.0.0.1:8080/stub_status

stub_status 的局限在于看不到 4xx/5xx 分布和耗时分布。这在生产 Nginx 上是个明显缺口。一般用两种办法弥补:用 Nginx Plus 提供的更详细的指标(商业版),或者用访问日志采集 + 应用侧指标来做补充(日志里记录每个请求的状态码和 upstream 耗时,再通过日志分析工具统计)。

Prometheus 接入服务 Exporter

prometheus.yml 里加上三类服务的 job:

yaml
scrape_configs:
  - job_name: mysql
    static_configs:
      - targets:
          - 192.168.10.12:9104
        labels:
          env: lab
          role: database
          hostname: db01

  - job_name: redis
    static_configs:
      - targets:
          - 192.168.10.13:9121
        labels:
          env: lab
          role: cache
          hostname: cache01

  - job_name: nginx
    static_configs:
      - targets:
          - 192.168.10.11:9113
        labels:
          env: lab
          role: web
          hostname: mon01

接入后用 sum by (job) (up) 验证:node 返回 3,mysqlredisnginxprometheus 各返回 1。

再到 Query 页面分别查 mysql_upredis_upnginx_up——这些 *_up 指标和 up 不一样,它们回答的是"Exporter 能不能连上它要监控的服务"。Targets 页面只回答 Prometheus 到 Exporter 这段。

服务指标和主机指标放在一起看

排查时经常要关联两边的信息:

看到的现象服务指标指向主机指标指向
MySQL 连接数升高threads_connected、慢查询、锁等待CPU、磁盘 IO、内存
Redis 命中率下降hits/misses、evicted_keys、used_memory内存、网络、CPU
Nginx 活跃连接突增active connections、requests网卡流量、CPU
Exporter 抓取超时scrape_duration、exporter 自身日志机器负载、网络

接口超时时,如果 MySQL 慢查询在涨、同时数据库那台机器的 iowait 也高——问题大概率在 SQL 或索引上。如果服务指标正常但主机 CPU 打满——进程多了或者代码里计算量大了。分开看容易漏掉关联信息。

Exporter 故障排查的三段论

出问题时从这三段顺序来验证——前面一段不通,后面就不用看了:

bash
# 段 1:Prometheus 到 Exporter——在 Prometheus 机器上测
curl -sS http://192.168.10.12:9104/metrics | grep '^mysql_up'

# 段 2:Exporter 到服务——在 Exporter 所在机器上测
mysql -h 127.0.0.1 -uexporter -pexporter123 \
  -e 'SHOW GLOBAL STATUS LIKE "Threads_connected";'

# 段 3:Exporter 自身日志——认证失败、权限不足、采集超时都在这里
journalctl -u mysqld_exporter -n 80 --no-pager
排查入口能回答什么
Status → Target healthPrometheus 到 Exporter 的抓取是否 UP,上次错误是什么
Query 页面查 *_upExporter 到服务是否连通
Grafana Explore用同一条 PromQL 在 Grafana 侧交叉确认数据源和查询