一、背景概述
市面上有很多开源的监控告警工具,提供了丰富的、可视化的监控指标,以及告警能力,而对于服务器维度,抛开业务指标外,我们关注的无外乎cpu使用率、内存使用率和磁盘使用率等是否超过了我们既定的安全阈值,如果超过了则推送告警通知,来引起研发人员的关注,从而采取相应的应对措施。
对于一些中小型项目,本身服务器数量并不算多,如果为了标准化和规范化再额外采购机器部署监控平台,本身会带来项目复杂度和额外的开支。
所以在项目规模不大,抑或者项目初期,用脚本来实现服务器本地化指标监控,也许是一种更好的选择。
我们大致采用服务器自身的crontab调度能力和shell脚本来实现脚本定频执行来实现监控告警:
- 编写监控告警脚本,监控cpu、内存和磁盘(可以按需添加其他指标)使用状况
- 如果超过设定阈值,则通过相应平台的webhook或机器人,推送告警通知
- 通过crontab实现5s执行一次脚本,并将执行记录写入日志做滚动
二、监控脚本
创建脚本目录:
mkdir -p /opt/tools/script
创建脚本:
touch load_check.sh
chmod +x load_check.sh
脚本内容:
#!/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脚本。那么就需要做一些小小的改造才能实现。
*/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中添加以下任务:
0 0 * * * /bin/find /opt/tools/script -type f -name "check_*.log" -mtime +0 -delete
每天凌晨查找/opt/tools/script目录下寻找check_*.log格式的日志,找出访问和修改时间是今天之前的文件,并且删除。
最后重启crontab使任务生效:
service crond restart
我们可以模拟创建一个历史日志check_2024-05-13.log,并且修改它的访问和修改时间:
touch -r /opt/tools/springboot-demo/pom.xml check_2024-05-13.log
这里讨了个巧,找了个今天以前的文件作为参考,修改文件的访问和修改时间为另一个文件的时间。
这样执行任务的命令:
/bin/find /opt/tools/script -type f -name "check_*.log" -mtime +0 -delete
这样就能删除check_2024-05-13.log了,也就是验证了脚本的执行日志保留一天,每天自动删除今天以前的执行日志。