基于crontab和shell实现监控告警

2024-05-21 20:24:39 浏览数 (2)

一、背景概述

市面上有很多开源的监控告警工具,提供了丰富的、可视化的监控指标,以及告警能力,而对于服务器维度,抛开业务指标外,我们关注的无外乎cpu使用率、内存使用率和磁盘使用率等是否超过了我们既定的安全阈值,如果超过了则推送告警通知,来引起研发人员的关注,从而采取相应的应对措施。

对于一些中小型项目,本身服务器数量并不算多,如果为了标准化和规范化再额外采购机器部署监控平台,本身会带来项目复杂度和额外的开支。

所以在项目规模不大,抑或者项目初期,用脚本来实现服务器本地化指标监控,也许是一种更好的选择。

我们大致采用服务器自身的crontab调度能力和shell脚本来实现脚本定频执行来实现监控告警:

  • 编写监控告警脚本,监控cpu、内存和磁盘(可以按需添加其他指标)使用状况
  • 如果超过设定阈值,则通过相应平台的webhook或机器人,推送告警通知
  • 通过crontab实现5s执行一次脚本,并将执行记录写入日志做滚动

二、监控脚本

创建脚本目录:

代码语言:javascript复制
mkdir -p /opt/tools/script

创建脚本:

代码语言:javascript复制
touch load_check.sh
chmod  x load_check.sh

脚本内容:

代码语言:javascript复制
#!/bin/bash  
CURR_IP="xxx.xxx.xxx.xxx"
CURR_ENV="prod"
ROBOT_TOKEN="token"
THRESHOLD=80
FOCUS_PROCESS="prod-server1"
CURR_DATE=$(TZ=UTC-8 date " %Y%m%d")
CURR_TIME=$(TZ=UTC-8 date " %H:%M:%S")
REQ_PATH="https://open.larksuite.com/open-apis/bot/v2/hook/$ROBOT_TOKEN"
REQ_TYPE="Content-Type: application/json"


function cpu(){  
  
 util=$(vmstat | awk '{if(NR==3)print $13 $14}')  
 iowait=$(vmstat | awk '{if(NR==3)print $16}')  
 echo "${CURR_DATE} ${CURR_TIME} CPU -使用率:${util}% ,等待磁盘IO相应使用率:${iowait}:${iowait}%"  
 if [ $util -ge $THRESHOLD ]; then
    echo "cpu usage >= 80%"
    BODY="{"msg_type":"interactive","card":{"config":{"wide_screen_mode":false,"enable_forward":true},"elements":[{"tag":"markdown","content":"n**服务器名称** : $FOCUS_PROCESS n**当前时间** : $CURR_DATE $CURR_TIME n**服务器ip** : $CURR_IP n**警告级别** :  CRITICAL n**警告内容** : 当前cpu使用率 $iowait%  n "}],"header":{"title":{"tag":"plain_text","content":"[$CURR_ENV 环境]: 服务器预警"},"template":"red"}}}"
      curl "$REQ_PATH" 
      -H "$REQ_TYPE" 
      -d "$BODY"
 fi
  
}  
function memory (){  
  
 total=`free -m |awk '{if(NR==2)printf "%.1f",$2/1024}'`  
    used=`free -m |awk '{if(NR==2) printf "%.1f",($2-$NF)/1024}'`  
    available=`free -m |awk '{if(NR==2) printf "%.1f",$NF/1024}'`  
    usage=$(awk "BEGIN {printf "%.2f", (${used}/${total})*100}")
    echo "${CURR_DATE} ${CURR_TIME} 内存 - 总大小: ${total}G , 使用: ${used}G , 剩余: ${available}G,使用率${usage}%"
    if [ $(echo "$usage >= $THRESHOLD" | bc) -eq 1 ]; then
      echo "memory usage >= 80"
      BODY="{"msg_type":"interactive","card":{"config":{"wide_screen_mode":false,"enable_forward":true},"elements":[{"tag":"markdown","content":"n**服务器名称** : $FOCUS_PROCESS n**当前时间** : $CURR_DATE $CURR_TIME n**服务器ip** : $CURR_IP n**警告级别** :  CRITICAL n**警告内容** : 当前内存使用情况,使用率 $usage%,已使用 $used G,剩余可用 $available G, 总内存 $total G n "}],"header":{"title":{"tag":"plain_text","content":"[$CURR_ENV 环境]: 服务器预警"},"template":"red"}}}"
      curl "$REQ_PATH" 
      -H "$REQ_TYPE" 
      -d "$BODY"
    fi
}  
disk(){  
    fs=$(df -h | awk '$NF=="/"{print $1}')
    for p in $fs; do  
        mounted=$(df -h |awk '$1=="'$p'"{print $NF}')  
        size=$(df -h |awk '$1=="'$p'"{print $2}')  
        used=$(df -h |awk '$1=="'$p'"{print $3}')  
        #used_percent=$(df -h |awk '$1=="'$p'"{print $5}')  
        used_percent=$(df -h | awk '$1=="'$p'"{print $5}' | tr -d '%')
        echo "${CURR_DATE} ${CURR_TIME} 硬盘 - 挂载点: $mounted , 总大小: $size , 使用: $used , 使用率: $used_percent%"  
        if [ $used_percent -ge $THRESHOLD  ]; then
           echo "disk usage >= 80"
           BODY="{"msg_type":"interactive","card":{"config":{"wide_screen_mode":false,"enable_forward":true},"elements":[{"tag":"markdown","content":"n**服务器名称** : $FOCUS_PROCESS n**当前时间** : $CURR_DATE $CURR_TIME n**服务器ip** : $CURR_IP n**警告级别** :  CRITICAL n**警告内容** : 当前磁盘使用情况,使用率 $used_percent %,已使用 $used,总大小 $size  n "}],"header":{"title":{"tag":"plain_text","content":"[$CURR_ENV 环境]: 服务器预警"},"template":"red"}}}"
            curl "$REQ_PATH" 
            -H "$REQ_TYPE" 
            -d "$BODY"
        fi
    done  
  
}  
cpu  
memory  
disk

该脚本核心做了以下几件事情:

  • 监控cpu,如果cpu超过80%,则发送告警到告警群
  • 监控内存,如果内存使用率超过80%,则发送告警通知到告警群
  • 监控磁盘,如果磁盘使用率超过80%,则发送告警通知到飞书告警群

手动执行脚本,看到如下类似的告警通知:

三、配置crontab任务

1.监控脚本5s定频执行

由于 Linux 的 crontab 的定时命令格式如下:

minute hour day-of-month month-of-year day-of-week commands

意味着标准定时任务中,最小定时周期是分钟。

但是,我们的服务器负载监控,可能需要每5秒就要求执行某个shell脚本。那么就需要做一些小小的改造才能实现。

代码语言:javascript复制
*/1 * * * * /bin/bash /opt/tools/script/load_check.sh  >>/opt/tools/script/check_$(date  "%Y-%m-%d").log
*/1 * * * * sleep 5  && /bin/bash /opt/tools/script/load_check.sh  >>/opt/tools/script/check_$(date  "%Y-%m-%d").log
*/1 * * * * sleep 10 && /bin/bash /opt/tools/script/load_check.sh  >>/opt/tools/script/check_$(date  "%Y-%m-%d").log
*/1 * * * * sleep 15 && /bin/bash /opt/tools/script/load_check.sh  >>/opt/tools/script/check_$(date  "%Y-%m-%d").log
*/1 * * * * sleep 20 && /bin/bash /opt/tools/script/load_check.sh  >>/opt/tools/script/check_$(date  "%Y-%m-%d").log
*/1 * * * * sleep 25 && /bin/bash /opt/tools/script/load_check.sh  >>/opt/tools/script/check_$(date  "%Y-%m-%d").log
*/1 * * * * sleep 30 && /bin/bash /opt/tools/script/load_check.sh  >>/opt/tools/script/check_$(date  "%Y-%m-%d").log
*/1 * * * * sleep 35 && /bin/bash /opt/tools/script/load_check.sh  >>/opt/tools/script/check_$(date  "%Y-%m-%d").log
*/1 * * * * sleep 40 && /bin/bash /opt/tools/script/load_check.sh  >>/opt/tools/script/check_$(date  "%Y-%m-%d").log
*/1 * * * * sleep 45 && /bin/bash /opt/tools/script/load_check.sh  >>/opt/tools/script/check_$(date  "%Y-%m-%d").log
*/1 * * * * sleep 50 && /bin/bash /opt/tools/script/load_check.sh  >>/opt/tools/script/check_$(date  "%Y-%m-%d").log
*/1 * * * * sleep 55 && /bin/bash /opt/tools/script/load_check.sh  >>/opt/tools/script/check_$(date  "%Y-%m-%d").log

上述任务是每5s执行一次监控脚本,并且把脚本执行记录输出到带有日期格式的日志中/opt/tools/script/check_$(date "%Y-%m-%d").log。

2.脚本执行日志滚动

但是这里会带来一个隐藏的问题,这里的脚本和任务完全有我们自己控制的,并没有使用logrotate来做日志切割和滚动,可能会因为监控脚本自身的执行记录日志导致磁盘打满,那么我们需要自己清除历史日志。

在crontab中添加以下任务:

代码语言:javascript复制
0 0 * * * /bin/find /opt/tools/script -type f -name "check_*.log" -mtime  0 -delete

每天凌晨查找/opt/tools/script目录下寻找check_*.log格式的日志,找出访问和修改时间是今天之前的文件,并且删除。

最后重启crontab使任务生效:

代码语言:javascript复制
service crond restart

我们可以模拟创建一个历史日志check_2024-05-13.log,并且修改它的访问和修改时间:

代码语言:javascript复制
touch -r /opt/tools/springboot-demo/pom.xml check_2024-05-13.log

这里讨了个巧,找了个今天以前的文件作为参考,修改文件的访问和修改时间为另一个文件的时间。

这样执行任务的命令:

代码语言:javascript复制
/bin/find /opt/tools/script -type f -name "check_*.log" -mtime  0 -delete

这样就能删除check_2024-05-13.log了,也就是验证了脚本的执行日志保留一天,每天自动删除今天以前的执行日志。

0 人点赞