Appearance
服务 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 服务没启动。
当前实验接入的三类服务:
| 服务 | 在哪台 | 服务端口 | 用的 Exporter | Exporter 端口 |
|---|---|---|---|---|
| MariaDB | db01 | 3306 | mysqld_exporter | 9104 |
| Redis | cache01 | 6379 | redis_exporter | 9121 |
| Nginx | mon01 | 80 / 8080 | nginx-prometheus-exporter | 9113 |
MySQL Exporter
mysqld_exporter 通过 MySQL 账号读取 SHOW GLOBAL STATUS、SHOW GLOBAL VARIABLES、复制状态等内部信息。常用指标:
| 指标 | 含义 | 什么场景下会看它 |
|---|---|---|
mysql_up | Exporter 是否连上了 MySQL | Exporter 到数据库的链路确认 |
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=3306systemd 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 协议读 INFO、SLOWLOG 等信息。缓存类问题常见表现是命中率下降、内存快到上限、key 被淘汰、慢命令增多:
| 指标 | 含义 | 什么场景下会看它 |
|---|---|---|
redis_up | Exporter 是否连上了 Redis | Exporter 到 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.targetbash
# 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_statusstub_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,mysql、redis、nginx、prometheus 各返回 1。
再到 Query 页面分别查 mysql_up、redis_up、nginx_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 health | Prometheus 到 Exporter 的抓取是否 UP,上次错误是什么 |
Query 页面查 *_up | Exporter 到服务是否连通 |
Grafana Explore | 用同一条 PromQL 在 Grafana 侧交叉确认数据源和查询 |