Bash脚本如何实现连续3次检测CPU超95%阈值才发邮件告警
实现逻辑
要过滤瞬时CPU尖峰的误告警,核心是为每台被监控主机持久化存储连续超阈值的检测计数,匹配规则完全对齐你的场景:
- 每次脚本运行时,从本地状态文件读取对应主机上次记录的连续超标次数
- 若本次检测CPU使用率≥95%,将对应计数+1
- 若本次检测CPU使用率<95%,直接清空对应主机的计数,打破连续超标序列
- 当某台主机的连续超标计数累加到3时,触发管理员告警,告警发送后可重置计数避免重复轰炸
- 由于脚本是cron每10分钟触发的独立进程,所有状态必须落盘存储,不能依赖脚本内存变量(进程执行结束后内存变量会被回收,无法跨进程传递状态)
改造后的脚本核心代码
# 配置项 STATE_FILE="/var/log/cpu_monitor_state.txt" # 存储监控计数的状态文件 THRESHOLD=95 # CPU使用率告警阈值 ALERT_CYCLES=3 # 需要连续超标的检测次数 ADMIN_EMAIL="admin@sample.com" # 告警接收人 # 初始化状态文件,收紧权限避免非授权篡改 [[ ! -f "$STATE_FILE" ]] && touch "$STATE_FILE" && chmod 600 "$STATE_FILE" for sn in $(grep -v "#" /tmp/hosts); do # 保留原有CPU检测逻辑,无需修改 cpuuse=$(ssh -qn -o ConnectTimeout=15 -oStrictHostKeyChecking=no -o BatchMode=yes $sn "top -b -n2 -p 1 | fgrep \"Cpu(s)\" | tail -1 | awk -F'id,' -v prefix=\"\$prefix\" '{ split(\$1, vs, \",\"); v=vs[length(vs)]; sub(\"%\", \"\", v); printf \"%s%.1f%%\n\", prefix, 100 - v }' | rev | cut -c 4- | rev") # 读取当前主机的历史连续计数,无记录则默认0 hist_count=$(grep "^${sn}:" "$STATE_FILE" | cut -d: -f2) [[ -z "$hist_count" ]] && hist_count=0 if [[ "$cpuuse" -ge "$THRESHOLD" ]]; then # 本次检测超标,计数累加 new_count=$((hist_count + 1)) # 更新状态文件,替换旧的主机记录 sed -i "/^${sn}:/d" "$STATE_FILE" echo "${sn}:${new_count}" >> "$STATE_FILE" # 达到连续告警次数则发送邮件 if [[ "$new_count" -ge "$ALERT_CYCLES" ]]; then echo "CPU告警!! $sn 连续${new_count}次检测CPU使用率超阈值,本次检测值${cpuuse}%" | mailx -s "$sn CPU持续高负载告警" "$ADMIN_EMAIL" # 发送告警后清零计数,避免后续检测重复发信 sed -i "/^${sn}:/d" "$STATE_FILE" fi else # 本次检测未超阈值,直接清空计数,打断连续序列 sed -i "/^${sn}:/d" "$STATE_FILE" fi done
落地注意事项
- 状态文件请勿存储在
/tmp目录,该目录下的文件会被系统定期清理,容易导致状态丢失 - 脚本开头建议添加文件锁逻辑,避免cron触发时上一次脚本尚未执行完成,出现状态文件读写冲突,可在脚本最开头加入:
flock -n /var/lock/cpu_monitor.lock "$0" || exit 0 - 若需要持续告警提醒,可在发送告警后不清零计数,等检测到CPU使用率恢复正常后再清零,实现“持续高负载则每30分钟提醒一次,恢复正常则重置计数”的效果
- 原脚本通过top取2次采样计算CPU使用率的逻辑本身具备一定抗尖峰能力,配合3次连续检测的规则,可完全过滤掉单次瞬时尖峰触发的误告警
内容的提问来源于stack exchange,提问作者mbarron9393
相关产品推荐
相关产品推荐

