20.16 shell中的函数(上)
$0 取脚本的名称 $# 取当前脚本的参数
20.17 shell中的函数(下)
网卡名冒号后面记得加空格
20.18 shell中的数组
20.19 告警系统需求分析
20.20 告警系统主脚本
•main.sh内容
#!/bin/bash
#Written by aming.
# 是否发送邮件的开关
export send=1
# 过滤ip地址
export addr=`/sbin/ifconfig |grep -A1 "ens33: "|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
echo "you shoud cd bin dir"
exit
fi
exec 1>>../log/mon.log 2>>../log/err.log
echo "`date +"%F %T"` load average"
/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 告警系统配置文件
•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 告警系统监控项目
•load.sh内容
#! /bin/bash
##Writen by aming##
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 aming_test@163.com "$addr\_load:$load" `cat ../log/load.tmp`
fi
echo "`date +%T` load is $load"
•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内容
#! /bin/bash
##Writen by aming##
rm -f ../log/disk.tmp
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/24/25 告警系统邮件引擎(上,中,下)
• mail.sh内容 //其中mail.py内容到这里下载https://coding.net/u/aminglinux/p/aminglinux-book/git/blob/master/D22Z/mail.py
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
./mail.py $1 $2 $3
echo "0" > /tmp/$log.txt
else
if [ ! -f /tmp/$log.txt ]
then
echo "0" > /tmp/$log.txt
fi
nu=`cat /tmp/$log.txt`
nu2=$[$nu+1]
echo $nu2>/tmp/$log.txt
if [ $nu2 -gt 10 ]
then
./mail.py $1 "trouble continue 10 min $2" "$3"
echo "0" > /tmp/$log.txt
fi
fi
20.26 运行告警系统
课堂串讲
20.16/20.17 shell中的函数
20.18 shell中的数组
20.19 告警系统需求分析
20.16/20.17 shell中的函数
函数就是把一段代码整理到了一个小单元中,并给这个小单元起一个名字,当用到这段代码时直接调用这个小单元的名字即可。定义函数必须要放在最前面。定义好的函数相当于是命令。
语法格式
1 2 3 | function f_name() { command } |
而可以把function给省略
1 2 3 | f_name() { command } |
打印相应的参数
1 2 3 4 5 6 7 8 9 10 11 | [root@kun05 shell]# vim fun1.sh #!/bin/bash function inp(){ echo "The first parameter is $1" echo "The second parameter is $2" echo "The third parameter is $3" echo "The script name is $0" echo "The number of parameter is $#" } inp 1 a 4.2 sdsf |
1 2 3 4 5 6 | [root@kun05 shell]# sh fun1.sh The first parameter is 1 The second parameter is a The third parameter is 4.2 The script name is fun1.sh The number of parameter is 4 |
也可以让参数再放到命令行里输入
1 2 3 4 5 6 7 8 9 | #!/bin/bash function inp(){ echo "The first parameter is $1" echo "The second parameter is $2" echo "The third parameter is $3" echo "The script name is $0" echo "The number of parameter is $#" } inp $1 $2 $3 |
1 2 3 4 5 6 | [root@kun05 shell]# sh fun1.sh 1 adf kun The first parameter is 1 The second parameter is adf The third parameter is kun The script name is fun1.sh The number of parameter is 3 |
定义加法函数
1 2 3 4 5 6 7 8 9 | [root@kun05 shell]# vim fun2.sh #!/bin/bash sum() { s=$[$1+$2] echo $s } sum 1 10 |
1 2 | [root@kun05 shell]# sh fun2.sh 11 |
输入网卡名字 打印出相应的IP地址
1 2 3 4 5 6 7 8 9 | [root@kun05 shell]# vim fun3.sh #!/bin/bash eth() { ifconfig |grep -A1 "$1: " |tail -1 |awk '{print $2}' } read -p "Please input the eth name:" a echo "`eth $a`" |
1 2 3 | [root@kun05 shell]# sh fun3.sh Please input the eth name:ens33 192.168.80.104 |
规范用户输入信息 并打出对应的IP地址 https://github.com/aminglinux/shell/blob/master/if_ip.txt
20.18 shell中的数组
数组其实就是变量,只不过变量里面是多个数字或者字符串。
-
数组其实就是变量,只不过变量里面是多个数字或者字符串。
-
语法格式
a=(元素1 元素2 元素3)
1
[root@kun05 shell]# a=(1 2 3 4)
-
调用数组
${a[@]}或者${a[*]}
1 2 3 4 5
[root@kun05 shell]# a=(1 2 3 4) [root@kun05 shell]# echo ${a[@]} 1 2 3 4 [root@kun05 shell]# echo ${a[*]} 1 2 3 4
-
查看对应下标的元素值 默认从0开始
${a[下标]}
1 2 3
[root@kun05 shell]# a=(1 2 3 4) [root@kun05 shell]# echo ${a[0]} 1
-
获取元素的个数
1 2 3
[root@kun05 shell]# a=(1 2 3 4) [root@kun05 shell]# echo ${#a[@]} 4
-
添加/更新元素
a[下标]=值
1 2 3
[root@kun05 shell]# a[0]=100 [root@kun05 shell]# echo ${a[@]} 100 2 3 4
-
删除元素
unset a[下标]
1 2 3
[root@kun05 shell]# unset a[0] [root@kun05 shell]# echo ${a[@]} 2 3 4
-
数组分片
${a[@]:下标:截取个数]
1 2 3 4 5 6 7 8 9
[root@kun05 shell]# b=(`seq 1 10`) [root@kun05 shell]# echo ${b[@]} 1 2 3 4 5 6 7 8 9 10 #从第六个元素起截取3个 [root@kun05 shell]# echo ${b[@]:5:3} 6 7 8 #从倒数第九个元素起截取2个 [root@kun05 shell]# echo ${b[@]:0-9:2} 2 3
-
数组替换
${a[@]/用来的值/替换后的值}
1 2
[root@kun05 shell]# echo ${b[@]/3/100} 1 2 100 4 5 6 7 8 9 10
-
20.19 告警系统需求分析
需求
:使用shell定制各种个性化告警工具,但需要统一化管理、规范化管理。
- 思路:指定一个脚本包,包含主程序、子程序、配置文件、邮件引擎、输出日志等。
主程序
:作为整个脚本的入口,是整个系统的命脉。配置文件
:是一个控制中心,用它来开关各个子程序,指定各个相关联的日志文件。子程序
:这个才是真正的监控脚本,用来监控各个指标。邮件引擎
:是由一个python程序来实现,它可以定义发邮件的服务器、发邮件人以及发件人密码输出日志
:整个监控系统要有日志输出
要求:我们的机器角色多种多样,但是所有机器上都要部署同样的监控系统,也就说所有机器不管什么角色,整个程序框架都是一致
的,不同的地方在于根据不同的角色
,定制不同的配置文件
。
程序架构:
bin
目录下是主程序conf
目录下是配置文件shares
目录下是各个监控脚本mail
目录下是邮件引擎log
目录下是日志。
20.20 告警系统主脚本
20.21 告警系统配置文件
20.22 告警系统监控项目
20.20 告警系统主脚本
根据程序的架构创建对应的目录
1 2 3 | [root@kun05 ~]# cd /usr/local/sbin/ [root@kun05 sbin]# cd mon/ [root@kun05 mon]# mkdir bin conf shares mail log |
/usr/local/sbin/
生成环境中脚本存放的目录,方便查找脚本
进人bin目录并创建主脚本main.sh
1 2 | [root@kun05 mon]# cd bin/ [root@kun05 bin]# vim main.sh |
添加下面代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | #!/bin/bash #export 表示该变量会应用到所有的子脚本中(主脚本所调用的脚本) #发送邮件的开关 export send=1 #发送邮件机器的ip地址 export addr=`/usr/sbin/ifconfig |grep -A1 "ens33" |tail -1 |awk '{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 echo "Please 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 if grep -q 'to_mon_502=1' $conf_file then #定义网站的访问日志路径 export log=`grep 'logfile=' $conf_file |awk -F '=' '{print $2}' |sed 's/ //g'` #调用502脚本 /bin/bash ../shares/502.sh fi |
20.21 告警系统配置文件
配置文件就是定义监控脚本的开关和相应的日志的路径 主脚本会去过滤配置文件 当监控的服务为1就会执行相应的子脚本
进人conf目录并创建配置文件mon.conf
1 2 3 | [root@kun05 bin]# cd .. [root@kun05 mon]# cd conf [root@kun05 conf]# vim mon.conf |
添加下面代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | ##to config 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 告警系统监控项目
进人shares目录
1 2 | [root@kun05 conf]# cd .. [root@kun05 mon]# cd shares/ |
创建load.sh 监控系统负载脚本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | [root@kun05 shares]# vim load.sh #!/bin/bash #当前系统负载值 load=`w |head -1 |awk -F 'load average: ' '{print $2}' |cut -d . -f 1` #判断主脚本是开启发送邮件和当前系统负载是否大于10 if [ $load -ge 10 ] && [ $send -eq "1" ] then #显示当前时间和负载值的文件 用于发送邮件的内容 echo "$addr `date +%T` load is $load" >../log/load.tmp #发送邮件 /bin/bash ../mail/mail.sh coco445566@163.com "$addr\_load:$load" `cat ../log/load.tmp` fi #把下面内容添加到主脚本中定义的日志中 echo "`date +%T` load is $load" |
创建502.sh 监控502脚本
1 2 3 4 5 6 7 8 9 10 11 12 13 | [root@kun05 shares]# vim 502.sh #!/bin/bash #定义一分钟前的时间 d=`date -d "-1 min" +%H:%M` #定义一分钟前访问日志中出现502的行数 c_502=`grep $d $log |grep '502' |wc -l` if [ $c_502 -ge 10 ] && [ $send == 1 ] then echo "$addr $d 502 count is $c_502" >../log/502.tmp /bin/bash ../mail/mail.sh coco445566@163.com "$addr\_502:$c_502" `cat ../log/502.tmp` fi echo "`date +%T`502 is $c_502" |
创建disk.sh 监控磁盘使用率脚本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | [root@kun05 shares]# vim disk.sh #!/bin/bash rm -f ../log/disk.tmp #[ %]+ 表示1个或者多个空格和%作为分隔符 for r in `df -h |awk -F '[ %]+' '{print $5}'|grep -v 已用` do if [ $r -ge 90 ] && [ $send == "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 coco445566@163.com "$addr\_disk:$r" `cat ../log/disk.tmp` echo "`date +%T` disk useage is nook" else echo "`date +%T` disk useage is ok" fi done |
20.23/20.24/20.25 告警系统邮件引擎
20.26 运行告警系统
20.23/20.24/20.25 告警系统邮件引擎
进入mial目录并创建mail.py 发邮件脚本
1 2 | [root@kun05 mon]# cd mail/ [root@kun05 mail]# vim mail.py |
添加下面代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | #!/usr/bin/env python #-*- coding: UTF-8 -*- import os,sys reload(sys) sys.setdefaultencoding('utf8') 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): gserver = 'smtp.qq.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] ##定义QQ邮箱的账号和密码,你需要修改成你自己的账号和密码(请不要把真实的用户名和密码放到网上公开,否则你会死的很惨) sendqqmail('1234567@qq.com','aaaaaaaaaa','1234567@qq.com',to,subject,content) if __name__ == "__main__": main() #####脚本使用说明###### #1. 首先定义好脚本中的邮箱账号和密码 #2. 脚本执行命令为:python mail.py 目标邮箱 "邮件主题" "邮件内容" |
修改gserver
sendqqmail
的邮件即可
内容在 https://coding.net/u/aminglinux/p/aminglinux-book/git/blob/master/D22Z/mail.py
创建mail.sh 调用发邮件和邮件收敛的脚本
1 | [root@kun05 mail]# vim mail.sh |
添加下面代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | #把mail.sh的第二个参数赋值给log log=$2 #当前时间的时间戳 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 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 ./mail.py $1 $2 $3 #定义报警计数器日志/清零计数器日志 echo "0" > /tmp/$log.txt else if [ ! -f /tmp/$log.txt ] then echo "0" > /tmp/$log.txt fi nu=`cat /tmp/$log.txt` nu2=$[$nu+1] echo $nu2>/tmp/$log.txt if [ $nu2 -gt 10 ] then ./mail.py $1 "trouble continue 10 min $2" $3 echo "0">/tmp/$log.txt fi fi |
/tmp/$log
是计时器日志 /tmp/$log.txt
是计数器日志
- 逻辑思路
- 第一次执行mail.sh t_s是当前时间戳 t_s2是两个小时前的时间戳 判断差值大于3600s 发邮件 并计数器为0
- 1分钟后再次执行mail.sh t_s是当前时间戳 t_s2为1分钟前的时间戳判断差值小于3600s 计数器为1
- 每分钟都执行mail.sh 到了第11分钟 因为 t_s和t_s2之前的差值都是小于3600s是不会发邮件的 当时 此时计数器为11 大于10 就发一份“问题持续10分钟”的邮件 并计数器清零
- 假如超多一个小时后再此执行mail.sh t_s和t_s2的时间戳 判断差值大于3600s 就会发邮件并会清空原来的计数器
20.26 运行告警系统
为了让系统每分钟都执行main.sh来监控,就使用任务计划
1 2 3 | [root@kun05 mail]# crontab -e * * * * * cd /usr/local/sbin/mon/bin;bash main.sh |