shell编程——告警系统

简介:

本文转自铭哥的帖子http://www.apelearn.com/bbs/thread-8106-1-1.html

本人附注对本shell告警系统的认识。

需求: 使用shell定制各种个性化告警工具,但需要统一化管理、规范化管理。

思路:指定一个脚本包,包含主程序、子程序、配置文件、邮件引擎、输出日志等。
主程序:作为整个脚本的入口,是整个系统的命脉。
配置文件:是一个控制中心,用它来开关各个子程序,指定各个相关联的日志文件。
子程序:这个才是真正的监控脚本,用来监控各个指标。
邮件引擎:是由一个php程序来实现,它可以定义发邮件的服务器、发邮件人以及收邮件人。
输出日志:整个监控系统要有日志输出。

要求:我们的机器角色多种多样,但是所有机器上都要部署同样的监控系统,也就说所有机器不管什么角色,整个程序框架都是一致的,不同的地方在于根据不同的角色,定制不同的配置文件。

程序架构:   

                                         (主目录 mon)
                 ____________________|_______________________________
                |              |                    |                       |                             |
               bin          conf            shares                  mail                         log
                |              |                    |                       |                             |
           [main.sh] [ mon.conf] [load.sh 502.sh]  [mail.php mail.sh] [  mon.log  err.log ]

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

1.  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
#!/bin/bash
#Written by aming.
 
# 是否发送邮件的开关
export  send=1     // 对send进行环境变量赋值:1为开,0为关。如果关闭则邮件不会发送,当你已经知道并着手时可以将此设置为0
# 过滤ip地址
export  addr=` /sbin/ifconfig  | grep  -A1  'eth0'  | grep  addr: | awk  '{print $2}' | awk  -F:  '{print $2}' `          // 对addr进行环境变量赋值:addr=本机IP
dir =` pwd `         //dir 为当前目录路径
# 只需要最后一级目录名
last_dir=` echo  $ dir | awk  -F '/'  '{print $NF}' `    //awk 出最后一段内容即当前目录
 
# 下面的判断目的是,保证执行脚本的时候,我们在bin目录里,不然监控脚本、邮件和日志很有可能找不到(但是我觉得这里有bug,万一main.sh的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   // 标准输出追加重定向到mon.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   //grep  -q 只作为判断条件,若1则对log进行环境变量赋值,即mon.conf中的 /data/log/xxx .xxx.com /access .log,执行502.sh脚本
     export  log=` grep  'logfile='  $conf_file | awk  -F  '='  '{print $2}'  | sed  's/ //g' `
     /bin/bash   .. /shares/502 .sh
fi

主程序只添加了502监控,根据需求我们可以在mon.conf和main.sh中按模板添加监控项目。但是需要将整个告警系统copy过去,保证系统的完整性。

2.  配置文件   mon.conf

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
## to config the options if to monitor
 
## cdb  主要定义mysql的服务器地址、端口以及user、password
to_mon_cdb=0    ##0 or 1, default 0,0 not monitor, 1 monitor
cdb_ip=10.20.3.13
cdb_port=3315
cdb_user=username
cdb_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

mon.conf配置文件还是很好理解的。

3.  load.sh

1
2
3
4
5
6
7
8
9
10
11
#! /bin/bash
 
##Writen by aming##
 
load=`uptime | awk  -F  'average:'  '{print $2}' | cut  -d ','  -f1| sed  's/ //g'  | cut  -d. -f1`   //load 是1分钟负载的整数部分数值
if  [ $load -gt 20 ] && [ $send - eq  "1"  ]   // 如果负载大于20(具体看自己系统硬件条件)且开启发送邮件则将“时间+负载”重定向到load.tmp,执行发送邮件脚本“$1 $2” $3,“$1 $2”是主题,$3是内容,跟mail.sh格式相同
then
     echo  "$addr `date +%T` load is $load"  >.. /log/load .tmp
     /bin/bash  .. /mail/mail .sh $addr\_load $load .. /log/load .tmp
fi
echo  "`date +%T` load is $load"    // 打印“时间+负载”

4.  502.sh

1
2
3
4
5
6
7
8
#! /bin/bash
d=` date  -d  "-1 min"  +%H:%M`     // 将一分钟前的时间赋值给d
c_502=` grep  :$d:  $log  | grep  ' 502 ' | wc  -l`   // 过滤统计 /data/log/xxx .xxx.com /access .log中502的次数
if  [ $c_502 -gt 10 ] && [ $send == 1 ];  then       // 如果502的次数超过10次,即502情况持续了10分钟,且邮件开,则将“IP+时间+502次数”重定向到502.tmp,发送邮件
      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"    // 打印“时间+502次数”

*扩展disk.sh

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#! /bin/bash
 
##Writen by aming##
 
rm  -f .. /log/disk .tmp
for  in  ` df  -h | awk  -F  '[ %]+'  '{print $5}' | grep  - v  Use`   // 过滤出磁盘Use的三个数据
do
     if  [ $r -gt 90 ] && [ $send - eq  "1"  ]      // 如果有数据大于90,且邮件开,则将“时间+Use值追加重定向到disk.tmp(遗憾的是不清楚是哪个盘Use大于90)
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

5. mail.php

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
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
<?php
class Smtp
{
     /* Public Variables */
     var $smtp_port;
     var $time_out;
     var $host_name;
     var $log_file;
     var $relay_host;
     var $debug;
     var $auth;
     var $user;
     var $pass;
     /* Private Variables */
     var $sock;
     /* Constractor */
     function  Smtp($relay_host =  "" , $smtp_port = 25,$auth =  false ,$user,$pass)
     {
         $this->debug = FALSE;
         $this->smtp_port = $smtp_port;
         $this->relay_host = $relay_host;
         $this->time_out = 30;  //is  used  in  fsockopen()
         #
         $this->auth = $auth; //auth
         $this->user = $user;
         $this->pass = $pass;
         #
         $this->host_name =  "localhost" //is  used  in  HELO  command
         $this->log_file =  "" ;
         $this->sock = FALSE;
     }
     /* Main Function */
     function  sendmail($to, $from, $subject =  "" , $body =  "" , $mailtype, $cc =  "" , $bcc =  "" , $additional_headers =  "" )
     {
         $mail_from = $this->get_address($this->strip_comment($from));
         $body = ereg_replace( "(^|(\r\n))(\.)" "\1.\3" , $body);
         $header =  "MIME-Version:1.0\r\n" ;
         if ($mailtype== "HTML" ){
             $header .=  "Content-Type:text/html\r\n" ;
         }
         $header .=  "To: " .$to. "\r\n" ;
         if  ($cc !=  "" ) {
             $header .=  "Cc: " .$cc. "\r\n" ;
         }
         $header .=  "From: $from<" .$from. ">\r\n" ;
         $header .=  "Subject: " .$subject. "\r\n" ;
         $header .= $additional_headers;
         $header .=  "Date: " . date ( "r" ). "\r\n" ;
         $header .=  "X-Mailer:By Redhat (PHP/" .phpversion(). ")\r\n" ;
         list($msec, $sec) = explode( " " , microtime());
         $header .=  "Message-ID: <" . date ( "YmdHis" , $sec). "." .($msec*1000000). "." .$mail_from. ">\r\n" ;
         $TO = explode( "," , $this->strip_comment($to));
         if  ($cc !=  "" ) {
             $TO = array_merge($TO, explode( "," , $this->strip_comment($cc)));
         }
 
         if  ($bcc !=  "" ) {
             $TO = array_merge($TO, explode( "," , $this->strip_comment($bcc)));
         }
         $sent = TRUE;
         foreach ($TO as $rcpt_to) {
             $rcpt_to = $this->get_address($rcpt_to);
 
             if  (!$this->smtp_sockopen($rcpt_to)) {
                 $this->log_write( "Error: Cannot send email to " .$rcpt_to. "\n" );
                 $sent = FALSE;
                 continue ;
             }
             if  ($this->smtp_send($this->host_name, $mail_from, $rcpt_to, $header, $body)) {
                 $this->log_write( "E-mail has been sent to <" .$rcpt_to. ">\n" );
             else  {
                 $this->log_write( "Error: Cannot send email to <" .$rcpt_to. ">\n" );
                 $sent = FALSE;
             }
             fclose($this->sock);
             $this->log_write( "Disconnected from remote host\n" );
         }
         return  $sent;
     }
/* Private Functions */
    function  smtp_send($helo, $from, $to, $header, $body =  "" )
     {
         if  (!$this->smtp_putcmd( "HELO" , $helo)) {
             return  $this->smtp_error( "sending HELO command" );
         }
         #auth
         if ($this->auth){
             if  (!$this->smtp_putcmd( "AUTH LOGIN" , base64_encode($this->user))) {
                 return  $this->smtp_error( "sending HELO command" );
             }
             if  (!$this->smtp_putcmd( "" , base64_encode($this->pass))) {
                 return  $this->smtp_error( "sending HELO command" );
             }
         }
         #
         if  (!$this->smtp_putcmd( "MAIL" "FROM:<" .$from. ">" )) {
             return  $this->smtp_error( "sending MAIL FROM command" );
         }
         if  (!$this->smtp_putcmd( "RCPT" "TO:<" .$to. ">" )) {
             return  $this->smtp_error( "sending RCPT TO command" );
         }
         if  (!$this->smtp_putcmd( "DATA" )) {
             return  $this->smtp_error( "sending DATA command" );
         }
         if  (!$this->smtp_message($header, $body)) {
             return  $this->smtp_error( "sending message" );
         }
         if  (!$this->smtp_eom()) {
             return  $this->smtp_error( "sending <CR><LF>.<CR><LF> [EOM]" );
         }
         if  (!$this->smtp_putcmd( "QUIT" )) {
             return  $this->smtp_error( "sending QUIT command" );
         }
         return  TRUE;
     }
 
     function  smtp_sockopen($address)
     {
         if  ($this->relay_host ==  "" ) {
             return  $this->smtp_sockopen_mx($address);
         else  {
             return  $this->smtp_sockopen_relay();
         }
     }
     function  smtp_sockopen_relay()
     {
         $this->log_write( "Trying to " .$this->relay_host. ":" .$this->smtp_port. "\n" );
         $this->sock = @fsockopen($this->relay_host, $this->smtp_port, $errno, $errstr, $this->time_out);
         if  (!($this->sock && $this->smtp_ok())) {
             $this->log_write( "Error: Cannot connenct to relay host " .$this->relay_host. "\n" );
             $this->log_write( "Error: " .$errstr. " (" .$errno. ")\n" );
             return  FALSE;
         }
         $this->log_write( "Connected to relay host " .$this->relay_host. "\n" );
         return  TRUE;
     }
     function  smtp_sockopen_mx($address)
     {
         $domain = ereg_replace( "^.+@([^@]+)$" "\1" , $address);
         if  (!@getmxrr($domain, $MXHOSTS)) {
             $this->log_write( "Error: Cannot resolve MX \"" .$domain. "\"\n" );
             return  FALSE;
         }
         foreach ($MXHOSTS as $host) {
             $this->log_write( "Trying to " .$host. ":" .$this->smtp_port. "\n" );
            $this->sock = @fsockopen($host, $this->smtp_port, $errno, $errstr, $this->time_out);
             if  (!($this->sock && $this->smtp_ok())) {
                 $this->log_write( "Warning: Cannot connect to mx host " .$host. "\n" );
                 $this->log_write( "Error: " .$errstr. " (" .$errno. ")\n" );
                continue ;
             }
             $this->log_write( "Connected to mx host " .$host. "\n" );
             return  TRUE;
         }
         $this->log_write( "Error: Cannot connect to any mx hosts (" .implode( ", " , $MXHOSTS). ")\n" );
        return  FALSE;
     }
     function  smtp_message($header, $body)
     {
       fputs($this->sock, $header. "\r\n" .$body);
         $this->smtp_debug( "> " .str_replace( "\r\n" "\n" . "> " , $header. "\n> " .$body. "\n> " ));
         return  TRUE;
     }
     function  smtp_eom()
    {
         fputs($this->sock,  "\r\n.\r\n" );
         $this->smtp_debug( ". [EOM]\n" );
         return  $this->smtp_ok();
     }
     function  smtp_ok()
    {
         $response = str_replace( "\r\n" "" , fgets($this->sock, 512));
         $this->smtp_debug($response. "\n" );
         if  (!ereg( "^[23]" , $response)) {
             fputs($this->sock,  "QUIT\r\n" );
             fgets($this->sock, 512);
             $this->log_write( "Error: Remote host returned \"" .$response. "\"\n" );
             return  FALSE;
         }
         return  TRUE;
     }
     function  smtp_putcmd($cmd, $arg =  "" )
     {
         if  ($arg !=  "" ) {
            if ($cmd== "" ) $cmd = $arg;
             else  $cmd = $cmd. " " .$arg;
         }
         fputs($this->sock, $cmd. "\r\n" );
         $this->smtp_debug( "> " .$cmd. "\n" );
         return  $this->smtp_ok();
     }
     function  smtp_error($string)
     {
         $this->log_write( "Error: Error occurred while " .$string. ".\n" );
         return  FALSE;
     }
     function  log_write($message)
     {
         $this->smtp_debug($message);
         if  ($this->log_file ==  "" ) {
                 return  TRUE;
         }
         $message =  date ( "M d H:i:s " ).get_current_user(). "[" .getmypid(). "]: " .$message;
         if  (!@file_exists($this->log_file) || !($fp = @fopen($this->log_file,  "a" ))) {
             $this->smtp_debug( "Warning: Cannot open log file \"" .$this->log_file. "\"\n" );
             return  FALSE;;
         }
         flock($fp, LOCK_EX);
         fputs($fp, $message);
         fclose($fp);
         return  TRUE;
     }
     function  strip_comment($address)
    {
         $comment =  "\([^()]*\)" ;
        while  (ereg($comment, $address)) {
             $address = ereg_replace($comment,  "" , $address);
         }
         return  $address;
     }
     function  get_address($address)
     {
         $address = ereg_replace( "([ \t\r\n])+" "" , $address);
         $address = ereg_replace( "^.*<(.+)>.*$" "\1" , $address);
         return  $address;
     }
     function  smtp_debug($message)
     {
         if  ($this->debug) {
         echo  $message;
         }
     }
}
$ file  = $argv[2];
$smtpserver =  "smtp.qq.com" ; //SMTP 服务器
$smtpserverport =  "25" ; //SMTP 服务器端口
$smtpusermail =  "1198658@qq.com" ; //SMTP 服务器的用户邮箱
$smtpemailto =  "lishi@139.com" ; // 发送给谁
$smtpuser =  "1198658" ; //SMTP 服务器的用户帐号
$smtppass =  "1212lss" ; //SMTP 服务器的用户密码(这个密码是邮箱的独立秘密,而不是邮箱的登陆密码)
$mailsubject = $argv[1]; // 邮件主题
$mailbody = file_get_contents($ file ); // 邮件内容
$mailtype =  "HTML" ; // 邮件格式(HTML /TXT ),TXT为文本邮件
$smtp = new smtp($smtpserver,$smtpserverport, true ,$smtpuser,$smtppass); // 这里面的一个 true 是表示使用身份验证,否则不使用身份验证.
// $smtp->debug = TRUE; // 是否显示发送的调试信息
$smtp->sendmail($smtpemailto, $smtpusermail, $mailsubject, $mailbody, $mailtype);
?>

好吧,不懂php,只能将下面的参数理一理。

要想发邮件的话,首先要有php支持,若你没有安装过lamp或者lnmp,则需要运行yum install -y php 安装。
然后运行  php  mail.php  "邮箱主题写在这里" "/tmp/test.txt"   。其中,/tmp/test.txt 内容为邮件内容。

6. 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
    
     log=$1   // $1是发送邮件时的$1,比如502.sh中的$addr\_502
     t_s=` date  +%s`   // 记录当前时间
     t_s2=` date  -d  "2 hours ago"  +%s`   // 记录2个小时之前的时间
     if  [ ! -f  /tmp/ $log ]    // 如果文件不存在,则将2个小时之前的时间重定向到这个文件
     then
         echo  $t_s2 >  /tmp/ $log
     fi
     t_s2=` tail  -1  /tmp/ $log| awk  '{print $1}' `   // 如果文件存在,将这个文件最后一个时间赋值给ts_2
     echo  $t_s>> /tmp/ $log  // 将当前时间追加重定向到这个文件中
     v =$[$t_s-$t_s2]          // 记录俩次时间的间隔
         echo  $ v
     if  [ $ v  -gt 3600 ] // 第一次执行这个脚本时,因为 /tmp/ $log的最后一个时间是2个小时之前,相当于7200,肯定是大于3600的,所以会先发送一份邮件;但如果后面一直报警(要注意的是我们在 crontab 任务计划中是1分钟执行一次main.sh),$ v 会小于3600也就是1小时,则脚本会不发送邮件,而对$log.txt里的数值从0进行累加,直到大于10,再发送一封邮件,并重新将0重定向到$log.txt,等待下一轮10
     then    
         /dir/to/php  .. /mail/mail .php  "$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    
             /dir/to/php  .. /mail/mail .php  "trouble continue 10 min $1  $2 "  "$3"
              echo  "0"  /tmp/ $log.txt
         fi      
     fi

这是本人对告警系统的理解,若有不对的地方,望提出改正,谢谢!





      本文转自YU文武貝 51CTO博客,原文链接:http://blog.51cto.com/linuxerxy/1725290,如需转载请自行联系原作者






相关实践学习
日志服务之使用Nginx模式采集日志
本文介绍如何通过日志服务控制台创建Nginx模式的Logtail配置快速采集Nginx日志并进行多维度分析。
相关文章
|
2天前
|
Shell Android开发
Android系统 adb shell push/pull 禁止特定文件
Android系统 adb shell push/pull 禁止特定文件
32 1
|
2天前
|
安全 Shell Android开发
Android系统 init.rc开机执行shell脚本
Android系统 init.rc开机执行shell脚本
55 0
|
2天前
|
Unix Linux Shell
【Shell 编程指南 日期命令】Date命令:显示与设置系统时间和日期
【Shell 编程指南 日期命令】Date命令:显示与设置系统时间和日期
57 0
|
2天前
|
Shell C++ 开发者
【Shell 编程指南】Shell运算操作符之$[]详解
【Shell 编程指南】Shell运算操作符之$[]详解
24 0
|
2天前
|
监控 关系型数据库 Shell
Shell脚本入门:从基础到实践,轻松掌握Shell编程
Shell脚本入门:从基础到实践,轻松掌握Shell编程
|
2天前
|
存储 Shell C语言
shell脚本 编程 变量 基本入门(详解)
shell脚本 编程 变量 基本入门(详解)
|
2天前
|
存储 网络协议 Shell
第十章 Shell编程时常用的系统文件
第十章 Shell编程时常用的系统文件
|
2天前
|
Kubernetes Shell 网络安全
Shell脚本快速部署Kubernetes(K8S v1.1版本)集群系统
Shell脚本快速部署Kubernetes(K8S v1.1版本)集群系统
|
2天前
|
监控 Shell 开发工具
Shell编程
Shell编程
|
2天前
|
Shell
Shell脚本编程基础
Shell脚本编程基础