前言
本篇文章基于链接: shell 脚本监听服务器,钉钉机器人推送的基础上修改完善而来,其中还有很多不足的地方,欢迎各位进行指正
一、监控内容
原本的脚本仅仅对磁盘。内存、CPU、服务、数据库进行了监控,原本的脚本并未对参数等属性进行抽离,也因此对多台服务器的复用性较低,我基于对方的代码进行了补充和完善。
其中新增加了对docker和k8s的监控
二、使用步骤
1.创建钉钉机器人
钉钉机器人需要基于群组实现,也就是说,必须要新创建一个群聊,才能创建机器人
1.1创建群聊
找到钉钉的右上角点击+号,选择人员后直接创建即可
1.2创建机器人
先打开刚创建好的群聊
打开之后拉到最下面会发现有个机器人的选项
点击之后可以添加机器人
添加自定义机器人即可
这里有三种安全设置,必须选择其中一种,最简单的方案为选择自定义关键词,可以选择多个
选择自己发送的消息内的关键词即可,例如我的关键词添加为内存 磁盘 CPU这三个关键词,当然也可以添加其他的,一般是没人乱给你发消息的。
勾选我已阅读并同意,然后完成即可,创建完成后可以看到多出来一个webhook
这个就是一个钉钉的地址,后面拼接了一个唯一token
到这一步,钉钉的准备工作就完成了
2.脚本
#!/bin/bash
# 可配置属性值
# 内存占用比例
mem_threshold=90
# cpu 占用比例
cpu_threshold=90
# 磁盘占用比例
disk_threshold=90
# 钉钉通知 URL 和 token,这个地方替换成自己的
url="https://oapi.dingtalk.com/robot/send?access_token=xxxx"
# 循环时间秒数
circulation_time=3600
# 需要检测的指定地址的能否联通
inform_address=("www.baidu.com" "www.wangyi.com")
# 需要检测的本地端口号是否存在
inform_post=(3306 6379)
# 需要通知的用户
inform_user=("xxxxx" "xxxxxxxxx")
# 当前系统的 Ip 地址
ip='192.168.xxx.xxx'
# 标题
title='服务器监控'
# 当前日期
time="$(date "+%Y-%m-%d")"
# 当前时间
times="$(date "+%H:%M:%S")"
# 是否检测 docker 程序
docker_check=false
# 是否检测 k8s 程序
Kubernetes_check=true
# 处理方法
while true; do
# 文本展示的提示
text_inform_user=""
# at 展示的提示
at_inform_user="["
first_element=true
# 处理两种类型的数据
for value in "${inform_user[@]}"; do
text_inform_user+="@${value} "
if [[ $first_element == false ]]; then
at_inform_user+=","
fi
at_inform_user+="\"${value}\""
first_element=false
done
at_inform_user+="]"
# 错误标识
send_alert=false
# 确认指定的地址是否能够使用
address_text=""
for value in "${inform_address[@]}"; do
# 使用更严格的超时时间和错误处理
response=$(curl --connect-timeout 5 --max-time 10 -I -m 10 -o /dev/null -s -w "%{http_code}" "$value" || echo "timeout")
if [ "$response" != "200" ]; then
send_alert=true
address_text+="**地址${value}状态**: <font color=\\\"#FF0000\\\">异常</font>\n\n"
fi
done
# 确认当前地址的端口号是否正常运行
port_text=""
for value in "${inform_post[@]}"; do
# 使用更可靠的方式检查端口
ports=$(netstat -an | grep -qw ":${value}" | grep -qw LISTEN && echo "正常" || echo "异常")
if [ "$ports" == "异常" ]; then
send_alert=true
port_text+="**端口${value}状态**: <font color=\\\"#FF0000\\\">异常</font>\n\n"
fi
done
# 校验 cup,内存,磁盘的占用比
lsblk=$(df -h / | awk 'END{print $5}' | sed 's/%//g')
mem=$(free | grep Mem | awk '{print $3/$2 * 100.0}')
cpu=$(top -bn1 | grep "Cpu(s)" | sed "s/.*, *\([0-9.]*\)%* id.*/\1/" | awk '{print 100 - $1}')
# 判断是否需要发送预警
if (($(echo "$mem > $mem_threshold" | bc -l))); then
send_alert=true
fi
if (($(echo "$cpu > $cpu_threshold" | bc -l))); then
send_alert=true
fi
if (($(echo "$lsblk > $disk_threshold" | bc -l))); then
send_alert=true
fi
# docekr 程序运行检测
if [[ $docker_check == true ]]; then
containers=$(docker ps -a)
docker_text=""
echo "$containers" | while read line; do
# 提取容器 ID 和状态
container_id=$(echo "$line" | awk '{print $1}')
status=$(echo "$line" | awk '{print $6}')
# 提取容器名称
container_name=$(docker ps -a --filter "id=$container_id" --format "{{.Names}}")
# 检查容器状态,并拼接错误信息
if [[ "$status" == "Exited"* ]]; then
docker_text+="**容器${container_name}状态**: <font color=\\\"#FF0000\\\">未启动</font>\n"
fi
done
fi
# k8s 程序运行检测
if [[ $Kubernetes_check == true ]]; then
Kubernetes_text=""
pods=$(kubectl get pods -A --no-headers | awk '{if (NR>1) print $1, $4}' | grep -v 'kube')
if [ -z "$pods" ]; then
echo "No pods found."
exit 1
fi
while IFS=' ' read -r namespace status; do
echo "${namespace}:${status}"
if [[ "$status" != "Running" ]]; then
send_alert=true
Kubernetes_text+="**节点${namespace}异常原因**: <font color=\\\"#FF0000\\\">${status}</font>\n\n"
fi
done <<<"$pods"
fi
text_body="{\"msgtype\": \"markdown\",\"markdown\": {\"title\": \"${title}\", \"text\": \"![screenshot](https://images.cnblogs.com/cnblogs_com/blogs/718800/galleries/2294157/o_230330085502_1.png)\\n\\n**监控项**: <font color=\\\"#0000FF\\\">${title}</font>\\n\\n**报警时间**: <font color=\\\"#0000FF\\\">${time} ${times}</font>\\n\\n**监控 ip**: <font color=\\\"#0000FF\\\">${ip}</font>\\n\\n**报警项**: <font color=\\\"#FF0000\\\">服务运行情况</font>\\n\\n**磁盘空间使用率**: <font color=\\\"#FF0000\\\">${lsblk}%</font>\\n\\n**内存使用率**: <font color=\\\"#FF0000\\\">${mem}%</font>\\n\\n**CPU 使用率**: <font color=\\\"#FF0000\\\">${cpu}%</font>\\n\\n${address_text} ${port_text}${docker_text}${Kubernetes_text} ${text_inform_user} \\n\\n> 脚本定时监控.\"}, \"at\": {\"isAtAll\":\"false\",\"atMobiles\": ${at_inform_user}}}"
# 如果需要发送预警,则构建并发送钉钉消息
if [ "$send_alert" = true ]; then
curl -H 'Content-Type: application/json' -X POST -d "$text_body" "$url"
# 检查 curl 命令的返回值
if [ $? -ne 0 ]; then
echo "发送钉钉消息失败"
fi
fi
# 等待 1 小时
sleep $circulation_time
done
总结
因为docker和k8s的版本不同,会导致名称成状态的列位置不同,也因此会导致返回的数据不对,如果发现返回的信息不对,请根据自己的具体情况进行调整,具体的侦测逻辑无需修改
大部分的参数我都做了剥离,可以在最上面进行调整,
具体的实现