ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

mysqld_exporter部署实战:从MySQL监控到Prometheus告警全指南

mysqld_exporter部署实战:从MySQL监控到Prometheus告警全指南 1. 部署前的认知mysqld_exporter到底解决了什么问题如果你接手过一套没有内部状态监控的MySQL大概都经历过这样的尴尬业务方跑过来说数据库变慢了你登录服务器看了一眼CPU、内存、磁盘都在正常范围系统负载也不高但你没法回答慢在哪——是锁等待堆积连接数被打满还是InnoDB缓冲池命中率崩了又或者干脆是复制延迟在拖后腿mysqld_exporter很多人习惯按标题写成mysql_export官方名字其实是mysqld_exporter就是专门用来补上这块拼图的组件。它是一个装在MySQL节点上的轻量级采集程序把自己伪装成一个普通客户端连到MySQL实例上周期性执行SHOW GLOBAL STATUS、SHOW VARIABLES、SHOW SLAVE STATUS这些命令把MySQL运行时的状态数据翻译成Prometheus可以抓取的metrics格式。Prometheus每隔十几秒来它这里拉一次数据之后无论是看Grafana图表还是配置告警都有了可靠的依据。这套东西适合谁适合那些不想被云厂商监控绑死、想自己掌控全链路监控的自建MySQL场景。你不需要额外购买商业监控软件也不需要改一行业务代码只要在数据库节点上多跑一个进程就能把一个黑盒的MySQL变成透明的MySQL。接下来这篇内容我会从选型、账号准备、部署方式、Prometheus对接到运行过程中我实际踩过的坑完整过一遍。1.1 exporter在整个监控链路中的位置先理清楚监控链路里几个角色的分工后面配置起来才不会糊Prometheus负责定时去各个exporter那边拉取指标数据存到本地时序数据库里。它只管取数和存数本身不太关心指标具体代表什么业务含义。mysqld_exporter部署在MySQL所在主机上扮演翻译官角色。MySQL本身没有Prometheus能原生识别的metrics接口exporter就是一个适配层把MySQL的各类状态变量转换成Prometheus的指标格式并在HTTP端口默认9104上暴露出去。Grafana从Prometheus里查数据、画图表。它不直接跟MySQL或exporter打交道。Alertmanager接收Prometheus推送过来的告警规则评估结果负责发邮件、企业微信、钉钉这类通知。整个数据流是这样的MySQL - mysqld_exporter9104端口 - Prometheus 抓取 - Grafana / Alertmanager画这个图是想强调一个关键点exporter只是一条数据管道它本身不做告警判断、不存历史数据。所以排查问题的时候别只盯着一端看链路里任何一环断了都可能表现为监控没数据。1.2 它到底能拿到哪些关键指标mysqld_exporter采集的指标范围很广我按实际用途整理成几类指标类别典型指标名实际能回答的问题运行状态mysql_up、mysql_global_status_uptimeexporter能不能连上MySQLMySQL活了多久连接类mysql_global_status_threads_connected、mysql_global_variables_max_connections现在有多少连接还剩多少连接额度查询类mysql_global_status_queries、mysql_global_status_slow_queriesQPS是多少慢查询有没有在涨InnoDB引擎mysql_global_status_innodb_buffer_pool_read_requests、mysql_global_status_innodb_buffer_pool_reads缓冲池命中率多高是不是一直在走磁盘读复制类mysql_slave_status_seconds_behind_master老版本或 mysql_slave_status_...从库延迟多少秒IO线程/SQL线程是否正常临时表与排序mysql_global_status_created_tmp_tables是不是产生了大量磁盘临时表流量统计mysql_global_status_bytes_received、mysql_global_status_bytes_sent数据库网络吞吐量大致走势这些指标配合PromQL的rate、histogram_quantile这类函数可以做很多衍生计算。比如用rate(mysql_global_status_queries[1m])就能算出近一分钟的平均QPS用(mysql_global_status_innodb_buffer_pool_read_requests - mysql_global_status_innodb_buffer_pool_reads) / mysql_global_status_innodb_buffer_pool_read_requests就能算缓冲池命中率。没有exporter这些数据你得靠手工敲命令才能看到而且敲的时候往往是已经出事的时候历史趋势完全无从谈起。1.3 为什么不用现成的数据库管理工具做MySQL监控不止一种方案。云厂商RDS自带监控看板Percona Monitoring and ManagementPMM也很好用通用监控工具Zabbix也能采集MySQL状态。那为什么还要自己搭一套mysqld_exporter我个人的经验是云厂商自带监控往往粒度不够比如看不到某个特定InnoDB指标的原始值或者只能保留最近几天数据想拉长到半年就为难了。PMM确实强大但它对部署环境有要求组件偏重对于只需要核心指标、希望轻量接入现有Prometheus体系的环境来说有点杀鸡用牛刀。Zabbix走的是另一套生态采集器要主动连MySQL做告警和可视化时和Prometheus这套社区的成熟面板、告警规则衔接得并不顺滑。mysqld_exporter的优势在于三点轻单个二进制文件或一个小容器资源占用可以忽略不计。和Prometheus天生一对抓取模式由Prometheus主导exporter无状态扩展多个实例就是多配几个targets的事。社区生态成熟Grafana上有大量现成的MySQL监控面板模板告警规则也到处能找到参考不需要从零开始摸索。当然它也有短板最大的短板就是不采集慢查询日志的具体内容、不采集表级别的热度统计。如果需要这些精细化数据通常得搭配Performance Schema相关的exporter或者额外工具来做。但就日常实例级健康度巡检而言mysqld_exporter是性价比最高的起点。2. 正式部署前的三个前置决策很多人部署exporter的习惯是下载二进制、填两行配置、启动、完事。我一开始也这么干后来发现有几个前置决策没做好后面会反复返工。所以在动手之前先把这三件事定下来。2.1 版本选择别盲目追新也别困在老版本mysqld_exporter的版本演进有几个关键节点我按实际维护经验给出建议0.14.x之前的版本参数风格偏老很多采集器默认开启性能开销略大配置文件传入方式也比较局限。0.15.0版本是一个重要分水岭。默认采集行为做了调整一些高开销的采集器比如InnoDB status解析不再默认启用命令行参数也变了比如--config.my-cnf和--mysqld.address这两个参数被明确保留和强化。0.16.x及以后整体更现代化对MySQL 8.0的兼容性更好安全处理也更有保障。我的建议是如果MySQL版本在5.7到8.0之间直接选0.15.1或更新的稳定版本不要用太老的0.13、0.14。老版本不是不能用而是它们在处理MySQL 8.0的认证插件、新权限模型时容易出问题排查起来比你升级版本更花时间。MySQL版本方面mysqld_exporter对5.6、5.7、8.0都支持得不错社区有人说8.0的某些隐藏变量采集不全实际影响很小核心状态指标都能拿到。Prometheus版本则几乎没有限制2.x全系列都能兼容。2.2 MySQL账号怎么建最安全最小权限原则exporter需要连接MySQL执行一堆SHOW命令但它绝不需要任何写权限、不需要读取业务表数据。所以数据库账号必须遵循最小权限原则别图省事直接用root。我的标准建账号语句如下CREATE USER mysqld_exporterlocalhost IDENTIFIED BY 此处填一个强密码; GRANT SELECT, PROCESS, REPLICATION CLIENT ON *.* TO mysqld_exporterlocalhost; GRANT SHOW DATABASES ON *.* TO mysqld_exporterlocalhost; FLUSH PRIVILEGES;逐个解释一下这三个权限为什么是必需的SELECT很多状态指标实际上是查系统表拿到的比如information_schema下的表统计需要SELECT权限。PROCESSSHOW PROCESSLIST要用到很多连接数、线程相关的指标依赖它。REPLICATION CLIENT主从复制的两个关键指标IO线程是否正常、SQL线程是否正常都靠它读取复制状态。SHOW DATABASES采集库列表相关指标时要用到。如果你不需要这个指标也可以不授。特别提醒一个MySQL 8.0的坑8.0默认的认证插件是caching_sha2_password早期版本的mysqld_exporter用的Go驱动对这个插件兼容性不好会出现连不上、报认证错误的情况。如果你用的MySQL是8.0且exporter连接时报Access denied甚至看不懂的握手错误可以先尝试把账号认证方式调整为mysql_native_passwordALTER USER mysqld_exporterlocalhost IDENTIFIED WITH mysql_native_password BY 此处填一个强密码;不过从0.15版本开始exporter对8.0默认认证的兼容已经好很多了。新建环境优先保持默认配置遇到问题再改。2.3 网络与端口规划exporter该监听到哪里默认情况下mysqld_exporter会监听在0.0.0.0:9104上这其实是个安全隐患。谁拿到这个端口就能看到你所有MySQL状态指标更重要的是这些metrics里包含一些运行细节被不该看到的人看到总是不太好。我的建议是分两类场景Prometheus和MySQL同机部署直接用--web.listen-address127.0.0.1:9104只监听本机回环地址最安全。Prometheus在另一台机器上exporter可以监听内网IP但防火墙别对公网开9104。如果公司网络环境允许也可以继续监听127.0.0.1然后通过反向代理或者ssh隧道让Prometheus来访问——不过大部分团队嫌麻烦直接用内网IP就够。另外有个小细节exporter所在的主机iptables/安全组一定要放行Prometheus服务器到9104端口的入站流量。我遇到过排了半小时查不出指标为什么拉不到最后发现是阿里云安全组只放行了3306没放行9104这个坑后面细说。3. 两种部署方式实操二进制与容器前置决策做完接下来就是动手环节。我分别给出二进制和容器两种部署方式任选其一即可。生产环境我推荐二进制systemd托管简单直接、排障方便如果你们的MySQL本身就是容器化的那当然是走Docker Compose更顺手。3.1 二进制部署从下载到systemd托管先去GitHub Releases页面下载适合你系统架构的二进制包。以Linux amd64为例cd /opt wget https://github.com/prometheus/mysqld_exporter/releases/download/v0.15.1/mysqld_exporter-0.15.1.linux-amd64.tar.gz tar xzf mysqld_exporter-0.15.1.linux-amd64.tar.gz mv mysqld_exporter-0.15.1.linux-amd64 mysqld_exporter cd mysqld_exporter然后写配置文件。mysqld_exporter读取MySQL连接信息的常见方式是.my.cnf它就是MySQL客户端常用配置文件的格式[client] host127.0.0.1 port3306 usermysqld_exporter password你的强密码这个文件权限最好收紧因为里面有数据库账号密码chmod 600 /opt/mysqld_exporter/.my.cnf接着用systemd托管进程创建/etc/systemd/system/mysqld_exporter.service[Unit] DescriptionPrometheus MySQL Exporter Afternetwork.target Wantsnetwork-online.target [Service] Typesimple Usermysql_exporter Groupmysql_exporter WorkingDirectory/opt/mysqld_exporter ExecStart/opt/mysqld_exporter/mysqld_exporter \ --config.my-cnf/opt/mysqld_exporter/.my.cnf \ --web.listen-address0.0.0.0:9104 \ --collect.info_schema.processlist \ --collect.info_schema.innodb_metrics Restartalways RestartSec5 [Install] WantedBymulti-user.target上面的User和Group建议单独创建一个系统账号运行不要用root跑exporteruseradd -r -s /sbin/nologin mysql_exporter chown -R mysql_exporter:mysql_exporter /opt/mysqld_exporter启动并设置开机自启systemctl daemon-reload systemctl enable --now mysqld_exporter systemctl status mysqld_exporter看到active (running)之后先本机验证一下curl http://127.0.0.1:9104/metrics | head能输出一堆Prometheus格式文本就说明exporter起来了。接着看关键指标curl http://127.0.0.1:9104/metrics | grep mysql_up如果结果是mysql_up 1说明exporter已经成功连上MySQL如果是0说明连接有问题按第5节的排查链路走。3.2 Docker容器部署一条命令跑起来容器化部署更省心适合已经有容器编排体系的团队。用docker run直接跑docker run -d \ --name mysqld-exporter \ --network host \ -e DATA_SOURCE_NAMEmysqld_exporter:你的密码(127.0.0.1:3306)/ \ -e MYSQLD_EXPORTER_PASSWORD你的密码 \ prom/mysqld-exporter:v0.15.1使用--network host的原因exporter需要访问本机3306端口同时要被Prometheus访问9104端口如果用默认bridge网络端口映射会绕一道而且在容器里访问宿主机IP时不时出一些诡异问题。直接host网络最省事但要注意容器跟宿主机共享网络命名空间端口冲突风险由你自己把握。DATA_SOURCE_NAME就是官方支持的连接串格式它和.my.cnf是两种等效的连接信息传递方式。如果你二选一我建议容器场景用DATA_SOURCE_NAME二进制场景用.my.cnf这俩不要混着传混了容易产生为什么我改了配置不生效的困惑。如果你用的是Docker Compose可以用环境变量的方式version: 3 services: mysqld-exporter: image: prom/mysqld-exporter:v0.15.1 container_name: mysqld-exporter network_mode: host environment: - DATA_SOURCE_NAMEmysqld_exporter:你的密码(127.0.0.1:3306)/ restart: always3.3 配置文件和行为参数你需要知道的关键项除了连接信息exporter还有一批采集行为控制参数这直接决定你能看哪些指标和性能开销多大。新人在这一步容易犯的错是所有配置都用默认值结果要么指标不全、要么采集开销大得吓人。几个常用参数我列一下参数作用我的建议--config.my-cnf指定.mysqld_exporter配置文件路径二进制部署必填--web.listen-addressexporter监听地址和端口默认:9104生产建议绑定具体IP--collect.info_schema.processlist采集processlist信息需要连接监控时打开注意开销--collect.info_schema.innodb_metrics采集InnoDB Metric信息诊断InnoDB问题时才开--collect.engine_innodb_status解析SHOW ENGINE INNODB STATUS比较重不要默认开--web.telemetry-pathmetrics路径默认/metrics一般不用改特别说下--collect.engine_innodb_status这个采集器会额外执行SHOW ENGINE INNODB STATUS并把完整输出解析成指标开销不小而且解析出来的很多指标其实用得不多。如果你在意的只是缓冲池命中率那类基础指标这个采集器关掉完全没问题。我在自己管理的低配MySQL上默认只开processlist采集InnoDB metrics和engine status都不开除非某天做专项排查才临时打开。4. 接入Prometheus抓取配置与验证链路exporter启动只是完成了数据生产环节真正让监控生效的是Prometheus端把它纳入抓取目标。这一节讲配置、验证以及告警规则。4.1 Prometheus的scrape配置在Prometheus的配置文件通常是prometheus.yml的scrape_configs下新增一段scrape_configs: - job_name: mysql static_configs: - targets: [127.0.0.1:9104] labels: instance: mysql-primary如果你的Prometheus托管在另一个节点targets里填exporter所在机器的内网IP比如[192.168.1.10:9104]。如果有多个MySQL实例就在targets里加多组地址或者分成多个job来区分集群环境。多实例场景我建议用labels区分角色拉取拓扑而不是在targets地址里做文章。比如一主一从你可以在同一个job下配两个target用labels标上role: primary和role: replica- job_name: mysql static_configs: - targets: [192.168.1.10:9104] labels: instance: db-primary role: primary - targets: [192.168.1.11:9104] labels: instance: db-replica role: replica改完配置后promtool check config prometheus.yml curl -X POST http://localhost:9090/-/reloadPrometheus会平滑加载新配置不需要重启进程。这一步很多人不知道每次改完配置就重启Prometheus其实没必要/-/reload接口就是干这个用的。4.2 指标验证与Grafana快速可视化配置好之后别急着画图先验证数据真的在进Prometheus。在Prometheus的Web界面里打开Status - Targets能看到mysql这个job是UP状态说明抓取成功。也可以直接去Prometheus的查询框里验证mysql_up返回1就说明链路已经通了。想确认到底有没有指标进来再查一下mysql_global_status_threads_connected这个值应该跟你用SHOW STATUS LIKE Threads_connected看到的数字一致。两边对不上说明exporter连的可能不是你以为的那台MySQL实例——比如配置文件里host写成了localhost但MySQL socket和TCP解析不一致之类。Grafana可视化就简单多了。打开GrafanaDashboards - Import输入面板ID7362这个被称为MySQL Overview的面板是社区最经典的一套覆盖连接数、查询量、缓冲池、复制状态等核心指标基本满足日常巡检需求。加载之后把Prometheus数据源选对图表就能出来了。我个人在这个面板基础上会再补一个主从延迟的单独看板因为MySQL复制告警在所有监控告警里优先级通常最高单独拉一块出来盯比较清晰。4.3 一套可以起步使用的告警规则监控配好了下一步就是把告警配置好。告警规则文件我用一个单独的文件比如mysql_alerts.yml然后在prometheus.yml里引用rule_files: - mysql_alerts.yml下面是我环境里实际在用的告警规则按优先级排列groups: - name: mysql_alerts rules: - alert: MySQLInstanceDown expr: mysql_up 0 for: 1m labels: severity: critical annotations: summary: MySQL 实例 {{ $labels.instance }} 已停止采集 - alert: MySQLHighConnections expr: mysql_global_status_threads_connected / mysql_global_variables_max_connections 0.85 for: 5m labels: severity: warning annotations: summary: MySQL 连接数超过 85% - alert: MySQLSlowQueriesIncreasing expr: rate(mysql_global_status_slow_queries[5m]) 10 for: 5m labels: severity: warning annotations: summary: MySQL 慢查询速率超过 10/秒 - alert: MySQLReplicaLag expr: mysql_slave_status_seconds_behind_master 30 for: 1m labels: severity: critical annotations: summary: MySQL 从库延迟超过 30 秒注意老版本指标名称可能略有差别比如从库相关的指标在0.15版本之前叫mysql_slave_status_seconds_behind_master新版本名称可能有变化。配置告警前可以先在Prometheus里执行mysql_slave_status_这个前缀的查询确认实际指标名再套公式。5. 跑起来之后你一定躲不掉的坑部署完成只是起点真正考验的是后续稳定性。这一节我把这几年维护mysqld_exporter踩过的坑集中列一下大部分都是看起来没毛病但就是不出数据的典型。5.1 mysql_up 0时的排查链路mysql_up这个指标是exporter自己反馈的MySQL连接健康状态。如果Prometheus里查出来它是0或者Grafana面板上MySQL实例显示红色按下面这个顺序排查别乱猜排查顺序检查点怎么查1exporter进程本身活着吗systemctl status mysqld_exportercurl http://127.0.0.1:9104/metrics2本地curl能不能出指标能-跳第3步不能-看exporter日志多半是配置路径或端口问题3MySQL账号密码对吗在MySQL本机执行mysql -umysqld_exporter -p -h127.0.0.1 -e select 1看能不能通4权限齐全吗用该账号执行SHOW GLOBAL STATUS;看是否报权限错误5MySQL 8.0认证插件问题尝试ALTER USER ... IDENTIFIED WITH mysql_native_password BY ...6防火墙/安全组放行了吗在Prometheus服务器执行telnet 192.168.1.10 9104这六步走完95%的问题都能定位。我记忆最深的一次是第6步卡了两个小时——本地curl一切正常Prometheus一拉就是connection refused最后发现是云防火墙规则到期了。从那次以后我给自己定了个规矩任何agent类组件部署完第一件事不是看进程而是在采集端手动telnet一下目标端口。5.2 高开销采集器性能杀手要谨慎开关exporter本身很轻但它采集MySQL数据时执行的命令并不都是免费的。其中开销最大的几个--collect.engine_innodb_status执行SHOW ENGINE INNODB STATUS并解析涉及大量文本解析频繁采集会额外增加MySQL CPU消耗。--collect.info_schema.processlist执行SELECT * FROM information_schema.processlist如果连接数多、每个连接又有长SQL这个查询本身会有点重而且采集频率过高MySQL的回答会把随机读拉高。--collect.info_schema.innodb_metrics读一堆InnoDB计数器系统库表查询量变大。这些采集器默认都是关闭的只有在你明确需要相关指标时才打开而且采集间隔可以拉长。Prometheus端可以通过配置每个job的scrape_interval来控制比如把默认的15s改成30s或60s都算合理。我见过一个团队把所有采集器全开每5s拉一次结果MySQL的慢日志里频繁出现SHOW ENGINE INNODB STATUS然后他们反过来怀疑是exporter导致数据库变慢。其实不是exporter本身重是配置策略错了。监控系统的第一原则是不干扰被监控系统用15s甚至30s的间隔看趋势完全够没必要追求秒级。5.3 多实例、主从切换和标签管理的实战建议如果你的环境是一主多从或者有定期切换的机制有几个细节值得提前设计好。关于多实例每个MySQL实例单独跑一个exporter进程或者同机多实例时给不同实例绑定不同端口和不同配置文件。比如3306实例用9104端口3307实例用9105端口。Prometheus端targets列表把它们分清楚labels统一命名规范比如clusterprod-mysql、rolereplica。关于主从切换如果用了VIP或者域名访问MySQLprometheus targets里到底填什么地址这是个需要想清楚的问题。填VIP地址切换后exporter从库变主库但指标仍然在同一个target下标签含义就会变味填实体IP切换后VIP漂移但Prometheus还指着旧IP数据就断了。我的经验是如果切换不频繁、你对告警容忍度高就用VIP实体IP双target的方式——VIP那个target负责当前对外服务的实例监控实体IP那个负责物理机层面MySQL进程状态监控。切换后人工在Prometheus里更新一下实体IP对应的labels就行。关于标签不要用exporter输出内容作为label区分因为同一个exporter只连一个MySQL实例它的连接信息在配置里写死了。标签应该在Prometheus配置的targets里定义这样告警、Grafana筛选都方便。采集配置之外还有一个习惯我想多说一句部署完exporter后先在Prometheus里把那几个核心指标拉出来看两天走势比如mysql_global_status_threads_connected、mysql_global_status_innodb_buffer_pool_read_requests、mysql_slave_status_seconds_behind_master。为什么要看因为你得先建立这套库的正常基线值——连接数平时是200还是2000缓冲池命中率是99.9%还是只有90%不同业务完全不同。没有基线概念后面告警阈值只能靠拍脑袋拍低了天天误报拍高了出事不报。我自己的习惯是把这个观察期至少留一周阈值确认后再正式接入Alertmanager。监控系统从来不是装完就结束它跟DB维护一样是要持续调优的。
返回列表