MySQL 监控总在重复配置?用 Shell 自动部署 mysqld_exporter,再让 Prometheus 远程抓取
前言
第一次部署 mysqld_exporter 时,下载二进制、建 MySQL 监控账号、写连接配置、补 systemd 服务,看起来都不难;真正烦的是机器一多,这些步骤就会不停重复。更麻烦的是,少一个权限、路径写错一处、服务忘了开机自启,最后往往不是"安装失败"这么直接,而是 Prometheus 抓不到指标后再倒回来排查。我更愿意把这类固定动作写进脚本,让重复部署尽量变成一套可复用流程,但同时也会保留对账号、密码、端口和权限的检查。尤其监控组件一旦开始批量铺开,真正难维护的往往不是安装本身,而是不同机器之间版本不一致、配置不一致、凭据散落和后续无法追溯,这些问题比"多敲几条命令"更麻烦。
这次我在 CentOS 7 上使用 Shell 脚本部署 mysqld_exporter v0.18.0:脚本自动安装依赖、下载程序到 /app/mysqld_exporter、创建 prometheus 系统用户和 MySQL 监控用户、生成 localhost_db.cnf、注册 systemd,并让服务监听 9105。随后先让 Prometheus 在局域网抓取 192.168.42.146:9105,再安装 cpolar,把 9105 Web 指标入口映射到公网,验证随机地址抓取,最后配置固定二级子域名 mysqll。我更看重"脚本到底自动化了哪些步骤、Prometheus 实际抓到了什么",而不是把"一键执行"直接等同于所有环境都能无脑复制。

1. 为什么把 mysqld_exporter 部署写成 Shell 脚本?
这类监控组件最适合自动化的地方,就是安装步骤高度重复。
如果每台机器都手工做一遍,通常要反复处理这些事情:
- 下载并解压 mysqld_exporter;
- 创建专用运行用户;
- 创建 MySQL 监控账号并授权;
- 写数据库连接配置;
- 设置配置文件权限;
- 创建 systemd 服务;
- 启用开机自启;
- 检查指标端口。
机器少的时候,手工还能接受;机器一多,真正消耗时间的是重复操作和后续排错。
所以我更愿意把目标定得简单一点:
脚本负责把固定部署步骤标准化,Prometheus 负责抓指标,cpolar 只在跨网络场景下提供
9105的公网访问入口。
这三层职责分开以后,后面哪一层出问题也更容易定位。
2. 这份脚本具体自动化了什么?
当前脚本使用的版本是:
v0.18.0
安装目录:
/app/mysqld_exporter
监听端口:
9105
整体流程包括:
- 检查是否以 root 运行;
- 安装
wget和tar; - 下载并解压 mysqld_exporter;
- 创建
prometheus系统用户; - 使用 MySQL root 账号测试连接;
- 创建
mysqld_exporter数据库监控用户; - 授予
REPLICATION CLIENT、PROCESS和performance_schema查询权限; - 生成
localhost_db.cnf; - 将配置文件权限设为
600; - 创建并启动
mysqld_exporter.service。
完整脚本如下:
shell
#!/bin/bash
# 一键安装 mysqld_exporter for CentOS 7
# 使用固定 MySQL root密码自动建用户
# 监控用户: mysqld_exporter / Sjixin520!
set -e
VERSION="v0.18.0"
INSTALL_DIR="/app/mysqld_exporter"
PORT="9105"
MYSQL_ROOT_USER="root"
MYSQL_ROOT_PASS="Sjixin52!"
EXPORTER_USER="mysqld_exporter"
EXPORTER_PASS="Sjixin52!"
RED='\033[0;31m'
GREEN='\033[0;32m'
NC='\033[0m'
log() { echo -e "${GREEN}[INFO]${NC} $1"; }
error() { echo -e "${RED}[ERROR]${NC} $1" >&2; exit 1; }
if [ "$EUID" -ne 0 ]; then
error "请以 root 用户运行此脚本!"
fi
# 安装依赖
log "安装必要工具..."
yum install -y wget tar &>/dev/null
# 下载mysqld_exporter
URL="https://github.com/prometheus/mysqld_exporter/releases/download/${VERSION}/mysqld_exporter-${VERSION#v}.linux-amd64.tar.gz"
log "正在下载mysqld_exporter ${VERSION} ..."
wget -O /tmp/mysqld_exporter.tar.gz "$URL" || error "下载失败"
# 解压到/app/mysqld_exporter
mkdir -p "$INSTALL_DIR"
tar -zxvf /tmp/mysqld_exporter.tar.gz -C "$INSTALL_DIR" --strip-components=1
chown -R root:root "$INSTALL_DIR"
# 确保prometheus用户存在
if ! id "prometheus" &>/dev/null; then
log "创建prometheus系统用户..."
useradd --system --shell /sbin/nologin --no-create-home prometheus
fi
# 自动创建MySQL监控用户
log "正在连接MySQL并创建监控用户 '$EXPORTER_USER' ..."
# 测试root登录是否成功
if ! mysql -u"$MYSQL_ROOT_USER" -p"$MYSQL_ROOT_PASS" -e "SELECT 1;" >/dev/null 2>&1; then
error "无法使用root / Sjixin52! 登录MySQL,请确认MySQL正在运行且密码正确"
fi
# 创建exporter用户
mysql -u"$MYSQL_ROOT_USER" -p"$MYSQL_ROOT_PASS" <<SQL
CREATE USER IF NOT EXISTS '$EXPORTER_USER'@'localhost' IDENTIFIED BY '$EXPORTER_PASS';
GRANT REPLICATION CLIENT, PROCESS ON *.* TO '$EXPORTER_USER'@'localhost';
GRANT SELECT ON performance_schema.* TO '$EXPORTER_USER'@'localhost';
FLUSH PRIVILEGES;
SQL
log "MySQL监控用户创建成功!"
# 创建.my.cnf文件
cat > "$INSTALL_DIR/localhost_db.cnf" <<EOF
[client]
user=$EXPORTER_USER
password=$EXPORTER_PASS
host=localhost
EOF
chmod 600 "$INSTALL_DIR/localhost_db.cnf"
chown prometheus:prometheus "$INSTALL_DIR/localhost_db.cnf"
# 创建systemd服务
cat > /etc/systemd/system/mysqld_exporter.service <<EOF
[Unit]
Description=MySQL Exporter
After=network.target
[Service]
Type=simple
User=prometheus
ExecStart=$INSTALL_DIR/mysqld_exporter \\
--config.my-cnf=$INSTALL_DIR/localhost_db.cnf \\
--web.listen-address=:9105
Restart=on-failure
RestartSec=10
StandardOutput=journal
StandardError=journal
[Install]
WantedBy=multi-user.target
EOF
# 启动服务
log "启动 mysqld_exporter 服务..."
systemctl daemon-reload
systemctl enable --now mysqld_exporter
log "✅ 安装完成!"
log "指标地址: http://$(hostname -I | awk '{print $1}'):${PORT}/metrics"
log "服务状态: systemctl status mysqld_exporter"
这里有一个我会特别留意的地方:脚本里直接写入了 MySQL root 和 exporter 的固定密码。
代码按当前内容原样保留,但如果后面准备把脚本放进 Git、Ansible 或多台机器批量使用,我不会把"能自动跑"当成"凭据管理已经解决"。脚本自动化的是部署流程,不是密码治理。
3. 把脚本保存成 mysql_exporter.sh
先创建脚本文件:
shell
vi mysql_exporter.sh
然后写入当前完整脚本:
shell
#!/bin/bash
# 一键安装 mysqld_exporter for CentOS 7
# 使用固定 MySQL root密码自动建用户
# 监控用户: mysqld_exporter / Sjixin520!
set -e
VERSION="v0.18.0"
INSTALL_DIR="/app/mysqld_exporter"
PORT="9105"
MYSQL_ROOT_USER="root"
MYSQL_ROOT_PASS="Sjixin52!"
EXPORTER_USER="mysqld_exporter"
EXPORTER_PASS="Sjixin52!"
RED='\033[0;31m'
GREEN='\033[0;32m'
NC='\033[0m'
log() { echo -e "${GREEN}[INFO]${NC} $1"; }
error() { echo -e "${RED}[ERROR]${NC} $1" >&2; exit 1; }
if [ "$EUID" -ne 0 ]; then
error "请以 root 用户运行此脚本!"
fi
# 安装依赖
log "安装必要工具..."
yum install -y wget tar &>/dev/null
# 下载mysqld_exporter
URL="https://github.com/prometheus/mysqld_exporter/releases/download/${VERSION}/mysqld_exporter-${VERSION#v}.linux-amd64.tar.gz"
log "正在下载mysqld_exporter ${VERSION} ..."
wget -O /tmp/mysqld_exporter.tar.gz "$URL" || error "下载失败"
# 解压到/app/mysqld_exporter
mkdir -p "$INSTALL_DIR"
tar -zxvf /tmp/mysqld_exporter.tar.gz -C "$INSTALL_DIR" --strip-components=1
chown -R root:root "$INSTALL_DIR"
# 确保prometheus用户存在
if ! id "prometheus" &>/dev/null; then
log "创建prometheus系统用户..."
useradd --system --shell /sbin/nologin --no-create-home prometheus
fi
# 自动创建MySQL监控用户
log "正在连接MySQL并创建监控用户 '$EXPORTER_USER' ..."
# 测试root登录是否成功
if ! mysql -u"$MYSQL_ROOT_USER" -p"$MYSQL_ROOT_PASS" -e "SELECT 1;" >/dev/null 2>&1; then
error "无法使用root / Sjixin52! 登录MySQL,请确认MySQL正在运行且密码正确"
fi
# 创建exporter用户
mysql -u"$MYSQL_ROOT_USER" -p"$MYSQL_ROOT_PASS" <<SQL
CREATE USER IF NOT EXISTS '$EXPORTER_USER'@'localhost' IDENTIFIED BY '$EXPORTER_PASS';
GRANT REPLICATION CLIENT, PROCESS ON *.* TO '$EXPORTER_USER'@'localhost';
GRANT SELECT ON performance_schema.* TO '$EXPORTER_USER'@'localhost';
FLUSH PRIVILEGES;
SQL
log "MySQL监控用户创建成功!"
# 创建.my.cnf文件
cat > "$INSTALL_DIR/localhost_db.cnf" <<EOF
[client]
user=$EXPORTER_USER
password=$EXPORTER_PASS
host=localhost
EOF
chmod 600 "$INSTALL_DIR/localhost_db.cnf"
chown prometheus:prometheus "$INSTALL_DIR/localhost_db.cnf"
# 创建systemd服务
cat > /etc/systemd/system/mysqld_exporter.service <<EOF
[Unit]
Description=MySQL Exporter
After=network.target
[Service]
Type=simple
User=prometheus
ExecStart=$INSTALL_DIR/mysqld_exporter \\
--config.my-cnf=$INSTALL_DIR/localhost_db.cnf \\
--web.listen-address=:9105
Restart=on-failure
RestartSec=10
StandardOutput=journal
StandardError=journal
[Install]
WantedBy=multi-user.target
EOF
# 启动服务
log "启动 mysqld_exporter 服务..."
systemctl daemon-reload
systemctl enable --now mysqld_exporter
log "✅ 安装完成!"
log "指标地址: http://$(hostname -I | awk '{print $1}'):${PORT}/metrics"
log "服务状态: systemctl status mysqld_exporter"

这里脚本文件名叫:
mysql_exporter.sh
而实际下载的二进制、systemd 服务以及当前项目名称都使用:
mysqld_exporter
两种写法在当前流程里同时存在,我按现有内容保留,不强行统一。
4. 执行脚本
保存完成以后运行:
shell
sh mysql_exporter.sh

如果整个流程能够顺利执行,脚本会依次完成依赖安装、下载、MySQL 用户创建、配置文件生成和 systemd 注册。
但我不会只看"脚本没报错"就结束。
真正关键的是再确认服务状态。
5. 检查 mysqld_exporter 服务
执行:
shell
systemctl status mysqld_exporter

当前结果显示 mysqld_exporter 服务已经成功启动。
脚本中还会输出类似:
http://主机IP:9105/metrics
的指标地址。
到这里,真正验证的是:
MySQL → mysqld_exporter → 9105 → systemd 服务正常运行。
6. 先在同一局域网里让 Prometheus 抓取
mysqld_exporter 跑起来以后,再去配置 Prometheus。
打开:
shell
vi prometheus.yml
当前示例加入:
shell
- targets: ["192.168.42.146:9105"]
labels:
app: "mysql_exporter"

这里 Prometheus 和 mysqld_exporter 不在同一台机器,所以抓取目标写的是:
192.168.42.146:9105
如果都在同一台机器,当前说明写的是可以使用 localhost。
接着通过:
IP + 9090
打开 Prometheus 页面。

当前页面已经能够看到 Prometheus 抓取到:
192.168.42.146
上的 mysql_exporter 服务。
这一层先把最基础的监控链验证清楚:
MySQL → mysqld_exporter 9105 → Prometheus 局域网抓取。
7. Prometheus 和 exporter 不在同一网络怎么办?
如果 Prometheus 和 mysqld_exporter 位于同一个局域网,直接写内网 IP 就可以。
问题出现在另一种场景:
- Prometheus 在外部服务器;
- MySQL 和 mysqld_exporter 在内网;
- Prometheus 无法直接访问
192.168.42.146:9105。
这时候真正需要开放的是:
mysqld_exporter 的指标入口 9105
而不是把 MySQL 数据库本身直接暴露出去。
cpolar 在这套方案里的职责也只有这一层:
把本地 mysqld_exporter 的
9105HTTP 指标页面提供给外部 Prometheus 访问。
数据库指标仍然由 mysqld_exporter 采集,Prometheus 仍然负责抓取和存储。
8. 安装 cpolar
执行当前安装命令:
shell
sudo curl https://get.cpolar.sh | sh

安装完成以后检查服务状态:
shell
sudo systemctl status cpolar

服务正常以后,通过主机 IP + 9200 打开 cpolar Web 管理页面。
当前页面写的是:
ip:9200
同时链接目标为:
http://localhost:9200/
实际访问时,以当前环境真正能打开的地址为准。

9. 给 mysqld_exporter 创建随机公网地址
进入【隧道管理 → 创建隧道】。
当前配置为:
- 隧道名称:
mysql_exporter - 协议:
http - 本地地址:
9105 - 域名类型:随机域名
- 地区:
China Top

创建以后,到在线隧道列表查看公网地址。

当前示例得到:
38c53143.r2.cpolar.top
接下来 Prometheus 的目标配置改为:
shell
- targets: ["38c53143.r2.cpolar.top"]
labels:
app: "mysql_exporter"

随后页面显示抓取成功。

这一层真正验证的是:
内网 mysqld_exporter 9105 → cpolar HTTP 公网地址 → 外部 Prometheus 抓取。
当前 Prometheus target 按现有配置原样写为:
38c53143.r2.cpolar.top
这里不额外补端口或改写格式。
10. 随机公网地址能用以后,再配置固定入口
随机地址适合先验证跨网络抓取。
如果 Prometheus 要长期使用同一个抓取目标,地址频繁变化就不方便继续维护。
所以当前流程继续配置固定二级子域名。
进入预留页面。

选择保留二级子域名。
当前记录为:
- 地区:
china Top - 二级子域名:
mysqll

然后回到【隧道管理 → 隧道列表】,找到:
mysql_exporter
隧道。

把域名类型改为:
二级子域名
并在:
Sub Domain
中填写已经保留的二级子域名。
地区继续选择:
China Top

更新完成以后,在线隧道列表中的随机公网地址会切换成固定二级子域名形式。

最后通过固定公网地址访问。

当前页面可以正常打开。
这里能够确认固定公网 Web 入口已经生效;但当前材料没有继续展示把 Prometheus 的 target 再改成固定二级子域名以后进行第二次抓取验证,所以我不会把这一步额外写成"固定域名 Prometheus 抓取已再次验证"。
总结
这套脚本真正省下来的,不是"敲一条命令"本身,而是把一组容易重复出错的部署步骤固定下来。
这次实际跑通的主线是:
CentOS 7 → Shell 脚本 → mysqld_exporter v0.18.0 → /app/mysqld_exporter → MySQL 监控用户 → localhost_db.cnf → prometheus 系统用户 → systemd → 9105 → Prometheus 局域网抓取 192.168.42.146:9105 → cpolar → 随机公网 38c53143.r2.cpolar.top → Prometheus 公网抓取成功 → 固定二级子域名 mysqll。
几个边界需要分清:
- 脚本自动化的是 mysqld_exporter 的安装、授权、配置和服务注册;
- Prometheus 才负责真正抓取指标;
- cpolar 只负责把
9105指标入口提供到公网,不参与 MySQL 指标采集; - 当前脚本把 MySQL root 和 exporter 密码直接写在脚本中,代码虽然按现有内容保留,但批量复用前要单独考虑凭据管理;
- 随机公网地址已经实际用于 Prometheus 抓取并显示成功;
- 固定二级子域名已经完成配置和页面访问,但现有流程没有继续展示 Prometheus 使用固定域名后的第二次抓取验证。
如果后面真的要部署很多节点,我更看重的不是"脚本能不能跑 100 台",而是版本、目录、权限、凭据和抓取目标是否都能保持一致。自动化的价值就在这里:不是省掉所有判断,而是把真正需要人工判断的部分留下,把重复动作尽量交给脚本。