munin で死活チェック(死活監視)

munin で死活チェックをやりたいからプラグイン作った。

wget_pageってプラグインがあったんだけど、ちょっと追加したい機能があったので perlとmuninプラグインの練習もかねて apache_accesses を改造して作ってみた。
perl初心者」が作ったので、熟練者から見れば「なんてことでしょう」ってところがあると思う。
一応、、動くみたい。
まだまだバグとかいろいろありそう。
もちろん動作保障なし。

こいつにできること。
特定のURLにアクセスして生きてるか死んでいるか調べます。
サーバの応答時間(秒)をグラフに描画します。
Host: ヘッダに対応しているので名前ベースのバーチャルドメインも超えられます。きゅんきゅん。
応答結果に特定の文字列が含まれているかどうか調べることもできます。

プラグインを作成する上で参考にさせていただいたウェブサイト
http://blog.enjoitech.jp/article/85
http://www.sdlab.org/wiki/doku.php?id=fedora8-munin
http://munin.projects.linpro.no/wiki/HowToWritePlugins


↓ここにインスコしてください。
/usr/share/munin/plugins/web_accesses

#!/usr/bin/perl
#
# Parameters supported:
#
# 	config
# 	autoconf
#
# hide option
#
#   autoconfigfile
#         ---> /etc/munin/plugin-conf.d/munin-node auto make.

=head1 NAME

web_accesses is web health check plugin.
INSPIRE wget_page.
This source was made based on apache_accesses. 

This configuration section shows the defaults of the plugin:

  [web_accesses]
     env.web0_url http://127.0.0.1/
     env.web0_host myhost.local
     env.web0_grep <html>
     env.web0_proxy http://192.168.1.1:8080
     env.web0_proxy_auth userid:password

     env.web1_url https://127.0.0.1/
     env.web1_host secure.myhost.local
     env.web1_grep <html>

     env.web2_url https://127.0.0.1/
     env.web2_host super.myhost.local

		<snip>

     env.web99_url https://127.0.0.1:8080/
     env.web99_grep <html>


env.web0_host and env.web0_grep is option paramater.
The graph can hang by the response speed. 
The time-out is 60 seconds. 

The figure of 61 abnormalities is returned when becoming an error. 

61   timeout or server status error (404 403 ... more)
62   When contents specified for grep are not included.

health check ga dekiruyo!
yattane taechan!!

=head1 BUGS

Does not support digest authentication.

=head1 AUTHOR

rti
This source was made based on apache_accesses. 


=head1 LICENSE

GPLv2

=cut


my $ret = undef;

if (! eval "require LWP::UserAgent;")
{
	$ret = "LWP::UserAgent not found";
}
# for ssl
eval "require Crypt::SSLeay;";

if ( defined $ARGV[0] and $ARGV[0] eq "autoconf" )
{
	if ($ret)
	{
		print "no ($ret)\n";
		exit 1;
	}
	if ( length(getHttpdSOption()) <= 0 )
	{
		print "no (httpd)\n";
		exit 1;
	}
	print "yes\n";
	exit 0;
}


#hide option.
if ( defined $ARGV[0] and $ARGV[0] eq "autoconfigfile" )
{
	# httpd -S
	my $virtualhosts = getHttpdSOption();
	if ( length($virtualhosts) <= 0 )
	{
		print "no (httpd)\n";
		exit 1;
	}
	print "[web_accesses]\n";

	my $scanmode = 0;
	my $i = 0;
	foreach my $line (split("\n",$virtualhosts))
	{
		if ($scanmode == 0)
		{
			if (    $line =~ /^VirtualHost configuration/im
			     || $line =~ /^wildcard NameVirtualHosts/im)
			{
				$scanmode = 1;
			}
		}
		elsif ($scanmode == 1)
		{
			#VirtualHost configuration
			if (   $line =~ /^.*?:([0-9]+?) +?([0-9a-zA-Z\.\-_]+?) /im
				|| $line =~ /^ .*?port ([0-9\*]+?) +?namevhost ([0-9a-zA-Z\.\-_]+?) /im)
			{
				my $protocol = "http";
				my $port = $1;
				my $host = $2;
				if ($port == "*") 
				{
					$port = 80;
				}
				if ($port == 443)
				{
					$protocol = "https";
				}
				print "env.web${i}_url $protocol://127.0.0.1:$port/\n";
				print "env.web${i}_host $host\n";
				print "#env.web${i}_ua mozilla\n";
				print "#env.web${i}_grep <html>\n";
				print "#env.web${i}_proxy http://192.168.1.1:8080\n";
				print "#env.web${i}_proxy_auth userid:password\n";
				$i ++;
			}
		}
	}
	exit;
}






if ( defined $ARGV[0] and $ARGV[0] eq "config" )
{
	print "graph_title Web Access Time\n";
	print "graph_args --base 1000 --lower-limit -10 --upper-limit 70\n";
	print "graph_vlabel sec of web access\n";
	print "graph_category HealthCheck\n";
	print "graph_info web access time\n";


	for(my $i = 0; $i < 100 ; $i ++ )
	{
		if (!exists $ENV{"web${i}_url"})
		{
			next;
		}
		my $url = $ENV{"web${i}_url"};
		my $host = "web{$i}";
		if (exists $ENV{"web${i}_host"})
		{
			$host = $ENV{"web${i}_host"};
		}
		print "web${i}.label $host\n";
		print "web${i}.info $host($url)\n";
		print "web${i}.type gauge\n";
		print "web${i}.draw LINE2\n";
		print "web${i}.max 70\n";
		print "web${i}.min -10\n";
		print "web${i}.warning 50\n";
		print "web${i}.critical 60\n";

	}

	exit 0;
}


for(my $i = 0; $i < 100 ; $i ++ )
{
	if (!exists $ENV{"web${i}_url"})
	{
		next;
	}

	my $ua = LWP::UserAgent->new(timeout => 60);
	my $request = HTTP::Request->new('GET',$ENV{"web${i}_url"});

	if (exists $ENV{"web${i}_host"})
	{
		$ua->default_header("Host"=>$ENV{"web${i}_host"});
	}
	if (exists $ENV{"web${i}_ua"})
	{
		$ua->agent($ENV{"web${i}_ua"});
	}

	#proxy and proxy auth support.
	if (exists $ENV{"web${i}_proxy"})
	{
		$ua->proxy('http', $ENV{"web${i}_proxy"});

		if (exists $ENV{"web${i}_proxy_auth"})
		{
			my $auth_sep = index($ENV{"web${i}_proxy_auth"} , ":");
			if ($auth_sep >= 1)
			{
				$request->proxy_authorization_basic( 
					substr($ENV{"web${i}_proxy_auth"},0 , $auth_sep) ,
					substr($ENV{"web${i}_proxy_auth"},$auth_sep + 1) );
			}
		}
	}
	
	
	#redirect off.
	$ua->requests_redirectable([]);
	my $requesttime = time;
	my $response = $ua->request($request);
	$requesttime = time - $requesttime;
	
	if ( $response->code <= 199 || $response->code >= 400 )
	{
		#bad respons code
		$requesttime = 61;
	}
	else
	{
		if (exists $ENV{"web${i}_grep"})
		{
			my $grep = $ENV{"web${i}_grep"};
			if ( ! ($response->content =~ /$grep/im) ) 
			{
				#bad respons contents
				$requesttime = 62;
			}
		}
	}
	print "web${i}.value $requesttime\n";
}


#`httpd -S` result.
sub getHttpdSOption()
{
	my $virtualhosts = `httpd -S 2>&1`;
	if (length($virtualhosts) > 0)
	{
		return $virtualhosts;
	}
	my $virtualhosts = `/usr/sbin/httpd -S 2>&1`;
        if (length($virtualhosts) > 0)
	{
		return $virtualhosts;
	}
	
	#for debian
	$virtualhosts = `apache2 -S 2>&1`;
        if (length($virtualhosts) > 0)
	{
		return $virtualhosts;
	}
	#for source build
	$virtualhosts = `/usr/local/apache2/sbin/apache -S 2>&1`;
        if (length($virtualhosts) > 0)
	{
		return $virtualhosts;
	}
	#not found...
	return "";
}

# vim:syntax=perl

プラグインを有効にします。

ln -s /usr/share/munin/plugins/web_accesses /etc/munin/plugins/web_accesses

設定を書きます。

vi /etc/munin/plugin-conf.d/munin-node

設定ファイルの書式は、、、えっ、、そんな面倒なことやってられるかって。
ごもっともです。

↓こんな感じで実行すると、httpd -S の結果から設定ファイルに書くべき内容を自動生成してくれますwww

perl /usr/share/munin/plugins/web_accesses autoconfigfile

debian:/etc/munin/plugins# perl /usr/share/munin/plugins/web_accesses autoconfigfile
[web_accesses]
env.web0_url http://127.0.0.1:80/
env.web0_host debian.localdomain
#env.web0_ua mozilla
#env.web0_grep
env.web1_url http://127.0.0.1:81/
env.web1_host debian.localdomain
#env.web1_ua mozilla
#env.web1_grep
env.web2_url http://127.0.0.1:80/
env.web2_host mytest.local
#env.web2_ua mozilla
#env.web2_grep


設定ファイルを開いて、一番下に、先ほど自動生成した内容をコピペします。

vi /etc/munin/plugin-conf.d/munin-node

muninを再起動します。

/etc/init.d/munin-node restart

今すぐ値が見たいせっかちさんは↓を実行
グラフのパラメータの確認

perl /usr/share/munin/plugins/web_accesses config

取得した値の確認

perl /usr/share/munin/plugins/web_accesses

ここでなんかエラーが出たりすると、、、動いていないです。残念。
うまくそれっぽい値が表示されたら、多分大丈夫です。
あとは、5分ぐらい待って、グラフに HealthCheck ってカテゴリーができていれば完成です。


グラフを -10からスタートしているのは、たいていリクエストは 0秒で即答されてしまうので、つまんないからです。
底上げってやつですね。パッド!!パッド!!

リクエストには、60秒のタイムアウトが設定されてます。
でも、グラフは70秒まであります。
実は 61以上はフラグに使っています。

  • 61 サーバーが 404 Not Found等の失敗のコードを返した。
  • 62 サーバの応答にgrepで指定した文字列が含まれなかった。

一応、設定ファイルの説明を!!

[web_accesses]
env.web0_url http://127.0.0.1:80/ ←アクセスするURL(必須)
env.web0_host debian.localdomain ←host名 Host: ヘッダにも使う。つけたほうがいいけど必須にあらず
#env.web0_ua mozilla ←UserAgent偽造 UAではじいているサイトは googlebot とかに(ry
#env.web0_grep ←サーバの応答に含まれていてほしい文字列

下の二つのパラメタは作ったけどまだ動かしてませんwww
コメントアウトしといてください。
いや、だってももう1:30ですよ、眠いです。しんぢゃいます。明日直すよ。


9/9追記
やっぱりバグっていたんで直しましたw