App::Monitor::Simpleというモジュール書いた
https://github.com/toritori0318/p5-App-Monitor-Simple
シンプルに「リトライ数だけ指定して全部エラーなら何かする」
ということがしたくて探してみたけど見つからず。
コードボリュームからしてもわざわざモジュールにする程でもないかもしれませんが、
あったらあったでまあまあ嬉しいかも、と思い書いてみました。
仕様
やることは至極単純で
「コマンド実行した戻り値が0 or 0以外でOKかNGを判定するだけ」
です。
オプションとしてリトライ数とリトライ間隔を指定することが出来ます。
コマンドラインツール
monitor-simpleというのがついてきます。
詳しくは--help参照ですが、例を説明してみます。
応用編
monitor-simple 'curl http://hogehoge.co.jp/' && '正常時の処理' monitor-simple 'curl http://hogehoge.co.jp/' || 'エラー時の処理'
このようにするとcronなどで使えるのではないでしょうか。
またコマンドの部分をシェルやLLで書いてももちろんOKで、
むしろそういった応用を利かす使い方が多いかもしれません。
Perlモジュールとして使う
もちろんPerlからも使えます。
以下はSYNOPSISのコピペ。なんとなくわかるはず。
my $ret = run( { command => 'ping -c 1 blahhhhhhhhhhhhhhhh.jp', interval => 10, retry => 5, quiet => 1, } );
example
いくつか例を書いてみます。*1
複数サーバのステータスを独自のデータストアに保存したい。
例えばですが、
「HTTP監視をした結果、それが成功しているサーバのリストを保持しておきたい」
という要望があったとしましょう。
以下のようなコードをcronで回すとよいです。
use strict; use warnings; use Data::Dumper; use Parallel::ForkManager; use Redis; use App::Monitor::Simple qw/run/; my $redis = Redis->new(server => 'localhost:6379'); my @hosts = ('xxx.xxx.xxx.xxx', 'yyy.yyy.yyy.yyy'); my $workers = scalar @hosts; $workers = 10 if $workers > 10; my $pm = Parallel::ForkManager->new($workers); do { print "parallel: $workers process\n"; $pm->run_on_start( sub { my ($pid, $ident) = @_; printf "[%s] start\n", $ident; } ); $pm->run_on_finish( sub { my ($pid, $exit, $ident) = @_; printf "[%s] completes.\n", $ident; } ); }; # run foreach my $host (@hosts) { $pm->start($host) and next; my $cmd = sprintf('curl --max-time 3 http://%s/', $host); my $status = run( { command => $cmd, retry => 3, interval => 5, quiet => 1, } ); if($status == 0) { warn "success!"; $redis->set($host, 1); } else { warn "error!"; $redis->set($host, 0); } $pm->finish; } # wait... $pm->wait_all_children;
Redisをフェイルオーバーしたい
Redisでは自動フェイルオーバーの機能は提供されていない…ですよね?
AWS上にマスター/スレーブ構成のRedisがあるとします。
マスターにはElasticIPがついていて、
WebアプリケーションからはそのIPを参照しています。
そのような構成になっていると仮定し、
App::Monitor::Simpleで定期的に死活監視しつつ
マスターが落ちたらスレーブを昇格させて
ElasticIPを自動的に付け替える、ということを実現してみたいと思います。
(AWS デザインパターンのこれですね)
http://aws.clouddesignpattern.org/index.php/CDP:Floating_IP%E3%83%91%E3%82%BF%E3%83%BC%E3%83%B3
use strict; use warnings; use Clone qw(clone); use App::Monitor::Simple qw/run/; use VM::EC2; my $ec2 = VM::EC2->new(-access_key=>$id,-secret_key=>$key,-endpoint=>$url); # マスターサーバ情報 my $master = { host => 'xxx.xxx.xxx.xxx', instance_id => 'aaaaaaaaaaaaa', is_master => 1, }; # スレーブサーバ情報 my $slave = { host => 'yyy.yyy.yyy.yyy', instance_id => 'bbbbbbbbbbbbb', is_master => 0, }; my $elastic_id='zzz.zzz.zzz.zzz'; for my $server ($master, $slave) { my $host = $server->{host}; warn "[$host] start!"; # redisの死活監視 my $ret = run( { command => "redis-cli -h $host -p 6379 dbsize", interval => 5, retry => 5, quiet => 1, } ); if($ret == 0) { warn "[$host] ok!"; } else { warn "[$host] ng!"; # マスターが落ちている場合 if($server->{is_master}) { # スレーブをマスターに昇格 my $change_host = $slave->{host}; system("redis-cli -h $change_host -p 6379 slaveof no one"); # Elastic IP 付け替え my @addr = $ec2->describe_addresses($elastic_ip); my $result = $addr[0]->associate_address($elastic_addr => $slave->{instance_id}); # 情報swap my $tmp = clone($master); $master = clone($slave); $slave = clone($tmp); $master->{is_master} = 1; $slave->{is_master} = 0; } # 自動でスレーブ作り直し? # ... # もしくは 通知して exit? return; } }
Todo
- タイムアウト処理もこのモジュールで出来たほうがいいのかなー
- Warningも別判定にしたほうがいいのかなー
最後に
もちろんNagiosなどツールを使えば同じことが出来ますが、
シンプルな監視をしたいときには有用かと思うのですがいかがでしょうか。
*1:全てフィクションです!!!!