muninでヘルスチェック(死活監視/生存監視/サイト監視とか)
munin派とnagios派とZABBIX派が戦うときにたいてい焦点になるのが死活監視などの監視だと思う。
「muninってグラフを書くしかできないよね!」 とかいわれたり、いやいや alert のメール送信できますよみたいな。
しかし、「muninってURLのチェックとか、プロセスの生存チェックとか、logのチェックとかできるの?」っていわれると munin派はぐぬぬぬ・・・といわなきゃいけなかった。
昔、 http監視する奴を作ったりはしたけど、ちょっと本格的に作ってみることにした。
ダウンロードする場合
プロセス監視
http://rtilabs.net/files/2011_06_05/healthcheck_process
URL監視(サイト監視/コンテンツ監視)
http://rtilabs.net/files/2011_06_05/healthcheck_url
ログ監視
http://rtilabs.net/files/2011_06_05/healthcheck_log
インストール方法など
#プラグインディレクトリまで移動 cd /usr/share/munin/plugins #過去のゴミを消す /bin/rm healthcheck_* #ダウンロード wget 'http://rtilabs.net/files/2011_06_05/healthcheck_process' wget 'http://rtilabs.net/files/2011_06_05/healthcheck_url' wget 'http://rtilabs.net/files/2011_06_05/healthcheck_log' wget 'http://rtilabs.net/files/2011_06_05/healthcheck_ping' #実行権限付与 chmod +x healthcheck_* #muninへ登録 ln -s /usr/share/munin/plugins/healthcheck_process /etc/munin/plugins/ ln -s /usr/share/munin/plugins/healthcheck_log /etc/munin/plugins/ ln -s /usr/share/munin/plugins/healthcheck_url /etc/munin/plugins/ ln -s /usr/share/munin/plugins/healthcheck_ping /etc/munin/plugins/ #設定を書く. vim /etc/munin/plugin-conf.d/munin-node 例 ------------------------------------------------------ [healthcheck_process] env.process_1 httpd env.process_2 mysqld [healthcheck_url] env.url_1 http://www.google.com/ env.slowspeed_1 5 #limit time(sec) #env.htmlgrep_1 google #check egrep string env.url_2 http://www.yahoo.com/ env.slowspeed_2 5 #limit time(sec) #env.htmlgrep_2 yahoo #check egrep string #env.htmlsize_2 10000 #check html size(byte). #env.proxy_2 127.0.0.1:8080 #over proxy [healthcheck_log] user root env.log_1 /var/log/messages [healthcheck_ping] env.ping_1 192.168.1.1 ------------------------------------------------------ #muninノード再起動 /etc/init.d/munin-node restart
プロセス監視
healthcheck_process ファイル。
ふつーに動かすと、設定したプロセス名で ps -C httpd とかやって、そのプロセスが利用しているメモリ容量の合算をMB単位で表示する。
ただし、プロセスが落ちている場合は、-10 という値になり、アラートがあがる仕組みになっている。
複数のプロセスをまとめて死活チェックできる。
これでmunin ってプロセス監視できないよね!! というのは過去のものになるはずだ。
#!/bin/bash # #healthcheck on munin #check process and alert. # #programed by rti (hiroyuki fujie) super.rti@gmail.com @super_rti #LICENSE: NYSL (public domain) # #config file # /etc/munin/plugin-conf.d/munin-node # #example minimum config #--------------------------------------------------- #[healthcheck_process] #env.process_1 httpd #--------------------------------------------------- # #chcek two process #--------------------------------------------------- #[healthcheck_process] #env.process_1 httpd #env.process_2 samba #--------------------------------------------------- # #chcek three process #--------------------------------------------------- #[healthcheck_process] #env.process_1 httpd #env.process_2 samba #env.process_3 mysqld #--------------------------------------------------- # # # #edakari speed up. CHECKMAX=`env | grep process_ | wc -l` let CHECKMAX="$CHECKMAX + 1" if [ "$1" = "autoconf" ]; then if [ $CHECKMAX -le 1 ]; then echo no exit 1 fi echo yes exit 0 fi if [ "$1" = "config" ]; then echo 'graph_title process memory Usage(MB)' echo "graph_args --base 1000 -l 0 --vertical-label MB" echo 'graph_scale no' echo 'graph_vlabel process memory' echo 'graph_category healthcheck' echo 'graph_info This graph shows the Memory used by process' for(( I = 1; I < $CHECKMAX; ++I )) do eval process=\$process_${I} eval alertmemory=\$alertmemory_${I} if [ "x${process}" = "x" ]; then continue fi echo "$process.label $process" echo "$process.info Memory used by $process" echo "$process.draw LINE2" echo "$process.min -10" echo "$process.critical 0:" done exit 0 fi for(( I = 1; I < $CHECKMAX; ++I )) do eval process=\$process_${I} if [ "x${process}" = "x" ]; then continue fi vrets=(`ps u --no-headers -C $process | awk 'BEGIN { count = 0 ; sum = 0; } { count ++ ; sum += $6/1024 ; } END { printf("%d %d\n",count,sum); }'`) count=${vrets[0]} value=${vrets[1]} if [ $count -le 0 ]; then echo "$process.value -10" echo "$process.extinfo process down" else echo "$process.value $value" fi done
URL監視
healthcheck_url ファイル。
指定したURL に curl でアクセスして処理時間(秒)を記録する。
あまりに遅いと遅すぎということでアラートがあがる。
で、もし、アクセス出来ない場合は -10 という値になりアラートが上がる。
ついでにコンテンツの中身をegrepして、特定の文字列が入っていないと、アラートを上げることもできる。(バグっていたので直した。ごめんなさい)
さらに、応答のhtmlのサイズをチェックするオプションも付けました。エラー画面のhtmlが帰ってきてしまって気がつかないみたいなことがないようにしました。応答のhtmlが特定のサイズ以下だとアラートを上げます。
また、proxyも対応しているので、インターネット経由(外回り)でのサイト監視もできるヨ。
もちろん、複数サイトを一つのグラフでまとめて管理することも可能。
整合性チェックのphpとかと組み合わせれば、結構面白いことができそうな気がする。
これで munin ってサイト監視できないよね! っていうのは過去のものになるはずだ。
#!/bin/bash # #healthcheck on munin #check site speed. #egrep contents string # ... and alert. # #programed by rti (hiroyuki fujie) super.rti@gmail.com @super_rti #LICENSE: NYSL (public domain) # #config file # /etc/munin/plugin-conf.d/munin-node # #example minimum config #--------------------------------------------------- #[healthcheck_url] #env.url_1 http://127.0.0.1/ #--------------------------------------------------- # #chcek two site #--------------------------------------------------- #[healthcheck_url] #env.url_1 http://127.0.0.1/ #env.url_2 http://www.google.com/ #--------------------------------------------------- # #chcek three site #--------------------------------------------------- #[healthcheck_url] #env.url_1 http://127.0.0.1/ #env.url_2 http://www.google.com/ #env.url_3 http://www.yahoo.com/ #--------------------------------------------------- # #set name #--------------------------------------------------- #[healthcheck_url] #env.url_1 http://127.0.0.1/ #env.name_1 homhom #--------------------------------------------------- # #check over proxy #--------------------------------------------------- #[healthcheck_url] #env.url_1 http://127.0.0.1/ #env.proxy_1 127.0.0.1:8080 #--------------------------------------------------- # #set slow speed(second) #--------------------------------------------------- #[healthcheck_url] #env.url_1 http://127.0.0.1/ #env.slowspeed_1 30 #--------------------------------------------------- # #grep string #--------------------------------------------------- #[healthcheck_url] #env.url_1 http://127.0.0.1/ #env.htmlgrep_1 saysaya #--------------------------------------------------- # #check html contents byte size. #--------------------------------------------------- #[healthcheck_url] #env.url_1 http://127.0.0.1/ #env.htmlsize_1 50000 #check 50000 bytes over. #--------------------------------------------------- # #full option #/etc/munin/plugin-conf.d/munin-node #--------------------------------------------------- #[healthcheck_url] #env.url_1 http://127.0.0.1/ #check url #env.htmlgrep_1 apache #check egrep string #env.name_1 127.0.0.1 #set line name. default by url domain #env.proxy_1 127.0.0.1:8080 #over proxy #env.slowspeed_1 30 #slow time on alert #env.htmlsize_1 50000 #check 50000 bytes over. #--------------------------------------------------- # # #edakari speed up. CHECKMAX=`env | grep url_ | wc -l` let CHECKMAX="$CHECKMAX + 1" CURL=/usr/bin/curl if [ "$1" = "autoconf" ]; then if [ $CHECKMAX -le 1 ]; then echo no exit 1 fi echo yes exit 0 fi if [ "$1" = "config" ]; then echo 'graph_title site speed (second)' echo "graph_args --base 1000 -l 0 --vertical-label second" echo 'graph_scale no' echo 'graph_vlabel second' echo 'graph_category healthcheck' echo 'graph_info This graph shows the site speed' for(( I = 1; I < $CHECKMAX; ++I )) do eval url=\$url_${I} eval name=\$name_${I} eval slowspeed=\$slowspeed_${I} if [ "x${url}" = "x" ]; then continue fi if [ "x${name}" = "x" ]; then #default name by domain name=`echo $url | sed 's#\.#_#g' | sed -e 's/[^/]*\/\/\([^@]*@\)\?\([^:/]*\).*/\2/'` fi if [ "x${slowspeed}" = "x" ]; then slowspeed=10 fi let slowspeed15="slowspeed * 3 / 2" #slowspeed * 1.5 echo "$name.label $name" echo "$name.info $url" echo "$name.draw LINE2" echo "$name.min -10" echo "$name.max ${slowspeed15}" echo "$name.critical 0:${slowspeed}" done exit 0 fi for(( I = 1; I < $CHECKMAX; ++I )) do eval url=\$url_${I} eval grep=\$htmlgrep_${I} eval size=\$htmlsize_${I} eval name=\$name_${I} eval proxy=\$proxy_${I} eval slowspeed=\$slowspeed_${I} if [ "x${url}" = "x" ]; then continue fi if [ "x${name}" = "x" ]; then #default name by domain name=`echo $url | sed 's#\.#_#g' | sed -e 's/[^/]*\/\/\([^@]*@\)\?\([^:/]*\).*/\2/'` fi PROXY_CMD="" if [ "x${proxy}" != "x" ]; then PROXY_CMD=" --proxy ${proxy} " fi if [ "x${slowspeed}" = "x" ]; then slowspeed=10 fi let timeout="${slowspeed} + 1" START=`date +%s` HTML_RESULT=`$CURL "${url}" -s --connect-timeout ${timeout} ${PROXY_CMD}` CURLEXITCODE=$? END=`date +%s` if [ $CURLEXITCODE -ne 0 ]; then echo "$name.value -10" echo "$name.extinfo curl return $CURLEXITCODE" continue fi GREPEXITCODE=0 if [ "x${grep}" != "x" ]; then echo $HTML_RESULT | egrep -i "${grep}" > /dev/null GREPEXITCODE=$? if [ $GREPEXITCODE -ne 0 ]; then echo "$name.value -9" echo "$name.extinfo can not found $grep regex strings" continue fi fi if [ "x${size}" != "x" ]; then if [ ${#HTML_RESULT} -lt ${size} ]; then echo "$name.value -8" echo "$name.extinfo html size ${#HTML_RESULT} is smaller than ${size}" continue fi fi let SPEED="$END - $START" echo "$name.value $SPEED" done
ログ監視
healthcheck_log ファイル。
ログを egrep して特定の文字列が含まれていないかチェックすることができる。
これで深刻なエラーがログには出ていたけど、チェック忘れれて気が付きませんでしたということもなくなるヨ。
#!/bin/bash # #healthcheck on munin #egrep system log and alert. # #programed by rti (hiroyuki fujie) super.rti@gmail.com @super_rti #LICENSE: NYSL (public domain) # # #config file # /etc/munin/plugin-conf.d/munin-node # #example minimum config #--------------------------------------------------- #[healthcheck_log] #user root #env.log_1 /var/log/messages #--------------------------------------------------- # #check two log #--------------------------------------------------- #[healthcheck_log] #user root #env.log_1 /var/log/messages #env.log_2 /var/log/syslog #--------------------------------------------------- # #check two three #--------------------------------------------------- #[healthcheck_log] #user root #env.log_1 /var/log/messages #env.log_2 /var/log/syslog #env.log_3 /var/log/dmesg #--------------------------------------------------- # #set name #--------------------------------------------------- #[healthcheck_log] #user root #env.log_1 /var/log/messages #env.name_1 my_server_messages #--------------------------------------------------- # #set egrep string #--------------------------------------------------- #[healthcheck_log] #user root #env.log_1 /var/log/messages #env.grep_1 alert|warning #--------------------------------------------------- # #set egrep string #--------------------------------------------------- #[healthcheck_log] #user root #env.log_1 /var/log/messages #env.grep_1 alert|warning #--------------------------------------------------- # #full option #/etc/munin/plugin-conf.d/munin-node #--------------------------------------------------- #[healthcheck_log] #user root #log file is read only root user. #env.log_1 /var/log/messages #target log filename #env.grep_1 critical|error #egrep string. #defualt by critical|error|warning|crash|fatal|kernel #--------------------------------------------------- # #edakari speed up. CHECKMAX=`env | grep log_ | wc -l` let CHECKMAX="$CHECKMAX + 1" MINUTE_BY_GREP_RANGE=10 if [ "$1" = "autoconf" ]; then if [ $CHECKMAX -le 1 ]; then echo no exit 1 fi echo yes exit 0 fi if [ "$1" = "config" ]; then echo 'graph_title log grep (match count)' echo "graph_args --base 1000 -l 0 --vertical-label match_count" echo 'graph_scale no' echo 'graph_vlabel match_count' echo 'graph_category healthcheck' echo 'graph_info This graph shows the bad event count on log' for(( I = 1; I < $CHECKMAX; ++I )) do eval log=\$log_${I} eval name=\$name_${I} eval grep=\$grep_${I} if [ "x${log}" = "x" ]; then continue fi if [ "x${name}" = "x" ]; then name=`echo $log | sed 's#[/|\.]#_#g'` fi if [ "x${name}" = "x" ]; then grep="critical|error|warning|crash|fatal|kernel" fi echo "$name.label $name" echo "$name.info egrep $grep $log | wc -l" echo "$name.draw LINE2" echo "$name.min 0" echo "$name.max 20" echo "$name.critical 0:0" done exit 0 fi NOWTIME=`date --date "$MINUTE_BY_GREP_RANGE minute ago" +%s` for(( I = 1; I < $CHECKMAX; ++I )) do eval log=\$log_${I} eval name=\$name_${I} eval grep=\$grep_${I} if [ "x${log}" = "x" ]; then continue fi if [ "x${name}" = "x" ]; then name=`echo $log | sed 's#[/|\.]#_#g'` fi if [ "x${grep}" = "x" ]; then grep="critical|error|warning|crash|fatal|kernel" fi COUNT=0 MESSAGE= IFS=$'\n' MATCHLINES=(`egrep -i "$grep" "$log"`) for(( N = ${#MATCHLINES[@]} - 1; N >= 0 ; --N )) do LINE=${MATCHLINES[$N]} DATESTRING=`echo $LINE | awk '{ printf("%s %s %s",$1,$2,$3)}'` LOGTIME=`date --date "$DATESTRING" +%s` if [ $LOGTIME -lt $NOWTIME ]; then break fi let COUNT="$COUNT + 1" MESSAGE="$MESSAGE$LINE //@LINE@// " done if [ $COUNT -eq 0 ]; then echo "${name}.value 0" else echo "${name}.value ${COUNT}" echo "${name}.extinfo ${MESSAGE}" fi done
ping監視(追加)
ついでに ping 監視も作ってみた。
healthcheck_ping ファイル。
ping をなげて応答速度を見ます。
#!/bin/bash # #healthcheck on munin #check ping speed. #egrep contents string # ... and alert. # #programed by rti (hiroyuki fujie) super.rti@gmail.com @super_rti #LICENSE: NYSL (public domain) # #config file # /etc/munin/plugin-conf.d/munin-node # #example minimum config #--------------------------------------------------- #[healthcheck_ping] #env.ping_1 127.0.0.1 #--------------------------------------------------- # #chcek two site #--------------------------------------------------- #[healthcheck_ping] #env.ping_1 127.0.0.1 #env.ping_2 www.google.com #--------------------------------------------------- # #chcek three site #--------------------------------------------------- #[healthcheck_ping] #env.ping_1 127.0.0.1 #env.ping_2 www.google.com #env.ping_3 192.168.1.1 #--------------------------------------------------- # #set name #--------------------------------------------------- #[healthcheck_ping] #env.ping_1 127.0.0.1 #env.name_1 homhom #--------------------------------------------------- ## #set slow speed(second) #--------------------------------------------------- #[healthcheck_ping] #env.ping_1 127.0.0.1 #env.slowspeed_1 3 #--------------------------------------------------- # #full option #/etc/munin/plugin-conf.d/munin-node #--------------------------------------------------- #[healthcheck_ping] #env.ping_1 127.0.0.1 #check ping #env.name_1 sayasaya #set line name. default by ping domain #env.slowspeed_1 3 #slow time on alert #--------------------------------------------------- # # #edakari speed up. CHECKMAX=`env | grep ping_ | wc -l` let CHECKMAX="$CHECKMAX + 1" if [ "$1" = "autoconf" ]; then if [ $CHECKMAX -le 1 ]; then echo no exit 1 fi echo yes exit 0 fi if [ "$1" = "config" ]; then echo 'graph_title ping speed (mili second)' echo "graph_args --base 1000 -l 0 --vertical-label milisecond" echo 'graph_scale no' echo 'graph_vlabel milisecond' echo 'graph_category healthcheck' echo 'graph_info This graph shows the site speed' for(( I = 1; I < $CHECKMAX; ++I )) do eval ping=\$ping_${I} eval name=\$name_${I} eval slowspeed=\$slowspeed_${I} if [ "x${ping}" = "x" ]; then continue fi if [ "x${name}" = "x" ]; then #default name by domain name=`echo $ping | sed 's#\.#_#g'` fi if [ "x${slowspeed}" = "x" ]; then slowspeed=3 fi let slowspeedMS="slowspeed * 1000" #convert milisecond. let slowspeedMS15="slowspeed * 3 / 2 * 1000" #slowspeedMS * 1.5 echo "$name.label $name" echo "$name.info $ping" echo "$name.draw LINE2" echo "$name.min -10" echo "$name.max ${slowspeedMS15}" echo "$name.critical 0:${slowspeedMS}" done exit 0 fi for(( I = 1; I < $CHECKMAX; ++I )) do eval ping=\$ping_${I} eval name=\$name_${I} eval slowspeed=\$slowspeed_${I} if [ "x${ping}" = "x" ]; then continue fi if [ "x${name}" = "x" ]; then #default name by domain name=`echo $ping | sed 's#\.#_#g'` fi if [ "x${slowspeed}" = "x" ]; then slowspeed=3 fi let timeout="${slowspeed} + 1" PING_RESULT=`ping -c 1 -n -w ${timeout} "${ping}" | grep "icmp_seq=1"` PINGRESULT=$? if [ $PINGRESULT -ne 0 ]; then if [ "x$PING_RESULT" = "x" ]; then PING_RESULT="ping can not respons. timeout $slowspeed second" fi echo "$name.value -10" echo "$name.extinfo $PING_RESULT" continue fi TIMEMS=`echo $PING_RESULT | sed 's/.*time=\([^ ]*\).*/\1/g'` if [ "x$TIMEMS" = "x" ]; then echo "$name.value -8" echo "$name.extinfo bad time: $PING_RESULT" continue fi echo "$name.value $TIMEMS" done