笔记内容:使用shell编写一个简单的告警系统
笔记日期:2017-11-28
- 20.19 告警系统需求分析
- 20.20 告警系统主脚本
- 20.21 告警系统配置文件
- 20.22 告警系统监控项目
- 20.23/20.24/20.25 告警系统邮件引擎
- 20.26 运行告警系统
20.19 告警系统需求分析
在这之前的笔记里,将大部分的常用shell基本语法已经介绍完了,在这之后就是实战一些小项目了。这个项目是做一个简单的告警系统,用于完成一些简单个性化的需求。
目录结构与要求:
20.20 告警系统主脚本
shell项目-告警系统main.sh
首先我们要编写一个告警系统的主脚本,我这里是放在了/usr/local/sbin/目录下,这个主脚本的文件名称为main.sh,在编写脚本之前我们还需要创建一个完整的目录结构:
[root@localhost ~]# cd /usr/local/sbin/
[root@localhost /usr/local/sbin]# mkdir mon
[root@localhost /usr/local/sbin]# cd mon
[root@localhost /usr/local/sbin/mon]# mkdir bin conf shares log mail
[root@localhost /usr/local/sbin/mon]# cd bin
[root@localhost /usr/local/sbin/mon/bin]#vim main.sh
然后进入到以上创建的bin目录下,编辑main.sh脚本, main.sh内容如下:
#!/bin/bash
#Written by aming.
# 是否打开发送邮件的开关,1为打开
export send=1
# 过滤ip地址,为了告诉我们发送邮件的IP,注意这里的网卡名称要改为你的网卡名称
export addr=`/sbin/ifconfig |grep -A1 "eth0: " |awk '/inet/ {print $2}'`
# 把当前路径拿出来
dir=`pwd`
# 只需要最后一级目录名
last_dir=`echo $dir|awk -F'/' '{print $NF}'`
# 下面的判断目的是,保证执行脚本的时候,我们在bin目录里,不然监控脚本、邮件和日志很有可能找不到
if [ $last_dir == "bin" ] || [ $last_dir == "bin/" ]; then
# 定义配置文件所在的路径
conf_file="../conf/mon.conf"
else
# 如果不在bin目录下就退出整个脚本
echo "you shoud cd bin dir"
exit
fi
# 输出正确日志和错误日志
exec 1>>../log/mon.log 2>>../log/err.log
# 给日志记录标记一个时间,还有系统负载
echo "`date +"%F %T"` load average"
# 执行load.sh子脚本
/bin/bash ../shares/load.sh
#先检查配置文件中是否需要监控502.sh
if grep -q 'to_mon_502=1' $conf_file; then
# 过滤出在配置文件中定义的日志文件路径
export log=`grep 'logfile=' $conf_file |awk -F '=' '{print $2}' |sed 's/ //g'`
/bin/bash ../shares/502.sh
fi
20.21 告警系统配置文件
shell项目-告警系统mon.conf
主脚本编写完之后,就是编写配置文件,配置文件比较简单里面主要就是一些定义开关的开启关闭状态和日志文件路径等内容。配置文件要放在/usr/local/sbin/mon/conf/下,文件名称必须为mon.conf,因为在主脚本里已经定义了:
[root@localhost ~]# cd /usr/local/sbin/mon/conf/
[root@localhost /usr/local/sbin/mon/conf]# vim mon.conf
mon.conf文件的内容如下:
## to config the options if to monitor
## 定义mysql的服务器地址、端口以及user、password
to_mon_cdb=0 ##0 or 1, default 0,0 not monitor, 1 monitor
db_ip=10.20.3.13
db_port=3315
db_user=username
db_pass=passwd
## httpd 如果是1则监控,为0不监控
to_mon_httpd=0
## php 如果是1则监控,为0不监控
to_mon_php_socket=0
## http_code_502 需要定义访问日志的路径
to_mon_502=1
# 定义日志文件路径
logfile=/data/log/xxx.xxx.com/access.log
## request_count 定义是否监控请求数量
to_mon_request_count=0
# 定义请求日志的路径
req_log=/data/log/www.discuz.net/access.log
# 定义域名
domainname=www.discuz.net
配置文件的参数可以自定义,本身配置文件就是用来给用户定义的。
20.22 告警系统监控项目
shell项目-告警系统load.sh
编写子脚本,也就是监控项目,这个load.sh子脚本是必须要有的,这个脚本用来监控系统负载,因为在主脚本里已经定义了需要执行这个子脚本来进行监控系统负载。这个脚本需要放在/usr/local/sbin/mon/shares/ 目录下,而且以后的子脚本都是放在这个目录下,因为主脚本里已经定义了:
[root@localhost ~]# cd /usr/local/sbin/mon/shares/
[root@localhost /usr/local/sbin/mon/shares]# vim load.sh
load.sh内容如下:
#! /bin/bash
# 定义load的值
load=`uptime |awk -F 'average:' '{print $2}'|cut -d',' -f1|sed 's/ //g' |cut -d. -f1`
# 判断系统负载是否高于预设值和发送邮件的开关是否打开
if [ $load -gt 10 ] && [ $send -eq "1" ]
then
# 输出一条日志记录,这个日志会作为邮件内容
echo "$addr `date +%T` load is $load" >../log/load.tmp
# 执行发邮件的脚本,这里的邮箱地址写你的邮箱地址
/bin/bash ../mail/mail.sh [email protected] "$addr\_load:$load" `cat ../log/load.tmp`
fi
# 输出日志到主脚本中指定的日志文件
echo "`date +%T` load is $load"
shell项目-告警系统502.sh
这个脚本用于监控502状态码发生的次数。
502.sh内容如下:
#! /bin/bash
# 截取一分钟以前的时间
d=`date -d "-1 min" +%H:%M`
# 获得502状态码发生的次数
c_502=`grep :$d: $log |grep ' 502 '|wc -l`
# 次数大于预设值,并且发送邮件的开关为打开状态,就发送告警邮件
if [ $c_502 -gt 10 ] && [ $send == 1 ]; then
# 记录日志信息作为告警邮件的内容
echo "$addr $d 502 count is $c_502">../log/502.tmp
# 调用发送邮件的脚本
/bin/bash ../mail/mail.sh $addr\_502 $c_502 ../log/502.tmp
fi
# 记录日志
echo "`date +%T` 502 $c_502"
shell项目-告警系统disk.sh
这个脚本是用来监控磁盘使用率的,这个脚本会把每个磁盘分区挨个看一下。
disk.sh内容如下:
#! /bin/bash
# 先删除之前记录的日志文件
rm -f ../log/disk.tmp
# 先把环境语言改为英文,因为是按照英文来进行过滤的
LANG=en
# 遍历出df -h 命令结果中为已用那一列的百分比数字
for r in `df -h |awk -F '[ %]+' '{print $5}'|grep -v Use`
do
# 如果有分区的已用率大于90就记录日志
if [ $r -gt 90 ] && [ $send -eq "1" ]
then
echo "$addr `date +%T` disk useage is $r" >>../log/disk.tmp
fi
# 判断日志文件是否存在
if [ -f ../log/disk.tmp ]
then
# 文件存在就发送邮件
df -h >> ../log/disk.tmp
/bin/bash ../mail/mail.sh $addr\_disk $r ../log/disk.tmp
echo "`date +%T` disk useage is nook"
else
# 不存在则记录日志
echo "`date +%T` disk useage is ok"
fi
20.23/20.24/20.25 告警系统邮件引擎
shell项目-告警系统mail.sh:
在编写mail.sh之前,先得准备一个发送邮件的功能性脚本,因为这个告警系统会在监控项出现问题的时候发送告警邮件,所以还需要编写一个能够发送邮件的脚本,我这里使用的脚本是python编写的,这个脚本放在/usr/local/sbin/mon/mail 目录下:
[root@localhost ~]# cd /usr/local/sbin/mon/mail
[root@localhost /usr/local/sbin/mon/mail]# vim mail.py
脚本内容如下:
#!/usr/bin/env python
#-*- coding: UTF-8 -*-
import os,sys
reload(sys)
sys.setdefaultencoding('utf-8')
import getopt
import smtplib
from email.MIMEText import MIMEText
from email.MIMEMultipart import MIMEMultipart
from subprocess import *
def sendqqmail(username,password,mailfrom,mailto,subject,content):
# 这里要修改为你邮箱的smtp服务地址,例如163邮箱的话,就是:smtp.163.com
gserver = 'smtp.example.com'
gport = 25
try:
msg = MIMEText(unicode(content).encode('utf-8'))
msg['from'] = mailfrom
msg['to'] = mailto
msg['Reply-To'] = mailfrom
msg['Subject'] = subject
smtp = smtplib.SMTP(gserver, gport)
smtp.set_debuglevel(0)
smtp.ehlo()
smtp.login(username,password)
smtp.sendmail(mailfrom, mailto, msg.as_string())
smtp.close()
except Exception,err:
print "Send mail failed. Error: %s" % err
def main():
to=sys.argv[1]
subject=sys.argv[2]
content=sys.argv[3]
##定义邮箱的账号和密码,你需要修改成你自己的账号和密码
sendqqmail('[email protected]','password','[email protected]',to,subject,content)
if __name__ == "__main__":
main()
#####脚本使用说明######
#1. 首先定义好脚本中的邮箱账号和密码
#2. 脚本执行命令为:python mail.py 目标邮箱 "邮件主题" "邮件内容"
然后再编写mail.sh脚本,这个脚本是做告警收敛的,因为之后会设定1分钟进行监控一次,所以主要用于控制当告警持续了10分钟后才发送告警邮件,这个脚本和mail.py一样也是放在mail/目录下,脚本内容如下:
#!/bin/bash
# 拿到当前脚本的第一个参数,也就是之前在子脚本中定义的监控项的标识与机器IP
log=$1
# 当前的时间戳
t_s=`date +%s`
# 两个小时之前的时间戳
t_s2=`date -d "2 hours ago" +%s`
# 判断日志是否不存在
if [ ! -f /tmp/$log ]
then
# 如果日志不存在就生成一个日志,这个日志的第一行就是两个小时之前的时间戳
echo $t_s2 > /tmp/$log
fi
# 截取日志文件的最后一行,也就是拿出上一次的时间戳
t_s2=`tail -1 /tmp/$log|awk '{print $1}'`
# 把当前的时间戳写入到日志里
echo $t_s>>/tmp/$log
# 计算两个时间戳的时间差
v=$[$t_s-$t_s2]
echo $v
# 如果时间差大于一个小时,也就是第一次出现告警
if [ $v -gt 3600 ]
then
# 就发送告警邮件
/usr/bin/python /usr/local/sbin/mon/mail/mail.py $1 $2 $3
# 并且生成一个新的日志,用于记录告警持续的次数
echo "0" > /tmp/$log.txt
else
# 时间差小于一小时就代表不是第一次告警了,所以判断一下log.txt文件是否存在
if [ ! -f /tmp/$log.txt ]
then
# 不存在就生成一个
echo "0" > /tmp/$log.txt
fi
# 拿出log.txt文件的内容
nu=`cat /tmp/$log.txt`
# 进行计数
nu2=$[$nu+1]
# 然后再写入到log.txt中,其实这个文件就相当于是一个计数器
echo $nu2>/tmp/$log.txt
# 判断计的数是否大于10
if [ $nu2 -gt 10 ]
then
# 是的话就发送一个告警邮件说明告警持续10分钟了
/usr/bin/python /usr/local/sbin/mon/mail/mail.py $1 "trouble continue 10 min $2" "$3"
# 重新计数
echo "0" > /tmp/$log.txt
fi
fi
20.26 运行告警系统
想要正常的运行这个告警系统你需要写一个任务计划,每分钟执行一次 main.sh 主脚本:
[root@localhost ~]# crontab -e
* * * * * cd /usr/local/sbin/mon/bin; bash main.sh
不过,我这里由于只是做实验就不写以上这个任务计划了,直接手动运行,方便一会进行测试。在这之前需要修改一下配置文件,将to_mon_502改为0,因为本实验环境中并没有502.sh脚本里定义的站点目录,所以打开这个监控项的话会报错,然后还需要注释掉 main.sh 主脚本中的写入日志那句代码:
[root@localhost ~]# cd /usr/local/sbin/mon/conf
[root@localhost /usr/local/sbin/mon/conf]# vim mon.conf
# 改为 0
to_mon_502=0
[root@localhost /usr/local/sbin/mon/conf]# cd ../bin
[root@localhost /usr/local/sbin/mon/bin]# vim main.sh
# 注释这一句代码
# exec 1>>../log/mon.log 2>>../log/err.log
完成以上操作后,执行 main.sh 主脚本:
[root@localhost /usr/local/sbin/mon/bin]# sh main.sh
2017-11-29 00:03:39 load average
00:03:39 load is 0
[root@localhost /usr/local/sbin/mon/bin]#
运行结果如上,没有出现报错信息,代表 load.sh 能够被正常执行。
以上已经验证load.sh 可以正常被执行了,接下来我们再来测试一下能否正常发送邮件,首先修改一下load.sh中的判断条件,修改成小于10,因为要故意让它报警:
[root@localhost ~]# cd /usr/local/sbin/mon/shares/
[root@localhost /usr/local/sbin/mon/shares]# vim load.sh
if [ $load -lt 10 ] && [ $send -eq "1" ]
然后到bin目录下执行11次主脚本,因为我没有定义任务计划,所以需要模拟一分钟执行一次的情况,执行到第11次的时候会停顿一下,这是因为在发送邮件中:
[root@localhost /usr/local/sbin/mon/shares]# cd ../bin/
[root@localhost /usr/local/sbin/mon/bin]# sh main.sh
......执行11次
测试结果是邮件能够正常收到,内容如下图:
完结:以上就是如何使用shell语言去编写一个简单监控系统,并且经过简单的测试证明能够正常使用,如果实验过程中遇到问题欢迎私信。