shell中的函数、shell中的数组、告警系统需求分析、告警系统主脚本、告警系统配置文件、告警系统监控项目、告警系统邮件引擎、运行告警系统

shell中的函数

  • 把一段代码整理到了一个小单元中,并给这个小单元起一个名字,当用到这段代码时直接调用这个小单元的名字即可。
  • 函数就是一个子shell,就是一个代码段,定义完函数就可以引用它
  • 格式: function 后是函数的名字,并且 function 这个单词是可以省略掉的;花括号{} 里面为具体的命令
格式: function f_name() {
                      command
             }
函数必须要放在最前面

示例1:这个函数是用来打印参数

[root@aminglinux shell]# vim fun1.sh
[root@aminglinux shell]# cat fun1.sh
#!/bin/bash
input(){
     echo $1 $2 $0 $#
}
input 1 a b 9
[root@aminglinux shell]# sh -x fun1.sh
+ input 1 a b 9
+ echo 1 a fun1.sh 4
1 a fun1.sh 4
函数,可以直接写在脚本内,相当于直接调用
內建变量
$1 第一个参数
$2 第二个参数
...
~
$# 参数名字
$0 总共有几个参数
[root@aminglinux shell]# vim fun1.sh
[root@aminglinux shell]# cat fun1.sh
#!/bin/bash
input(){
     echo $1 $2 $0 $#
}
input $1 $2
[root@aminglinux shell]# sh fun1.sh 
fun1.sh 0
[root@aminglinux shell]# sh fun1.sh 1 4
1 4 fun1.sh 2

**示例2:**用于定义加法的函数,shell中定义的函数,必须放在上面

  • 在shell里面需要优先定义函数,比如在调用这个函数的时候,函数还没有定义,就会报错
  • 在想要调用哪一个函数,就必须在调用语句之前,先定义这个函数
[root@aminglinux shell]# vim fun2.sh
[root@aminglinux shell]# cat fun2.sh
#!/bin/bash
sum() {
    s=$[$1+$2]
#定义变量s = $1+$2 /其中 $1为第一个参数,$2为第二个参数
    echo $s
}
sum 2 6
#输出 第一个参数和第二个参数
[root@aminglinux shell]# sh -x fun2.sh
+ sum 2 6
+ s=8
+ echo 8
8

**例3:**显示IP,输入网卡的名字,然后显示网卡的IP

[root@aminglinux shell]# vim fun3.sh
[root@aminglinux shell]# cat fun3.sh
#!/bin/bash
ip() {
    ifconfig |grep -A1 "$eth: " |awk '/inet/ {print $2}'
#查看网卡,过滤出ens33及下面的一行,匹配inet行并打印出第二段
}
read -p "Please input the eth name: " eth
myip=`ip $eth`
echo "$eth address is $myip"
  • grep -A1 "ens33" 过滤显示出关键词及关键词下的一行
[root@aminglinux shell]# ifconfig |grep -A1 "ens33"
ens33: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 192.168.222.110  netmask 255.255.255.0  broadcast 192.168.222.255
[root@aminglinux shell]# ifconfig |grep -A1 "ens33"|tail -1
        inet 192.168.222.110  netmask 255.255.255.0  broadcast 192.168.222.255
[root@aminglinux shell]# ifconfig |grep -A1 "ens33"|tail -1|awk '{print $2}'
192.168.222.110

**案例4:**判定是否为本机的网卡,判定输入的网卡是否有IP

[root@aminglinux shell]# vim fun4.sh
[root@aminglinux shell]# cat fun4.sh
#!/bin/bash
#coding:utf8
ip()
{
    a=`ifconfig |grep -A1 "$eth: " |tail -1 |awk '{print $2}'`
    if [ -z "$a" ]
    then
        echo $eth
        echo "没有这个网卡名"
        exit
    fi
    echo $a
}
read -p "请输入你的网卡名字: " eth
myip=`ip $eth`
echo "$eth address is $myip"
[root@aminglinux shell]# sh -x fun4.sh
+ read -p '请输入你的网卡名字: ' eth
请输入你的网卡名字: ens33
++ ip ens33
+++ grep -A1 'ens33: '
+++ awk '{print $2}'
+++ tail -1
+++ ifconfig
++ a=192.168.222.110
++ '[' -z 192.168.222.110 ']'
++ echo 192.168.222.110
+ myip=192.168.222.110
+ echo 'ens33 address is 192.168.222.110'
ens33 address is 192.168.222.110
[root@aminglinux shell]# sh fun4.sh
请输入你的网卡名字: ens 38
ens 38 address is ens 38
没有这个网卡名

shell中的数组

  • 一串数字或一串字符串的形式,形成一个变量

  • shell中的数组1

    定义数组 a=(1 2 3 4 5); echo ${a[@]}
      echo ${#a[@]} 获取数组的元素个数
      echo ${a[2]} 读取第三个元素,数组从0开始
      echo ${a[*]} 等同于 ${a[@]} 显示整个数组
  • 数组赋值

    a[1]=100; echo ${a[@]}
      a[5]=2; echo ${a[@]} 如果下标不存在则会自动添加一个元素
  • 数组的删除

    uset a; unset a[1]
  • shell中的数组2

  • 数组分片

    a=(`seq 1 10`)
      echo ${a[@]:0:3} 从第一个元素开始,截取3个
      echo ${a[@]:1:4} 从第二个元素开始,截取4个
      echo ${a[@]:0-3:2} 从倒数第3个元素开始,截取2个
  • 数组替换

    echo ${a[@]/3/100}
      a=(${a[@]/3/100})
  • 定义数组:

[root@aminglinux shell]# a=(1 2 3 4 5 6)
[root@aminglinux shell]# echo ${a[@]}  //@、*都可以打印数组所有元素
1 2 3 4 5 6
  • 取数组的元素:
[root@aminglinux shell]# echo ${a[2]}  //数组第三个元素,从0算起:a[0] a[1] a[2]
3
[root@aminglinux shell]# echo ${#a[@]}  //数组的元素个数
6
  • 数组元素赋值、覆盖
[root@aminglinux shell]# a[1]=100
[root@aminglinux shell]# echo ${a[@]}
1 100 3 4 5 6
[root@aminglinux shell]# a[6]=2   
[root@aminglinux shell]# echo ${a[@]}   //如果下标不存在,则自动添加一个元素,存在则覆盖
1 100 3 4 5 6 2
  • 删除数组:

  • [root@aminglinux shell]# unset a[1]
  • [root@aminglinux shell]# echo ${a[@]}
  • 1 3 4 5 6 2
  • [root@aminglinux shell]# unset a
  • [root@aminglinux shell]# echo ${a[@]}
  •  
  • [root@aminglinux shell]#
  • 数组分片:截取某几个元素

[root@aminglinux shell]# a=(`seq 1 10`)
[root@aminglinux shell]# echo ${a[@]:0:3} //从第1个元素开始,取3个元素
1 2 3
[root@aminglinux shell]# echo ${a[@]:1:4} //从第2个元素开始,取4个元素
2 3 4 5
[root@aminglinux shell]# echo ${a[@]:0-3:2} //从倒数第3个元素开始,取2个元素
8 9
  • 数组替换
[root@aminglinux shell]# echo ${a[@]/3/100}
1 2 100 4 5 6 7 8 9 10
[root@aminglinux shell]# a=(${a[@]/3/100}) //直接赋值(需要用括号括起来)
[root@aminglinux shell]# echo ${a[@]}
1 2 100 4 5 6 7 8 9 10

告警系统需求分析

  • 需求:使用shell定制各种个性化告警工具,但需要统一化管理、规范化管理。
  • 思路:指定一个脚本包,包含主程序、子程序、配置文件、邮件引擎、输出日志等。
  • 主程序:作为整个脚本的入口,是整个系统的命脉。
  • 配置文件:是一个控制中心,用它来开关各个子程序,指定各个相关联的日志文件。
  • 子程序:这个才是真正的监控脚本,用来监控各个指标。
  • 邮件引擎:是由一个python程序来实现,它可以定义发邮件的服务器、发邮件人以及发件人密码
  • 输出日志:整个监控系统要有日志输出。
  • 要求:我们的机器角色多种多样,但是所有机器上都要部署同样的监控系统,也就说所有机器不管什么角色,整个程序框架都是一致的,不同的地方在于根据不同的角色,定制不同的配置文件。

** 程序架构:**

  • bin下是主程序
  • conf下是配置文件
  • shares下是各个监控脚本
  • mail下是邮件引擎
  • log下是日志。

告警系统主脚本

  • 定义监控系统的各个目录,然后再去定义主脚本,因为是分布式的,所以需要每一台机器都需要定义,事先创建好各个脚本和各个目录,随后脚本直接拷贝过去即可,然后再去做一些更改

  • 所有的shell脚本放到 /usr/local/sbin/ 目录下,方便查找

  • 切换到 /usr/local/sbin/ 目录下,并创建子目录

[root@aminglinux ~]# cd /usr/local/sbin/
[root@aminglinux sbin]# mkdir mon
[root@aminglinux sbin]# cd mon
[root@aminglinux mon]# mkdir bin conf shares mail log
[root@aminglinux mon]# ls
bin  conf  log  mail  shares
  • 切换到 bin 目录下(主脚本放在 bin 目录下,主脚本作为一个入口,应该去判断配置文件,查看某监控项目是否需要监控,还需调用各个需要监控的子脚本)
[root@aminglinux mon]# cd bin
[root@aminglinux bin]# vim main.sh
#!/bin/bash  
export send=1   //shell环境变量,是否发送邮件的开关
export addr=`/sbin/ifconfig |grep -A1 "ens33: "|awk '/inet/ {print $2}'` //把监控的机器的ip地址过滤出来
dir=`pwd`  //获取当前脚本的目录,保证后面能够找到配置文件和子脚本
last_dir=`echo $dir|awk -F'/' '{print $NF}'`  //只需要最后一级目录名
if [ $last_dir == "bin" ] || [ $last_dir == "bin/" ]  //判断目的是保证执行脚本的时候,我们在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    //执行负载监控子脚本
if grep -q 'to_mon_502=1' $conf_file  //先检查配置文件中是否需要监控502
then
    export log=`grep 'logfile=' $conf_file |awk -F '=' '{print $2}' |sed 's/ //g'` //过滤web的访问日志的路径给环境变量log
    /bin/bash  ../shares/502.sh   //执行502监控子脚本
fi

告警系统配置文件

  • 配置文件路径在: /mon/conf/mon.conf
[root@aminglinux mon]# cd conf/
[root@aminglinux 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 是否监控mysql
db_ip=10.20.3.13 

db_port=3315
db_user=username
db_pass=passwd

to_mon_httpd=0  // httpd  如果是1则监控,为0不监控

to_mon_php_socket=0  // php 如果是1则监控,为0不监控

to_mon_502=1   // http_code_502  
logfile=/data/log/xxx.xxx.com/access.log  //需要定义访问日志的路径

to_mon_request_count=0  //request_count数监控  
req_log=/data/log/www.discuz.net/access.log  //定义访问日志路径以及域名
domainname=www.discuz.net
  • 把请求日志摘出来的目的,你要考虑到要想把shell写得规范化,标准化,那你肯定要考虑监控的机器肯定不止1台;要想要让脚本通用,兼容性很强,就需要把所有需要监控的服务的日志都载入到配置文件中,改动起来方便,省得后期改动起来一个一个的对应脚本去修改,就太麻烦了

告警系统监控项目

  • 定义子脚本,就是监控项目
  • 系统负载的子脚本:/mon/shares/load.sh
[root@aminglinux conf]# cd ../shares/
[root@aminglinux shares]# vim load.sh

#! /bin/bash
load=`uptime |awk -F 'average:' '{print $2}'|cut -d',' -f1|sed 's/ //g' |cut -d. -f1`  //过滤系统一分钟的负载
if [ $load -gt 10 ] && [ $send -eq "1" ]  //负载大于10,发送邮件
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"

web服务状态码502的监控脚本:/mon/shares/502.sh

[root@aminglinux shares]# vim 502.sh
#! /bin/bash 502.sh 
d=`date -d "-1 min" +%H:%M`  //记录上一分钟的时间
c_502=`grep :$d:  $log  |grep ' 502 '|wc -l`  //统计上一分钟,502状态码出现的次数
if [ $c_502 -gt 10 ] && [ $send == 1 ]  //一分钟出现大于10次
then 
     echo "$addr $d 502 count is $c_502">../log/502.tmp  //记录502次数写进文件保存
     /bin/bash ../mail/mail.sh [email protected] "$addr\_502 $c_502"  `cat ../log/502.tmp`  //发送邮件动作
fi
echo "`date +%T` 502 $c_502"
  • (磁盘使用率):思路就是挨个把分区看下
  • 磁盘空间的子脚本:/mon/shares/disk.sh
[root@aminglinux shares]# vim disk.sh
#! /bin/bash
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" ]  //如果超过90%则告警,发邮件
    then
       echo "$addr `date +%T` disk useage is $r" >>../log/disk.tmp  //超过90%的告警信息写进记录文件
    fi
    if [ -f ../log/disk.tmp ] //如果告警记录文件存在
    then
       df -h >> ../log/disk.tmp //把所有分区信息也写入记录文件
       /bin/bash ../mail/mail.sh [email protected] "$addr\_disk $r" `cat ../log/disk.tmp`  //发邮件动作
       echo "`date +%T` disk useage is No ok"
    else
       echo "`date +%T` disk useage is ok"
    fi
done
  • awk指定多个分隔符
[root@aminglinux shares]# echo "12:aa#123bb:22#ww" |awk -F '[:#]' '{print $3}'
123bb
[root@aminglinux shares]# echo "12:aa#123bb:22#ww" |awk -F '[:#]' '{print NF}'
5
[root@aminglinux shares]# echo "12:aa#123bb:22##ww" |awk -F '[:#]' '{print NF}'
6
[root@aminglinux shares]# echo "12:aa#123bb:22##ww" |awk -F '[:#]+' '{print NF}'
5

告警系统邮件引擎

  • 告警系统邮件引擎由两个文件组成,放在/mon/mail/目录下:mail.pymail.sh

    mail.py:是邮件的核心python脚本,邮件功能的实现
      mail.sh:是告警邮件系统的shell脚本,实现了邮件收敛的功能,里面调用了mail.py脚本发送邮件的操作。
  • mail.sh:邮件收敛即同一监控项目告警,每一小时

[root@aminglinux shares]# cd ../mail/
[root@aminglinux mail]# vim mail.sh

log=$1    //第一个参数$1:是收件人邮箱
t_s=`date +%s`  //告警触发时间的时间戳
t_s2=`date -d "2 hours ago" +%s`   //告警触发时间的前2个小时的时间戳,第一次产生告警时,用作来对比时间差值
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 ]   //如果两次告警间隔了1小时或以上了(或第一次运行脚本),才会进入发邮件的流程,老发邮件会被骂死的
then
    ./mail.py  $1  $2  $3   //发邮件操作
    echo "0" > /tmp/$log.txt   //告警计数文件清0,重新开始计数
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 ]  //10分钟内连续告警或者10次告警后,需要发邮件
    then
         ./mail.py  $1 "trouble continue 10 min $2" "$3"  
         echo "0" > /tmp/$log.txt  
    fi
fi
  • 该脚本运用于,间隔3600 故障;10分钟内故障;间歇性故障;
  • 核心判断:计时、计数

运行告警系统

  • 我们使用任务计划来运行这个告警系统,计划每分钟运行一次
crontab -e //编辑任务计划

* * * * * cd /usr/local/sbin/mon/bin ; bash main.sh   //先进入主脚本的目录,再执行脚本
  • 可以预先自己先手动执行一次脚本,用-x选项查看脚本执行过程,看有无报错
sh -x main.sh
  • 监控发送邮件的部分,尽量少用空格,因为mail.py发送邮件是以 空格来定义三个参数的

猜你喜欢

转载自blog.csdn.net/xou6363/article/details/81276774