做服务器监控这事,我一直觉得不需要一上来就上太重的东西。早年间我试过 Zabbix、Nagios,装完那叫一个折腾,各种依赖、Web 界面、Agent 部署,等配完都快忘了自己想监控什么。后来做业务运维的时间长了,慢慢发现一个道理:监控系统自己先别成为负担。能在一台 CentOS 7 上用小半天时间搭起来、能解决 80% 日常巡检需求的方案,才是最实用的。今天这篇就把我常用的这套 MySQL + Supervisor + LVM 组合拳完整拆开讲,从分区规划到数据库存储,再到进程守护,一条龙给你捋清楚。
1. 这个监控方案的整体思路与组件分工
这套东西不是拿三件套拼凑出一个看起来很高端的架构,而是它们各自都有不可替代的位置。你要先理解每个组件到底在监控体系里扮演什么角色,后面部署才不会沦为“装了个软件”。
1.1 为什么自建监控而不是直接用 Zabbix/Prometheus
说句实话,Zabbix 确实强大,Prometheus 更是当前云原生监控的主流。但问题在于,很多场景下你只需要监控三五台机器,甚至就是一台重要的数据库服务器。为这种规模引入一套完整的监控平台,光是维护成本就够喝一壶的。Zabbix 依赖 LAMP 环境,Prometheus 虽然轻一些但生态组件多,Grafana 面板要调好看也得花时间。
自建方案的核心逻辑是“小而美”:监控数据的采集用 shell 脚本,存储用 MySQL,进程守护交给 Supervisor,磁盘弹性交给 LVM。全部是我们日常运维中已经很熟悉的技术栈,出了问题好排查,逻辑透明,不依赖厂商。对中小团队、个人站长、以及刚接手服务器运维的新手来说,这套方案能让你真正理解监控数据的流转过程,而不是在黑盒里操作。
1.2 三个核心组件在监控体系中的定位
先说 LVM,它解决的是存储层的弹性和可靠性问题。监控系统跑久了,最尴尬的事就是磁盘满了导致监控数据写不进去——监控系统自己不监控自己。LVM 的逻辑卷可以在线扩容,还能做快照备份,这两点对长时间运行的监控数据库来说太重要了。
MySQL 在这里承担的角色是监控数据的存储端。你可能觉得用文件存监控数据不就行了?问题是当你有多个采样指标、要按时间维度做趋势分析的时候,文件方案根本没法查。MySQL 用一张表把所有采样数据存下来,一条 SQL 就能查出某个时间段 CPU 的趋势,还能配合定时任务做数据清理,这种灵活度不是文件能比拟的。
Supervisor 的职责是守护采集脚本的持久运行。监控脚本跑在前台会随着终端关闭而退出,用 crontab 虽然可以每分钟执行一次,但脚本执行时间重叠的问题很麻烦,而且没有进程状态可视化管理。Supervisor 用 Python 写的,运行稳定,配置简单,能把脚本的启动、停止、重启、日志输出全部管起来。谁挂了立刻拉起,还会发通知。
1.3 一条完整的监控数据流转链路
把三个组件串起来看整个流程就很清晰了:Supervisor 守护的采集脚本每隔固定时间收集一次系统状态数据(CPU、内存、磁盘、MySQL 运行状态),然后通过 SQL 语句写入 MySQL 数据库;LVM 在底层支撑 MySQL 数据目录的动态扩容,保证存储空间不会成为瓶颈;当你需要查看历史趋势或排查问题时,登录 MySQL 直接查询监控数据表即可。
这就像是一条流水线:采集端(脚本)负责感知服务器的“体温”,存储端(MySQL)负责留下“病历”,守护端(Supervisor)确保“医生”时刻坚守岗位,底层(LVM)保证“病历库”永远不会因为空间不足而关门。理解了这个链路,后面每一个部署步骤你都会知道自己在干什么。
2. 基础准备:CentOS 7 系统与网络配置
谈监控方案之前,得先把场子搭好。CentOS 7 虽然已经进入维护周期的尾声,但在现网中存量依然巨大,很多生产环境跑的就是 7.9 版本。这里我以最小化安装的 CentOS 7.9 为例,把系统层面的准备工作一条条说清楚。
2.1 镜像选择与安装阶段的分区规划
镜像强烈建议用 CentOS-7-x86_64-Minimal-2009.iso,这是 7 系列的最后一个小版本,也是我用的最稳的一个。下载时优先选国内镜像站,比如清华 TUNA 或者阿里云,速度比官网快很多。如果你的机器配置不高,安装时直接把“软件开发工具”和“兼容性库”两个组包勾上,别的都不用选,最小化安装就够。
安装阶段最重要的其实是分区。很多运维朋友装完系统之后才想起来磁盘不够用,结果只能重新搞。用 LVM 方案就不要选择自动分区,要手动划分配置。我的建议是:/boot 分区给 1GB,swap 按内存的 1 到 2 倍给,剩下的全部划给 / 根分区,并把根分区直接建立在 LVM 逻辑卷上。这样后续不管是扩容根目录还是单独切出数据卷,都有很大的操作空间。
具体操作时,在安装界面的“分区方案”里选择“LVM 标准分区”,然后创建分区的挂载点选 / ,设备类型选“LVM”,卷组名称起个容易认的,比如 vg_root。你不用在一开始就把所有空间都分配给根目录,可以预留一部分空间不分配,等系统装完再通过后面的步骤动态创建数据卷给 MySQL 用。这个习惯真的建议养成,磁盘规划一定要留一手。
2.2 网络配置与防火墙放行
CentOS 7 最小化安装后默认网卡是 DHCP,生产环境建议固定 IP。修改 /etc/sysconfig/network-scripts/ifcfg-ens192(网卡名用 ip addr 查看),把 BOOTPROTO=dhcp 改成 static,然后追加 IPADDR、NETMASK、GATEWAY、DNS1、DNS2。改完记得 systemctl restart network 重启网络,不生效就检查是不是 NetworkManager 管理着网卡。
防火墙这一块,很多教程一上来就让你 systemctl stop firewalld,这在生产环境是大忌。正确做法是按需放行端口:监控方案涉及的 MySQL 端口 3306、Supervisor Web 管理端口 9001,以及 SSH 的 22 端口。一条命令搞定:
firewall-cmd --permanent --add-port=3306/tcp firewall-cmd --permanent --add-port=9001/tcp firewall-cmd --reload还有 SELinux,如果 MySQL 绑定非默认端口或者用非标准目录存放数据,就得要调整 SELinux 策略。新手建议先把 SELinux 设成 permissive 模式练手,用 setenforce 0 临时生效,改 /etc/selinux/config 里的 SELINUX=permissive 持久生效。等运维水平上来了再折腾 enforcing 的定制策略。
2.3 时间同步与基础依赖组件
监控数据的准确性完全依赖系统时间的准确性,这一步忽略的人最多。没有时间同步,你的采样时间戳全乱套,趋势分析直接废掉。CentOS 7 自带 chronyd,启动它并设为开机自启:
systemctl enable chronyd --now chronyc sources -v看到带 * 号的时间源就说明同步成功了。如果你的环境不能访问外部时间源,至少要保证同一局域网内各机器时间一致,可以搭建内部 NTP 服务器。
基础依赖组件方面,MySQL 官方仓库和 Supervisor 安装过程需要下面这些工具,提前装上省得半路报错:
yum install -y wget vim net-tools unzip epel-release yum update -y其中 epel-release 是必装的,Supervisor 在 EPEL 源里有现成的包,不需要用 pip 从源码装那么麻烦。
3. LVM 分区规划设计:从 PV、VG 到 LV
LVM 这套机制,简单理解就是传统分区的“升级版”。传统分区把一个硬盘切成几块,切完之后想要调整大小几乎不可能;LVM 则把物理分区(PV)先聚合成一个资源池(VG),再从这个池子里按需划出“虚拟分区”(LV)给系统用。用了 LVM,磁盘就像变成了乐高积木,想加一块就拼一块,想分一块就切一块。
3.1 安装时如何规划好 LVM 卷组
结合监控场景,我建议安装系统时做如下 LVM 布局。把 /、/home、swap 全部放进一个名为 vg_root 的卷组,其中 / 分配 80% 左右的容量,/home 分配 10%,swap 按实际内存来。如果你需要单独存放 MySQL 数据,可以预留一部分空间不分配,之后直接从 vg_root 里 lvcreate 卷组切割出来挂载到 /data 或者直接用 MySQL 默认的 /var/lib/mysql。
有个非常实用的习惯,给 LV 命名的时候按用途来。比如 lv_root、lv_home、lv_mysql、lv_backup。这样你在执行 lvdisplay 或者 lvs 命令时,一眼就能知道每个逻辑卷是干什么的。当年我接手一台服务器,发现逻辑卷名叫 lvol0、lvol1,根本对应不上服务,排查问题都得先做一轮“考古”。
3.2 给 MySQL 数据盘扩容的完整操作流程
服务器跑监控一段时间之后,MySQL 数据文件增长导致磁盘空间告急,这是最常见的事件。LVM 扩容的流程在掌握了核心概念之后其实非常简单。核心就是几个命令:pvcreate 把新磁盘变成 PV,vgextend 把 PV 加入 VG,lvextend 把空间分配给 LV,xfs_growfs(或者 resize2fs)让文件系统真正“吃下”新增空间。
假设你现在 vg_root 卷组空间不足了,新加了一块 100GB 的磁盘 /dev/sdb,操作如下:
# 创建 PV pvcreate /dev/sdb # 扩展 VG vgextend vg_root /dev/sdb # 扩展 LV(给 MySQL 数据卷增加 50GB) lvextend -L +50G /dev/vg_root/lv_mysql # 如果文件系统是 XFS: xfs_growfs /data/mysql # 如果文件系统是 EXT4: resize2fs /dev/vg_root/lv_mysql注意的一点是,xfs_growfs 后面跟的是挂载点,而 resize2fs 后面跟的是设备路径,这个区别踩坑的人太多了。lvextend 加 -r 参数可以让文件系统自动扩展,但我个人习惯手动分步操作,每一步验证通过再走下一步,因为直接 -r 的话如果文件系统类型异常,日志很不好排查。
扩展完 vgdisplay 和 df -h 一对比,物理空间增大了,挂载点可用空间也增大了,整个过程完全在线完成,服务零中断。这就是 LVM 相比传统分区最让人放心的地方。
3.3 缩容操作的正确姿势与风险提示
比扩容更棘手的是缩容。缩容操作建议只在测试环境或者停机维护窗口内做,而且顺序特别讲究:先缩小文件系统,再缩小逻辑卷,顺序绝不能反。操作不当直接损坏数据,这和你用分区工具改分区表失败的结果是一样的。
# 1. 卸载需要缩容的文件系统(必须停掉相关服务) umount /data/mysql # 2. 检查文件系统 e2fsck -f /dev/vg_root/lv_mysql # 3. 缩容文件系统(EXT4 示例,缩到 50G) resize2fs /dev/vg_root/lv_mysql 50G # 4. 缩容逻辑卷 lvreduce -L 50G /dev/vg_root/lv_mysql # 5. 重新挂载 mount -a这里要特别强调,XFS 文件系统不支持在线缩容,缩容操作只适用于 ext 系列,而且缩容后你的逻辑卷大小一定不能小于文件系统内已有数据量,否则文件系统损坏,哭都来不及。生产环境我基本上只做扩容不做缩容,宁可让空间“浪费”着,也不要冒险动有业务的卷。
3.4 LVM 快照在监控方案中的应用
除了扩容,LVM 还有一个杀手级功能——快照。快照可以在秒级生成一个逻辑卷在某时间点的“照片”,在这个时间点之后对该卷的修改不会影响快照里的数据。这对监控方案有什么用?
试想一个场景:MySQL 数据目录在 /var/lib/mysql,你打算对监控数据库做一次数据导出备份,但直接备份正在写数据的库会导致数据不一致。利用 LVM 快照,先对 lv_mysql 做快照,然后挂载快照卷进行备份,期间 MySQL 可以继续运行,备份出来的数据是一致且完整的。
lvcreate -L 10G -s -n snapshot_mysql /dev/vg_root/lv_mysql mkdir -p /mnt/snapshot mount /dev/vg_root/snapshot_mysql /mnt/snapshot # 备份 /mnt/snapshot 下数据 umount /mnt/snapshot lvremove /dev/vg_root/snapshot_mysql快照空间的大小需要预估一下,快照只记录差异数据,写操作多的话快照空间会消耗很快,满了快照就失效了。
4. MySQL 安装配置:存储监控数据的基础设施
存储监控数据用 MySQL,听起来有点“大炮打蚊子”,但你要是真想自己控制监控体系,MySQL 的灵活性和查询能力是文件方案没法比的。而且用 MySQL 还有一个好处:你平时维护数据库的经验都能复用,比如备份、恢复、权限管理,用的都是你熟悉的那一套。
4.1 官方仓库安装 MySQL 8.0 的完整步骤
CentOS 7 默认源里带的 MySQL 版本太老,我建议直接用 MySQL 官方 YUM 仓库安装 8.0 版本。这一步要注意,官方仓库在安装前还需要源里的 gpg key 校验,网络环境不好的机器很容易卡在这里。
# 下载官方仓库 wget https://dev.mysql.com/get/mysql80-community-release-el7-5.noarch.rpm # 安装仓库信息 rpm -ivh mysql80-community-release-el7-5.noarch.rpm # 安装 MySQL 服务器 yum install -y mysql-community-server # 启动并设置开机自启 systemctl enable mysqld --now安装完成后 MySQL 会生成一个临时密码,在 /var/log/mysqld.log 里,用 grep 'temporary password' 找到它。然后登录数据库,马上修改密码。MySQL 8.0 默认装了密码校验插件 validate_password,要求密码包含大小写字母、数字和特殊字符,长度至少 8 位。如果嫌麻烦,可以在 /etc/my.cnf 里加一行禁用:
[mysqld] validate_password.policy=LOW validate_password.length=6不过监控库的数据价值高,我还是建议保留强密码策略,配置好权限只允许本机和监控应用访问,也省得以后被安全扫描扫出来单。
4.2 监控场景下的数据库调优参数
监控场景的数据库特点是:写入频繁、查询集中、单表数据量大。针对这几个特点,MySQL 参数要稍微调整一下。打开 /etc/my.cnf,重点关注以下几个参数:
[mysqld] # 日志大小,决定事务性能的上限 innodb_log_file_size = 256M # InnoDB 缓冲池,核心内存参数 innodb_buffer_pool_size = 1G # 允许的最大连接数 max_connections = 200 # 日志缓冲区 innodb_log_buffer_size = 16M # 数据写入磁盘的策略 innodb_flush_log_at_trx_commit = 2 # binlog 保留天数 expire_logs_days = 7innodb_buffer_pool_size 是 MySQL 内存占用的大头,建议设置为物理内存的 50% 到 70%,但要注意别和机器上别的服务抢内存,如果你是 2G 内存的小机器就给 512M 就足够了。innodb_flush_log_at_trx_commit 的默认值是 1,每次事务提交都要刷盘,安全但慢;监控数据的容忍度可以放宽,设置成 2 或者 0 能显著提升写入速度,代价是极端宕机时可能丢最近一秒的数据,对监控场景来说完全能接受。
同时要开启性能相关状态开关,方便监控脚本采集:
UPDATE performance_schema.setup_consumers SET ENABLED='YES' WHERE NAME LIKE 'events_statements%';4.3 建库建表与监控用户创建
存储监控数据,我习惯建独立的库名 monitor,表结构尽量简洁高效。核心表就三张:系统指标表、MySQL 状态表、通知记录表。系统指标表记录 CPU、内存、磁盘等基础状态,MySQL 状态表记录连接数、慢查询数、主从状态等数据库自身指标,通知记录表记录告警发送的时间和内容,方便事后复盘。
-- 创建监控库 CREATE DATABASE IF NOT EXISTS monitor DEFAULT CHARSET utf8mb4; USE monitor; -- 系统基础指标表 CREATE TABLE system_metrics ( id INT UNSIGNED NOT NULL AUTO_INCREMENT, hostname VARCHAR(64) NOT NULL COMMENT '主机名', cpu_usage DECIMAL(5,2) NOT NULL COMMENT 'CPU 使用率', mem_usage DECIMAL(5,2) NOT NULL COMMENT '内存使用率', disk_usage DECIMAL(5,2) NOT NULL COMMENT '根分区使用率', load_avg VARCHAR(32) NOT NULL COMMENT '负载均值', collect_time DATETIME NOT NULL COMMENT '采集时间', PRIMARY KEY (id), KEY idx_collect_time (collect_time) ) ENGINE=InnoDB COMMENT='系统基础指标表'; -- MySQL 性能指标表 CREATE TABLE mysql_metrics ( id INT UNSIGNED NOT NULL AUTO_INCREMENT, threads_connected INT NOT NULL COMMENT '当前连接数', slow_queries INT NOT NULL COMMENT '慢查询数', qps INT NOT NULL COMMENT '每秒查询数', buffer_pool_hit_rate DECIMAL(5,2) NOT NULL COMMENT '缓冲池命中率', collect_time DATETIME NOT NULL COMMENT '采集时间', PRIMARY KEY (id), KEY idx_collect_time (collect_time) ) ENGINE=InnoDB COMMENT='MySQL 性能指标表'; -- 告警通知记录表 CREATE TABLE alert_log ( id INT UNSIGNED NOT NULL AUTO_INCREMENT, alert_type VARCHAR(32) NOT NULL COMMENT '告警类型', alert_content VARCHAR(255) NOT NULL COMMENT '告警内容', status TINYINT NOT NULL DEFAULT 0 COMMENT '通知状态', create_time DATETIME NOT NULL COMMENT '触发时间', PRIMARY KEY (id) ) ENGINE=InnoDB COMMENT='告警通知记录表';这张表的设计有几个实用之处:DECIMAL(5,2) 存储百分比数值,既精确又不占空间;collect_time 建索引,历史趋势查询秒回;utf8mb4 字符集应对各种告警内容不报错。索引的建立要克制,索引能加速查询但也拖慢写入,监控表以写入为主、查询集中在时间范围,所以只给时间字段建索引就够了。
权限方面,创建独立的监控账号,权限最小化。账号只授予 monitor 库的 SELECT、INSERT、UPDATE、DELETE 权限,绝不给 GRANT 和跨库权限:
CREATE USER 'monitor'@'localhost' IDENTIFIED BY 'YourStrongPassword!'; GRANT SELECT, INSERT, UPDATE, DELETE ON monitor.* TO 'monitor'@'localhost'; FLUSH PRIVILEGES;这里的 host 一定要按实际来写,如果你的监控脚本和 MySQL 在同一台机器上,用 localhost 就行;如果要走网络采集,再按需改成具体 IP 或网段。
4.4 数据保留策略与定时清理
监控数据随时间无限增长,必须设计清理策略。我的方案是按月保留:每月月初清理三个月前的数据。不用 DELETE 一条条删,效率太低,直接按时间范围批量删除:
DELETE FROM system_metrics WHERE collect_time < DATE_SUB(NOW(), INTERVAL 90 DAY);这条语句理论上可行,但当数据量达到百万级时,直接 DELETE 会拖垮系统。更好的方式是利用分区表按月分区,或者直接开个定时任务定期用 DROP TABLE 换表的方式做轮转。
对大多数中小配置的服务器,90 天数据清一次就够,配合定时任务每月执行即可。清理任务推荐放进 MySQL 自身的事件调度器:
SET GLOBAL event_scheduler = ON; CREATE EVENT IF NOT EXISTS clean_monitor_data ON SCHEDULE EVERY 1 DAY DO BEGIN DELETE FROM system_metrics WHERE collect_time < DATE_SUB(NOW(), INTERVAL 90 DAY); DELETE FROM mysql_metrics WHERE collect_time < DATE_SUB(NOW(), INTERVAL 90 DAY); END;需要注意:DELETE 操作也会产生 binlog,主从环境下会产生大量日志,建议在离线时段(比如凌晨 4 点)执行。另外,监控数据的价值随时间递减,保留 90 天对日常排查完全够用。
5. Supervisor 部署与监控脚本编写
Supervisor 是整个体系里负责“保障运行”的组件。脚本写好了,但谁来确保它一直跑着?Supervisor 的意义在于它把你的采集脚本变成“系统服务”——可以自动启动、崩溃重启、日志持久化、状态可通过命令查看。这跟直接 crontab 丢进去是两个时代的东西。
5.1 Supervisor 的安装与核心配置
在 CentOS 7 上装 Supervisor 最简单的方式是直接用 EPEL 源:
yum install -y supervisor systemctl enable supervisord --nowEPEL 里的 Supervisor 版本是 3.x,对于常规进程守护完全够用。配置文件在 /etc/supervisord.conf,实际使用中我习惯把每个进程的配置单独放一个文件,放在 /etc/supervisord.d/ 目录下,主配置文件末尾用 include 引入:
[include] files = /etc/supervisord.d/*.ini这样每新增一个守护任务,只需在 /etc/supervisord.d/ 下新建一个 .ini 文件,再执行 supervisorctl update 即可,不用频繁改主配置文件。这个习惯在你管理的监控脚本和后台任务多起来之后,价值立竿见影。
5.2 服务器监控采集脚本的编写要点
真正的监控实战核心,其实是采集脚本本身。我写脚本的原则是不引入额外的 agent 依赖,纯 shell 加上系统自带命令就能采集到关键指标。CPU 使用率可以用 mpstat 或 vmstat,内存、磁盘、负载则用 free、df、uptime 等命令解析。
下面是我常用的采集脚本思路,核心是三个函数:采集系统指标、采集 MySQL 指标、写入数据库。
#!/bin/bash # /usr/local/bin/collect_metrics.sh MYSQL_CMD="mysql -umonitor -p'YourStrongPassword!' -h127.0.0.1 -N -e" HOSTNAME=$(hostname) TIME=$(date '+%Y-%m-%d %H:%M:%S') # 1. 采集系统指标 # CPU:使用 vmstat 获取空闲率,100 减掉即使用率 CPU_IDLE=$(vmstat 1 2 | tail -1 | awk '{print $15}') CPU_USAGE=$(echo "100 - $CPU_IDLE" | bc) # 内存:free 返回数值,输出已用内存总计占比 MEM_TOTAL=$(free -m | awk '/^Mem:/{print $2}') MEM_USED=$(free -m | awk '/^Mem:/{print $3}') MEM_USAGE=$(echo "scale=2; $MEM_USED * 100 / $MEM_TOTAL" | bc) # 磁盘:根分区使用率 DISK_USAGE=$(df -h / | awk 'NR==2{print $5}' | sed 's/%//') # 负载均值 LOAD_AVG=$(uptime | awk -F'load average:' '{print $2}') # 2. 写入系统指标表 $MYSQL_CMD "INSERT INTO monitor.system_metrics (hostname, cpu_usage, mem_usage, disk_usage, load_avg, collect_time) VALUES ('$HOSTNAME', '$CPU_USAGE', '$MEM_USAGE', '$DISK_USAGE', '$LOAD_AVG', '$TIME');"这里有些细节必须注意:vmstat 的参数写法,因为要取采样后的数据,所以后面加了“1 2”,取第二行的值;mysql 命令用 -N 参数跳过表头,省去文本解析的头疼事;所有指标做变量引用时都加了单引号,防止特殊字符破坏 SQL 语句。
MySQL 状态指标采集原理类似,从 SHOW GLOBAL STATUS 和 SHOW GLOBAL VARIABLES 中取关键值:
# 连接数 THREADS_CONNECTED=$(mysql -umonitor -p'YourStrongPassword!' -h127.0.0.1 -N -e "SHOW GLOBAL STATUS LIKE 'Threads_connected';" | awk '{print $2}') # 慢查询数 SLOW_QUERIES=$(mysql -umonitor -p'YourStrongPassword!' -h127.0.0.1 -N -e "SHOW GLOBAL STATUS LIKE 'Slow_queries';" | awk '{print $2}') # QPS 两次采样差值的平均值 QPS=$(mysqladmin -umonitor -p'YourStrongPassword!' status | awk '{print $6}') # 缓冲池命中率 HIT_RATE=$(mysql -umonitor -p'YourStrongPassword!' -N -e "SHOW GLOBAL STATUS LIKE 'Innodb_buffer_pool_read_requests';" | awk '{print $2}')读到这里你应该明白了,这个自建监控没有你不知道的黑科技,全是你在 MySQL 日常运维中肯定会用到的命令行工具。把状态值按时写入数据表后,后面的一切分析都有了可靠的数据基础。
5.3 用 Supervisor 进程守护实现脚本自动拉起
采集脚本写完还不算完,关键是如何让它每 30 秒执行一次,并保证崩了能自动恢复。Supervisor 的配置里不支持小于 1 秒的 sleep 循环控制,但我们可以让脚本自身循环执行,Supervisor 只负责守护脚本进程。
把采集逻辑包在一个无限循环里,每次执行完采集后用 sleep 控制采样间隔:
#!/bin/bash # /usr/local/bin/collect_metrics_daemon.sh while true; do /usr/local/bin/collect_metrics.sh sleep 30 done然后创建 Supervisor 配置:
[program:collect_metrics] directory=/usr/local/bin command=/bin/bash /usr/local/bin/collect_metrics_daemon.sh autorestart=true startsecs=5 startretries=10 redirect_stderr=true stdout_logfile=/var/log/monitor/collect_metrics.log stdout_logfile_maxbytes=100MB stdout_logfile_backups=5这里 set autorestart=true 的作用是,无论任何原因导致脚本挂了,Supervisor 都会自动拉起。startsecs=5 表示进程持续运行 5 秒以上才认为是启动成功,防止“反复重启死循环”。日志配置可以防止日志无限膨胀,这个在长期运行中特别重要。
配置完成后:
supervisorctl reread supervisorctl update supervisorctl status看到 collect_metrics 的状态是 RUNNING,就说明守护成功。你可以试一下 kill 进程,观察 supervisorctl status 里进程会自动重启,这就是 Supervisor 守护职责的实战验证。
5.4 告警阈值与通知机制
监控的目的是在出问题时尽早发现,所以要给脚本加入阈值判断和通知逻辑。最简单的通知方式是结合 SMTP 发邮件,或者调用企业微信机器人、钉钉机器人的 Webhook 接口。
以钉钉机器人为例,先在钉钉群里添加一个自定义机器人,拿到 Webhook 地址,然后在脚本里封装一个 webhook 通知函数:
DING_MSG() { local content="$1" curl -s -H "Content-Type: application/json" -X POST "$DING_WEBHOOK_URL" \ -d "{\"msgtype\": \"text\", \"text\": {\"content\": \"[服务器监控] $content\"}}" > /dev/null }在采集脚本里加上阈值判断,比如 CPU 超过 85%、磁盘超过 90%、MySQL Threads_connected 超过上限,都触发告警:
if [ "$(echo "$CPU_USAGE >= 85" | bc)" -eq 1 ]; then DING_MSG "CPU 使用率过高: ${CPU_USAGE}%" fi if [ "$(echo "$DISK_USAGE >= 90" | bc)" -eq 1 ]; then DING_MSG "根分区使用率过高: ${DISK_USAGE}%" fi这里如果用简单的大小比较会踩坑,因为 awk 计算出的 CPU_USAGE 是浮点数,shell 的 -ge 命令不支持浮点运算,所以用了场景中很常见的 bc 命令做浮点比较。
注意告警通知要加入去重逻辑,同一个问题在持续时间内不要重复轰炸。可以在通知表里记录最近一次的通知时间,如果当前时间和上次通知时间相差不足 10 分钟就跳过,这一步能有效防止告警风暴。
5.5 数据可视化最简单的方式
存了监控数据,总得让它产生价值。全功能可视化可以以后接 Grafana,但在一开始,用命令行也可以满足查看趋势的需求。比如查询最近一小时 CPU 使用率:
SELECT collect_time, cpu_usage FROM monitor.system_metrics WHERE collect_time >= DATE_SUB(NOW(), INTERVAL 1 HOUR) ORDER BY collect_time;如果你连 Grafana 都懒得装,也可以用 MySQL 自带的方式把数据导出成 CSV,丢进 Excel 画图,效果完全够日常巡检用:
SELECT collect_time, cpu_usage, mem_usage, disk_usage FROM monitor.system_metrics WHERE collect_time >= DATE_SUB(NOW(), INTERVAL 24 HOUR) INTO OUTFILE '/tmp/metrics_24h.csv' FIELDS TERMINATED BY ',';要说最实用的场景,其实是故障复盘。服务器出问题后,你第一时间去查监控库里对应时间点的数据,CPU、负载、慢查询数全部呈现,比什么排查工具都直接。
6. 常见问题与排查技巧实录
跑了这套体系两年多,我把真正踩过的高频问题整理成一份速查表,希望能帮你少走几周弯路。
6.1 高频问题排查对照表
| 症状 | 可能原因 | 排查思路 |
|---|---|---|
| Supervisor 启动时报错 /var/run/supervisord.sock 不存在 | supervisor 服务未启动 | systemctl status supervisord,先启动服务 |
| 采集脚本手动执行正常,但 Supervisor 启动后无数据 | 脚本路径或环境变量问题 | 在配置里指定绝对路径 interpreter |
| mysql 命令连接报 Access denied | 密码策略太强或权限不对 | 检查用户 host 匹配、密码复杂度,用 mysql -u -p 手动验证 |
| lvextend 后磁盘容量没变化 | 文件系统未扩展 | xfs 用 xfs_growfs / 挂载点,ext4 用 resize2fs 设备路径 |
| LVM 快照挂载后数据不一致 | 快照前 MySQL 有未刷盘的缓冲数据 | 最好通过 mysqldump 或 xtrabackup 做一致性备份 |
| 监控数据表越来越大,查询变慢 | 缺少时间索引或数据未清理 | 补建 collect_time 索引,开启自动清理事件 |
| 服务器重启后 Supervisor 没有拉起脚本 | Supervisor 开机自启没设 | systemctl enable supervisord,检查配置文件无语法错误 |
| curl 发 Webhook 通知超时 | 外网网络不稳定或 Webhook 地址不通 | 手动 curl 测试,加超时参数 --connect-timeout 5 |
6.2 我踩过的两个印象深刻的坑
第一个坑是刚部署 Supervisor 时,脚本起不来,日志里报权限错误。排查了半天发现是脚本目录 /usr/local/bin 下没有给执行权限。Supervisor 的 command 指定 /bin/bash 调用脚本其实不受执行权限影响,但我犯的错是在 command 里直接写了脚本路径且没加 bash 前缀,导致文件没有 x 权限时直接被拒绝。现在我的配置一律写成 /bin/bash /path/to/script.sh,顽固依赖彻底规避。
第二个坑是 MySQL 扩容。有一次监控库的机器磁盘告警,我满怀信心地 lvextend -L +20G,执行完 xfs_growfs 后 df -h 一查,容量一点没变。查了半天才发现那块数据卷是 ext4 文件系统,xfs_growfs 根本不支持,手动确认文件系统类型后改用 resize2fs 才成功。这个低级失误提醒我,操作前必须用 blkid 或者 lsblk -f 确认文件系统类型。
6.3 安全加固建议和备份策略
监控系统虽然不直接承载业务,但它记录的是你所有服务器的健康档案,安全等级不能太低。MySQL 侧要定期修改密码,Supervisor 的 Web 管理端口(默认 9001)除非在受控内网,否则建议关闭外网访问,或者用 SSH 隧道来访问。防火墙规则要收紧,3306 和 9001 不做静态放行,用 SSH 隧道转发端口来连最稳。
数据库备份方面,监控数据虽然可以丢一部分,但运维复盘时需要历史数据,建议每天对 monitor 库做一次 mysqldump 备份,保留 14 天:
mysqldump -umonitor -p'YourStrongPassword!' monitor > /data/backup/monitor_$(date +%F).sql find /data/backup -name "monitor_*.sql" -mtime +14 -delete你可以把这条命令也写进 Supervisor 配置里,做成一个定时执行的备份程序,比 crontab 看得更清楚。
6.4 扩展思路:从 shell 到更现代的监控视野
这套自建监控方案跑通之后,你已经拥有了监控系统的骨架:存储层、采集层、守护层、提醒层。在这个骨架上做扩展非常容易。比如把采集数据输出为 Prometheus 格式,对接一个轻量级的 Prometheus 服务端和 Grafana,就能获得专业级的可视化面板;也可以把采集目标从一台机器扩展到多台机器,MySQL 库表里加一个 hostname 字段就能区分所有来源。
我个人实际运营下来的感想是:不再需要看到任何面板都觉得是黑科技了。因为监控的本质,无非是定时取数、按规则判异、通知有人的过程。自己搭过一遍,反而对那些重型监控系统有了平常心,知道它们在解决什么问题,也知道哪些问题是自己的规模根本遇不到的。
这套组合的另一个好处是后续迁移成本低。MySQL、Supervisor、LVM 都是跨平台的成熟技术,你换到 Ubuntu、换到 Debian,核心逻辑完全复用,只是包管理器的命令不同而已。最后再分享一个小技巧:玩 LVM 的时候,执行完任何变更操作之后,养成 lvs、vgs、pvs 三个命令轮番看一眼的习惯,你就能时刻掌握存储的完整状态。这套监控方案我跑了两三年,期间扛过 CPU 打满、磁盘写满、进程假死等各种故障,该报警的时候从没哑火过,值得你试试。