Linux 系统监控 + 邮件告警实战:CPU、内存、IO、流量全覆盖
在服务器运维中,“被动救火” 往往会导致业务中断、数据丢失等严重问题。如果能实时监控系统核心资源(CPU、内存、硬盘、IO、网络),并在指标超限时自动发送告警邮件,就能将运维从 “事后处理” 转变为 “事前预警”。本文将手把手教你用 Bash 脚本实现这套监控告警系统,配置简单、轻量化,适用于各类 Linux 服务器。
一、为什么需要这套监控系统?
日常运维中,我们常遇到这些痛点:
- 服务器突然卡顿,才发现 CPU 使用率飙升到 100%;
- 硬盘满了导致服务崩溃,事前毫无察觉;
- 网络带宽被占满,用户反馈访问慢才去排查;
- 磁盘 IO 等待过高,数据库响应延迟却没及时发现。
这套监控系统能解决这些问题:
- 覆盖核心指标:CPU、内存、硬盘使用率(超 80% 告警)、IO await(超 50ms 告警)、网络流量(超 10MB/s 可自定义告警);
- 自动邮件告警:用 QQ/163/139 邮箱发送告警,包含服务器 IP、异常指标、告警时间;
- 轻量化部署:基于 Bash 脚本,无需安装复杂监控平台,适合中小服务器集群;
- 灵活可配置:所有阈值可自定义,适配不同业务场景。
二、前置准备:3 分钟搞定邮箱 SMTP 配置
要实现邮件告警,首先需要开启邮箱的 SMTP 服务(用于发送邮件),并获取授权码(替代登录密码,更安全)。以 QQ 邮箱为例,其他邮箱(163、139)步骤类似。
1. 开启 QQ 邮箱 SMTP 服务
- 登录 QQ 邮箱,点击顶部「设置」→「账户」;
- 下拉找到「POP3/IMAP/SMTP/Exchange/CardDAV/CalDAV 服务」,开启「POP3/SMTP 服务」;
- 按提示用手机 QQ 扫码验证,生成 16 位「授权码」,务必保存好(后续脚本要用)。
2. 核心邮箱参数整理
无论用哪种邮箱,最终需要这 5 个参数,整理成表格备用:
参数名称 | 说明 | QQ 邮箱示例 | 163 邮箱示例 |
SENDER_EMAIL | 发件人邮箱(告警邮箱) | 2578628978@qq.com | yourname@163.com |
SENDER_PWD | 邮箱授权码(非登录密码) | vjcjarmmmuleecce(示例) | xxxxxxxx(自行获取) |
RECEIVER_EMAIL | 收件人邮箱(运维邮箱) | 2578628978@qq.com(可同发件人) | admin@xxx.com |
SMTP_SERVER | 邮箱 SMTP 服务器地址 | smtp.qq.com | smtp.163.com |
SMTP_PORT | SMTP 端口(SSL 加密) | 465 | 465 |
三、实战步骤:编写 Bash 监控告警脚本
1. 安装依赖工具
脚本需要用到msmtp(发送邮件)、sysstat(获取 IO/CPU 数据)、ifstat(获取网络流量),执行以下命令安装(适用于 CentOS/RHEL,Ubuntu/Debian 将yum替换为apt-get):
# 安装依赖
yum install -y msmtp sysstat ifstat bc curl
2. 编写完整监控脚本
创建脚本文件system_monitor.sh,直接复制以下代码,注意将「邮箱配置区」替换为你自己的参数:
#!/bin/bash
#########################################################################
# 脚本名称:system_monitor.sh
# 功能描述:监控CPU、内存、硬盘、IO、网络,超阈值发送邮件告警
# 适用系统:CentOS/RHEL/Ubuntu/Debian
# 作者:运维笔记
# 日期:2025-09-07
#########################################################################
# ======================== 1. 邮箱配置区(必须修改!)=======================
SENDER_EMAIL="2578628978@qq.com" # 发件人邮箱(你的QQ邮箱)
SENDER_PWD="vjcjarmmmuleecce" # 邮箱授权码(前文获取的16位码)
RECEIVER_EMAIL="2578628978@qq.com" # 收件人邮箱(可填多个,用逗号分隔)
SMTP_SERVER="smtp.qq.com" # SMTP服务器(QQ邮箱固定为smtp.qq.com)
SMTP_PORT="465" # SMTP端口(SSL加密固定465)
# ======================== 2. 监控阈值配置区(可自定义)=======================
CPU_THRESHOLD=80 # CPU使用率阈值(超80%告警)
MEM_THRESHOLD=80 # 内存使用率阈值(超80%告警)
DISK_THRESHOLD=80 # 硬盘使用率阈值(超80%告警,监控/根分区)
IO_AWAIT_THRESHOLD=50 # IO等待时间阈值(超50ms告警,值越大IO越卡)
NET_THRESHOLD=10 # 网络流量阈值(超10MB/s告警,上下行均监控)
# ======================== 3. 初始化变量与系统信息 ========================
ALARM_CONTENT="" # 告警内容,初始为空
SERVER_NAME=$(hostname) # 服务器主机名
SERVER_IP=$(curl -s ifconfig.me 2>/dev/null || hostname -I | awk '{print $1}') # 服务器IP
CURRENT_TIME=$(date '+%Y-%m-%d %H:%M:%S') # 当前时间
LOG_FILE="/var/log/system_monitor.log" # 监控日志文件路径
# ======================== 4. 监控函数:逐个检查系统指标 ========================
# 4.1 监控CPU使用率
monitor_cpu() {
# 取1分钟内CPU平均使用率(排除空闲进程)
CPU_USAGE=$(vmstat 1 2 | awk 'NR==4{print 100-$15}' | awk '{print int($1)}')
if [ $CPU_USAGE -gt $CPU_THRESHOLD ]; then
ALARM_CONTENT+="⚠️ CPU使用率异常:${CPU_USAGE}%(阈值${CPU_THRESHOLD}%)\n"
fi
}
# 4.2 监控内存使用率
monitor_memory() {
# 总内存-空闲内存(含缓存)=已用内存,计算使用率
MEM_TOTAL=$(free | awk '/Mem/{print $2}')
MEM_FREE=$(free | awk '/Mem/{print $4 + $6 + $7}') # 空闲+缓存+缓冲,更准确
MEM_USAGE=$(( (MEM_TOTAL - MEM_FREE) * 100 / MEM_TOTAL ))
if [ $MEM_USAGE -gt $MEM_THRESHOLD ]; then
ALARM_CONTENT+="⚠️ 内存使用率异常:${MEM_USAGE}%(阈值${MEM_THRESHOLD}%)\n"
fi
}
# 4.3 监控硬盘使用率(默认监控/根分区,可修改为其他分区如/data)
monitor_disk() {
DISK_USAGE=$(df -h / | awk '/\/$/{print substr($5, 1, length($5)-1)}')
if [ $DISK_USAGE -gt $DISK_THRESHOLD ]; then
ALARM_CONTENT+="⚠️ 硬盘使用率异常(/分区):${DISK_USAGE}%(阈值${DISK_THRESHOLD}%)\n"
fi
}
# 4.4 监控磁盘IO等待时间(await)
monitor_io() {
# 采集2次IO数据,取平均await(排除虚拟loop设备)
IO_AWAIT=$(iostat -x 1 2 | grep -vE '^$|Device|loop' | awk '{sum += $10} END {print sum/NR}')
IO_AWAIT=$(printf "%.1f" "$IO_AWAIT") # 保留1位小数
# 浮点数比较,用bc工具
if (( $(echo "$IO_AWAIT > $IO_AWAIT_THRESHOLD" | bc -l) )); then
ALARM_CONTENT+="⚠️ 磁盘IO等待时间过高:${IO_AWAIT}ms(阈值${IO_AWAIT_THRESHOLD}ms)\n"
fi
}
# 4.5 监控网络流量(上下行速率,单位MB/s)
monitor_network() {
# 采集2次网络数据,计算每秒速率(排除lo回环接口)
NET_STATS=$(ifstat -i eth0,ens*,wlan* -T 1 2 | awk 'NR==3 {print $1, $2}')
RX_RATE=$(echo "$NET_STATS" | awk '{print $1}') # 下载速率(MB/s)
TX_RATE=$(echo "$NET_STATS" | awk '{print $2}') # 上传速率(MB/s)
# 保留1位小数
RX_RATE=$(printf "%.1f" "$RX_RATE")
TX_RATE=$(printf "%.1f" "$TX_RATE")
# 分别判断上下行是否超阈值
if (( $(echo "$RX_RATE > $NET_THRESHOLD" | bc -l) )); then
ALARM_CONTENT+="⚠️ 网络下载流量过高:${RX_RATE}MB/s(阈值${NET_THRESHOLD}MB/s)\n"
fi
if (( $(echo "$TX_RATE > $NET_THRESHOLD" | bc -l) )); then
ALARM_CONTENT+="⚠️ 网络上传流量过高:${TX_RATE}MB/s(阈值${NET_THRESHOLD}MB/s)\n"
fi
}
# ======================== 5. 发送告警邮件函数 ========================
send_alert_email() {
# 只有当有告警内容时,才发送邮件
if [ -n "$ALARM_CONTENT" ]; then
# 构建完整邮件内容(含服务器信息)
FULL_CONTENT="⚠️ 服务器资源告警 ⚠️\n\n"
FULL_CONTENT+="📌 服务器信息:\n"
FULL_CONTENT+="主机名:${SERVER_NAME}\n"
FULL_CONTENT+="IP地址:${SERVER_IP}\n"
FULL_CONTENT+="告警时间:${CURRENT_TIME}\n\n"
FULL_CONTENT+="🔴 异常指标:\n"
FULL_CONTENT+="${ALARM_CONTENT}\n"
FULL_CONTENT+="💡 建议:\n"
FULL_CONTENT+="1. CPU/内存异常:检查是否有高占用进程(top命令)\n"
FULL_CONTENT+="2. 硬盘异常:清理无用文件(du -sh * 查找大文件)\n"
FULL_CONTENT+="3. IO异常:检查磁盘是否故障(smartctl工具)\n"
FULL_CONTENT+="4. 网络异常:查看流量来源(iftop命令)"
# 用msmtp发送邮件(SSL加密)
echo -e "From: ${SENDER_EMAIL}\nTo: ${RECEIVER_EMAIL}\nSubject: 【系统告警】${SERVER_NAME}资源异常\n\n${FULL_CONTENT}" | \
msmtp --server ${SMTP_SERVER} \
--port ${SMTP_PORT} \
--from ${SENDER_EMAIL} \
--auth on \
--user ${SENDER_EMAIL} \
--password ${SENDER_PWD} \
${RECEIVER_EMAIL}
# 记录告警日志(便于后续排查)
echo "[$CURRENT_TIME] 告警发送成功:${ALARM_CONTENT}" >> ${LOG_FILE}
echo "✅ 告警已发送至 ${RECEIVER_EMAIL},日志见 ${LOG_FILE}"
else
# 无异常时,也记录正常日志(可选)
echo "[$CURRENT_TIME] 所有指标正常:CPU=${CPU_USAGE}% 内存=${MEM_USAGE}% 硬盘=${DISK_USAGE}% IO=${IO_AWAIT}ms 下载=${RX_RATE}MB/s 上传=${TX_RATE}MB/s" >> ${LOG_FILE}
echo "✅ 所有指标正常,日志见 ${LOG_FILE}"
fi
}
# ======================== 6. 主程序:执行监控与告警 ========================
main() {
echo "[$CURRENT_TIME] 开始系统监控..."
# 执行所有监控函数
monitor_cpu
monitor_memory
monitor_disk
monitor_io
monitor_network
# 发送告警(如有)
send_alert_email
}
# 启动主程序
main
3. 脚本权限配置
给脚本添加可执行权限,否则无法运行:
chmod +x system_monitor.sh
四、测试与部署:确保监控正常运行
1. 手动测试脚本
先手动运行脚本,验证是否能正常采集指标、发送邮件:
# 运行脚本
./system_monitor.sh
测试结果判断:
- 若输出 “✅ 所有指标正常”:说明脚本逻辑没问题,可继续部署定时任务;
- 若输出 “✅ 告警已发送至 xxx@qq.com”:打开收件邮箱,应能收到告警邮件(示例如下);
- 若报错:根据错误信息排查(常见错误见下文 “问题解决”)。
告警邮件示例:
⚠️ 服务器资源告警 ⚠️
📌 服务器信息:
主机名:localhost.localdomain
IP地址:192.168.1.100
告警时间:2025-09-07 15:30:00
🔴 异常指标:
⚠️ CPU使用率异常:85%(阈值80%)
⚠️ 网络下载流量过高:12.3MB/s(阈值10MB/s)
💡 建议:
1. CPU/内存异常:检查是否有高占用进程(top命令)
2. 硬盘异常:清理无用文件(du -sh * 查找大文件)
3. IO异常:检查磁盘是否故障(smartctl工具)
4. 网络异常:查看流量来源(iftop命令)
2. 部署定时任务:实现持续监控
手动运行只能测试一次,要实现 “每 5 分钟监控一次”,需用crontab设置定时任务:
# 编辑定时任务
crontab -e
# 在文件末尾添加以下内容(每5分钟执行一次,日志输出到/var/log/system_monitor.log)
*/5 * * * * /path/to/system_monitor.sh >> /var/log/system_monitor.log 2>&1
- 注意:将/path/to/system_monitor.sh替换为脚本的实际路径(如/root/shengge/system_monitor.sh);
- */5 * * * * 表示 “每 5 分钟执行一次”,可根据需求修改(如*/1 * * * *为每分钟一次)。
3. 验证定时任务
设置后,等待 5 分钟,查看日志文件是否有新记录:
tail -f /var/log/system_monitor.log
若能看到类似以下日志,说明定时任务正常运行:
[2025-09-07 15:35:00] 开始系统监控...
[2025-09-07 15:35:02] 所有指标正常:CPU=25% 内存=60% 硬盘=75% IO=8.2ms 下载=1.5MB/s 上传=0.8MB/s
五、常见问题解决:避坑指南
在部署过程中,可能会遇到以下问题,这里提供针对性解决方案:
1. 脚本运行报错:bash: ./system_monitor.sh: 权限不够
- 原因:脚本没有可执行权限;
- 解决:执行chmod +x system_monitor.sh赋予权限。
2. 邮件发送失败:msmtp: authentication failed
- 原因:邮箱授权码错误,或 SMTP 服务未开启;
- 解决:
-
- 重新登录 QQ 邮箱,检查「POP3/SMTP 服务」是否开启;
-
- 重新生成授权码,替换脚本中的SENDER_PWD;
-
- 确保SENDER_EMAIL与SMTP_SERVER匹配(如 QQ 邮箱必须用smtp.qq.com)。