20.16/20.17 shell中的函数
20.18 shell中的数组
20.19 告警系统需求分析
20.20 告警系统主脚本
20.21 告警系统配置文件
20.22 告警系统监控项目
20.23/20.24/20.25 告警系统邮件引擎
20.26 运行告警系统
20.16/20.17 shell中的函数
•函数就是把一段代码整理到了一个小单元中,并给这个小单元起一个名字,当用到这段代码时直接调用这个小单元的名字即可。
格式: (function可以省略掉不写,f_name表示定义函数名)
function f_name() {
command
}
#函数必须要放在最前面
• 示例1
定义系统参数变量
vim fun.sh
#!/bin/bash
input() {
echo $1 $2 $# $0
}
input 1 a b
#这里input后面跟具体的参数
sh
•示例2
两个参数相加求和
vim fan2.sh
#!/bin/bash
sum() {
s=$[$1+$2]
echo $s
}
sum 1 2
•示例3
输入网卡名字显示ip地址
#!/bin/bash
ip() {
ifconfig |grep -A1 "$1 " |tail -1 |awk '{print $2}'|awk -F':' '{print $2}'
}
read -p "Please input the eth name: " e
myip=`ip $e`
echo "$e address is $myip"
改进:1、判断是否是系统的网卡
2、判断网卡是否有ip
20.18 shell中的数组
#应用场景不多
•定义数组
a=(1 2 3 4 5)
echo ${a[@]}
#将a数组的所有元素显示
echo ${#a[@]} 获取数组的元素个数
echo ${a[2]} 读取第三个元素,数组从0开始算第一个元素
echo ${a[*]} 等同于 ${a[@]} 显示整个数组
• 数组赋值
a[1]=100 #将a数组的第一个元素赋值100
echo ${a[@]} #显示所有a的元素
a[5]=2; echo ${a[@]}
#将a的第五个元素赋值2,并信使所有元素
#如果下标(就是第几个元素的标记)不存在则会自动添加一个元素
• 数组的删除
uset a #删除整个a数组
unset a[1] #删除a的第一个元素
•数组分片(截取)
a=(`seq 1 5`);echo ${a[*]} #用seq进行元素赋值并显示
echo ${a[@]:0:3} #从第一个元素开始,截取3个元素
echo ${a[@]:1:4} #从第二个元素开始,截取4个元素
echo ${a[@]:0-3:2} #从倒数第3个元素开始,截取2个元素
• 数组替换
echo ${a[@]/3/100} #将a的第三个元素替换成100
等同于
a=(${a[@]/3/100}) #将a的第三个元素替换成100
20.19 告警系统需求分析
shell项目-告警系统
• 需求:使用shell定制各种个性化告警工具,但需要统一化管理、规范化管理。
• 思路:指定一个脚本包,包含主程序、子程序、配置文件、邮件引擎、输出日志等。
-
主程序:作为整个脚本的入口,是整个系统的命脉。
-
配置文件:是一个控制中心,用它来开关各个子程序,指定各个相关联的日志文件。
-
子程序:这个才是真正的监控脚本,用来监控各个指标。
-
邮件引擎:是由一个python程序来实现,它可以定义发邮件的服务器、发邮件人以及发件人密码
-
输出日志:整个监控系统要有日志输出。
•要求:我们的机器角色多种多样,但是所有机器上都要部署同样的监控系统,也就说所有机器不管什么角色,整个程序框架都是一致的,不同的地方在于根据不同的角色,定制不同的配置文件。
• 程序架构:
bin下是主程序
conf下是配置文件
shares下是各个监控脚本
mail下是邮件引擎
log下是日志。
创建相关目录
cd /usr/local/sbin/
mkdir mon
cd mon/
mkdir bin conf shares mail log
20.20 告警系统主脚本
cd /usr/local/sbin/mon/bin/
vim main.sh
• main.sh内容
#!/bin/bash
# 是否发送邮件的开关(如果系统处于维护的状态,应该把发送邮件开关关掉)
export send=1
# 过滤ip地址(实验网卡是ens33)
export addr=`/sbin/ifconfig |grep -A1 "ens33: "|awk '/inet/ {print $2}'`
dir=`pwd`
# 找当前主脚本所在的目录,只需要最后一级目录名
last_dir=`echo $dir|awk -F'/' '{print $NF}'`
# 下面的if判断为了保证执行脚本的时候,我们在bin目录里,不然监控脚本、邮件和日志很有可能找不到
if [ $last_dir == "bin" ] || [ $last_dir == "bin/" ]; then
conf_file="../conf/mon.conf"
#创建配置文件路径
else
echo "you shoud cd bin dir"
exit
fi
#定义log日志路径
exec 1>>../log/mon.log 2>>../log/err.log
#打一个日期标记求出系统负载
echo "`date +"%F %T"` load average"
#直接执行子脚本load.sh
/bin/bash ../shares/load.sh
#先检查配置文件中是否需要监控502
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 告警系统配置文件
cd /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,1监控cdb数据库,0不监控cdb数据库
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 需要定义访问日志的路径
#1是监控502状态码,0是关闭
to_mon_502=0
logfile=/data/log/xxx.xxx.com/access.log
## request_count 定义日志路径以及域名
to_mon_request_count=0
#监控请求数,1监控,0不监控
req_log=/data/log/www.discuz.net/access.log
#定义请求log日志路径
domainname=www.discuz.net
#请求log日志内涉及到的域名
20.22 告警系统监控项目
• load.sh内容(监控系统负载)
cd /usr/local/sbin/mon/shares/
vim load.sh
#! /bin/bash
#查看系统负载的值
load=`uptime |awk -F 'average:' '{print $2}'|cut -d',' -f1|sed 's/ //g' |cut -d. -f1`
#if判断系统负载大于10并且send的开关是打开的,进行操作,然后指定内容放到临时文件,然后再通过邮件发送
if [ $load -gt 10 ] && [ $send -eq "1" ]
then
echo "$addr `date +%T` load is $load" >../log/load.tmp
/bin/bash ../mail/mail.sh languoxin_test@163.com "$addr\_load:$load" `cat ../log/load.tmp`
fi
echo "`date +%T` load is $load"
• 502.sh内容(监控502状态码)
cd /usr/local/sbin/mon/shares/
vim 502.sh
#! /bin/bash
d=`date -d "-1 min" +%H:%M`
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"
• disk.sh内容(监控磁盘使用率)
cd /usr/local/sbin/mon/shares/
vim disk.sh
#! /bin/bash
rm -f ../log/disk.tmp
#定义系统语言为英文,目的是为了下面df -h 过滤掉Use,如果是中文的话是已用,没有过滤掉
LANG=en
#'[ %]+'以一个或者多个空格或者%作为分隔符,使用多个分隔符时用中括号 [ ]括起来
for r in `df -h |awk -F '[ %]+' '{print $5}'|grep -v Use`
do
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 告警系统邮件引擎
cd /usr/local/sbin/mon/mail/
vim mail.sh
•mail.sh内容 (主要做告警收敛)
#告警收敛:就是告完第一次警后,指定间隔多长时间才会再次告警
#其中mail.py内容到这里下载(也放在mail目录)https://coding.net/u/aminglinux/p/aminglinux-book/git/blob/master/D22Z/mail.py
log=$1
#定义时间戳
t_s=`date +%s`
#定义两个小时前时间戳(为了后面的if判断时间差)
t_s2=`date -d "2 hours ago" +%s`
#if判断log日志如果不存在,将t_s2的时间戳放入log日志
if [ ! -f /tmp/$log ]
then
echo $t_s2 > /tmp/$log
fi
#第一次执行mail,生成log日志,内容是两个小时前时间戳,这次定义变量t_s2,会覆盖上次定义的t_s2
t_s2=`tail -1 /tmp/$log|awk '{print $1}'`
#将当前时间戳写入log日志
echo $t_s>>/tmp/$log
#定义变量值为当前时间戳与两小时前时间戳的时间差
v=$[$t_s-$t_s2]
echo $v
#if判断告警时间差大于一个小时,则发生告警($log.txt相当于计数器)
if [ $v -gt 3600 ]
then
#mail.py的三个参数是发送给谁,邮件主题,邮件内容
./mail.py $1 $2 $3
echo "0" > /tmp/$log.txt
else
#if判断$log.txt如果不存在,创建并重置
if [ ! -f /tmp/$log.txt ]
then
echo "0" > /tmp/$log.txt
fi
#定义nu为查看$log.txt里面的数字
nu=`cat /tmp/$log.txt`
#定义nu2位nu的数字加一
nu2=$[$nu+1]
echo $nu2>/tmp/$log.txt
#if判断nu2计数器大于10,则发送邮件,提示计时持续了10分钟
if [ $nu2 -gt 10 ]
then
./mail.py $1 "trouble continue 10 min $2" "$3"
#$log.txt计数器被重置
echo "0" > /tmp/$log.txt
fi
fi
#mail.sh是一分钟运行一次的
第一种场景:
某监控一直出现问题,mail.sh运行了11次,发送邮件(mail.sh每次运行一次,计数器加一,11次后满足if判断发送邮件的要求)
第二种场景:
假如502监控前两次运行mial.sh出现问题,在mail.sh第三次运行时正常了,计数器停在2;然而第八次运行时又开始告警(由于502次数大于10),此时计数器是3,再运行mail.sh八次,会发送邮件告警计数器持续十分钟;等过了一个小时后,时间差超过3600,才发送一封邮件告警时间差大于一小时,然后重置计数器
20.26 运行告警系统
一般是写一个任务计划cron
crontab -e
* * * * * cd /usr/local/sbin/mon/bin;bash main.sh
#本次实验不需要定义log日志,可以先将main.sh定义log的语句注释掉
手动运行main.sh脚本