背景:本人就职于业务导向型的传统企业,技术部门虽有划分开发部和运行部,但运维实力仍属于基础运维。平时工作除了解决用户报障、监控服务可用性、监控服务器性能指标外,偶尔帮公司写写运维脚本,提高运维效率。
今早领导脑子一拍,说要对公司提供的互联网IP进行可用性监控,我想互联网IP可用性确实是得监控起来,一激灵写出代码如下,供大家参考。脚本巡检160个IP耗时8s。
先进行需求分析:
- 从列表内获取IP,抽象出获取IP方法,后续修改为从CMDB获取IP序列;
- 可调节ping IP的成功率阈值,减少误报;
- 调节PING的频率及包数;
- 使用ping命令同时ping多个IP,返回每个IP ping成功率;
- 可调节巡检窗口大小,例如执行任务3次都失败才进行告警,减少误报;
- 输出统计结果,输出巡检成功个数,巡检失败个数。
环境准备:
- 一台可以连互联网的linux服务器
- 一组发布在公网的IP
- 一个爱动脑筋的领导
- 一位踏实肯干的码农
原理图和输出结果:
流程图:
代码部分:
0、定义日志输出Log(),将需要输入到变量$buffer的字符都使用Log()函数来输入。
- 代码简洁,减少重复。
- 便于后续修改日志格式。
- 缺点:要使用echo -e $buffer 才能看到变量内容
#定义Log()函数,将单次PING巡检的日志存放到$buffer中,使用\t保持每次输入数据格式一致
使用echo -e输出$buffer内日志
Log() {
buffer="$buffer\t$1"
}
#存取日志方式如下:
Log(this is log 1)
Log(this is log 2)
echo -e $buffer
> this is log 1 this is log 2
初始化变量
PackNum=1 #共100个包
interval=1 #几秒发1个包 默认1秒1个 1秒1次 1/1=1
alarmThreshold=100 #告警阈值,大于等于50为巡检成功
echo "Starting Pinging Script with >=$alarmThreshold% sucRate to success, Each IP Pinging in $PackNum package(s) in `awk 'BEGIN{printf("%.1f",'$PackNum' * '$interval')}'` s."
filepath=/home/test/ping_test/list.txt
echo "reading pathfile at $filepath"
1、定义输入IP函数readip(),从文件读取IP列表,定义读取读取IP函数。
- 便于修改输入源
- 便于调试代码 自定义输入
- 后续巡检每个过程均会记录日志,便于确保脚本运行。
#定义读取IP函数 readip()
filepath=/home/test/ping_test/list.txt
echo "reading pathfile at $filepath"
cat $filepath
> 14.215.177.39
> 14.215.177.40
> 14.215.177.41
> 2001:df0:1003::f
readip(){
if [[ -f $filepath ]] ; then
cat $filepath | sort -n | uniq
return 0
else
echo "Pinging Test Failed. Not match IP_list."
exit 1
fi
#可以使用以下方式获取IP
cat /tmp/ping_test/list.txt | sort -n | uniq
echo "qwerqwer" #干扰项
echo "127.0.0.1" #本机IP
echo "::1" #本机v6IP
}
2、定义IP合法性检查函数testv4() 和 testv6()
- 如后续使用外部输入,会要求对输入源做校验。
#v4IP检查
testv4() {
if [[ "$1" =~ ^([0-9]{1,3}.){3}[0-9]{1,3}$ ]]; then
Log "is ipv4."
return 0
else
#Log "is not ipv4."
return 4
fi
}
#v6IP检查
testv6() {
if [[ "$1" =~ ^\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?\s*$ ]]; then
#---skip ipv6 test
Log "is ipv6."
return 0
else
#Log "is not ipv6."
return 6
fi
}
#ip合法性检查操作方式如下:
testv4(14.215.177.39)
echo -e $buffer
> 14.215.177.39 is ipv4
testv6(qwerqwer)
#> qwerqwer is not ipv6
3、分别定义v4和v6的ping巡检函数,并记录巡检结果。取ping成功百分比做巡检结果。
定义函数输出 printResult()函数
root@linux1-x:~$ ping -c 1 192.168.1.101
PING 192.168.1.101 (192.168.1.101) 56(84) bytes of data.
64 bytes from 192.168.1.101: icmp_req=1 ttl=64 time=0.168 ms
--- 192.168.1.101 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.168/0.168/0.168/0.000 ms
############PING TEST
ipv4check() {
Log "Pinging..."
local ping
ping=`ping -c $PackNum -i $interval $1 2> /dev/null |grep loss |awk '{print $6}' | awk -F "%" '{print $1}' `
printResult
}
ipv6check() {
Log "Pinging..."
local ping
ping=`ping -6 -c $PackNum -i $interval $1%2 2> /dev/null |grep loss |awk '{print $6}' | awk -F "%" '{print $1}'`
printResult
}
printResult() {
local SuccessRate
SuccessRate=$[100-$ping]
Log "SuccessRate $SuccessRate%."
if [[ $SuccessRate -ge $alarmThreshold ]]; then
WriteSuc
return 0
else
WriteFail
return 3
fi
}
4、定义巡检过程dotest(),将上面定义的函数串起来。
输入IP
-->检测IP是V4-->V4 PING检测,
-->检测IP是V6-->V6 PING检测,
-->如都不是,则输出IP非法。
############TEST ROUTINE
dotest() {
## test ipv4
testv4 $ip
if [[ $? -eq 0 ]]; then
ipv4check $ip #test and Pinging IPv4
if [[ $? -eq 0 ]]; then
#echo "$ip Yes."
Log "Yes."
echo -e $buffer
continue
else
#echo "ipv4 check finished, finish code = $?."
#Log "IPv4_Check finished, code = $?."
Log "No."
echo -e $buffer
continue
fi
fi
## test ipv6
testv6 $ip
if [[ $? -eq 0 ]]; then
ipv6check $ip
if [[ $? -eq 0 ]]; then
#echo "$ip Yes."
Log "Yes."
echo -e $buffer
continue
else
#echo "ipv6_check finished, finish code = $?."
#Log "IPv6_Check finished, code = $?."
Log "No."
echo -e $buffer
continue
fi
fi
#sleep 1
#echo "$ip is not a vaildate ip."
Log " is not a vaildate ip."
WriteFail
echo -e $buffer #按行输出
continue
}
5、定义主函数,主函数使用readip()输入需要巡检的IP,使用dotest执行巡检任务。
经检验使用次方法可以串行执行巡检任务。
#定义主函数,使用readip()获取并输入IP,
for ip in `readip`
do
buffer=""
buffer="$ip"
dotest #& #<---调用巡检任务
#sleep 0.01
#echo -e $buffer
done
5.1修改巡检任务执行方法,增加&,使巡检任务放在后台执行,异步执行多个巡检任务。
dotest & #<---调用巡检任务
sleep 0.01
5.2加上&异步执行任务后,发现多个子进程共用变量,如都输出到同一变量会导致格式串行。
如要分别输出巡检结果,需要对使用的变量落盘处理,对代码进行以下处理。
sucNum=/dev/shm/PT_sucNum
failNum=/dev/shm/PT_failNum
failIP=/dev/shm/PT_failIP
WriteSuc() {
echo $(($(<$sucNum) +1 )) > $sucNum
}
WriteFail() {
echo $(($(<$failNum) +1 )) > $failNum
echo -e "$ip" >> $failIP
}
并输出巡检结果
####output --> cannot use WriteLog in a function
echo -e "Pinging Test Finish. SuccessTest:$(<$sucNum) FailedTest:$(<$failNum)."
思考:
- 当前数据原为静态资产清单,后续等cmdb就绪可以使用调接口的方式获取实时IP数据。
- 告警容错度太差,巡检失败即产生告警;理想的巡检应可以增加容错考虑。例如:执行三次巡检任务,均失败才告警。 --->更新线 :该问题已得到解决,将在另外一篇文章进行讲解:跳转入口:CSDN
- 可以添加告警恢复机制。
- 巡检机器存在单点故障的问题,可以在多台服务器上配置巡检脚本。
- 现在监控工具越来越多,但没能集中管理、集中展示,后续是否有考虑使用统一的监控工具?
现在配置策略如下:
公网IP PING巡检 应用组 互联网出口IP 161个 每5分钟执行1次 PING 3次
巡检结果输出如下:
Starting Pinging Script with >=100% sucRate to success, Each IP Pinging in 1 package(s) in 1.0 s.
reading pathfile at /home/test/ping_test/list.txt
a.b.c.d is ipv4. Pinging... SuccessRate 100%. Yes.
aa.bb.cc.dd is ipv4. Pinging... SuccessRate 100%. Yes....
aaa:bbb:ccc:ddd::eee is ipv6. Pinging... SuccessRate 100%. Yes.
aaaa:bbbb:cccc:dddd::eeee is ipv6. Pinging... SuccessRate 100%. Yes.
Pinging Test Finish. SuccessTest:161 FailedTest:0.