Webサイトの正常動作を定期的に監視するPHPスクリプト

今回は「自動化してWeb運営を楽にしよう計画」の一貫として、自分が運営するWebサイトが落ちていないかどうか監視する作業について自動化を考えてみたいと思います。

24時間稼働させているWindows PCが手元にあり、そのタスクスケジューラーにPHPスクリプトを登録して定期実行させることを想定しています。もちろんCentOSとかのLinuxでも構わないんですけど、うちの環境がWindowsなので一応。

監視はしたい。でも負荷はかけたくない。

Webサイトが落ちていないか監視すると言っても、その頻度はどうするか、どこまでのエラーを検出するか、と考えはじめるとけっこう悩みます。

例えば、Webサイトが落ちていることを一刻も早く知りたいのなら、監視用のスクリプトを5分に1回、あるいは1分に1回のタイミングで動作させることになりますが、それで自分が運営するWebサイトに負荷をかけていたら本末転倒ですよね。

何が言いたいのかはスクリプトを見たほうがわかりやすいと思いますので、PHPサンプルを挙げます。

■Webサイトのヘッダ情報を取得する例

<?php
//監視結果を送信するメールアドレス
define('MAIL_TO', 'webmaster@example.com');

$url = 'http://example.com/';
$headers = @get_headers($url, 1);
if (!$headers) {
	mail(MAIL_TO, 'Webサイトが落ちているかも', 'ヘッダーが取れなかったよ', 'From:'.MAIL_TO);
}
?>

こんだけかよっ!って感じですが、意外とバカに出来ません。

get_headers関数でヘッダ情報を取得しているだけなのでデータ転送量もサーバー負荷も微々たるものです。

ヘッダ情報の取得に失敗する場合はWebサイトは落ちていると判断して良いでしょうし、なによりそんなことが意外と(特に格安レンタルサーバーでは)ちょいちょい発生しています。

ぼくの経験上、月に1度くらいはエラーメールが飛んでくる感じですね。だいたいは確認する頃には復旧しているので、瞬間的な高負荷に一瞬だけ耐えられなかったのかなぁーと思っていますけども。

ともあれ、あーぼくが寝ているときでも監視してくれているんだなぁーなんて安心感には繋がります。

ただ、ヘッダーが読めるか読めないかだけだと内部サーバーエラーとか、DB接続エラーとかそういうのが検出できないので、もうちょっとだけマジメにやるならこんな感じでしょうか。

■Webサイトのヘッダ情報をもうちょっとだけ取得する例

<?php
//監視結果を送信するメールアドレス
define('MAIL_TO', 'webmaster@example.com');

$url = 'http://example.com/';
$headers = @get_headers($url, 1);
if (!$headers) {
	mail(MAIL_TO, 'Webサイトが落ちているかも', 'ヘッダーが取れなかったよ', 'From:'.MAIL_TO);
} else if ($headers[0] != 'HTTP/1.1 200 OK' && $headers [0] != 'HTTP/1.0 200 OK') {
	mail(MAIL_TO, 'Webサイトが落ちているかも', $headers[0].'ですってよー', 'From:'.MAIL_TO);
}
?>

またしてもこんだけかよっ!ってレベルですが、ステータスが200かどうかチェックするだけでもエラー検出の精度がけっこう上がります。長年運営しているとレンタルサーバー側のミスでステータス500エラーとか普通にありますしね。

多少の負荷は気にしないからちゃんと監視したい

1日のアクセスが数万PVを超えはじめると、ヘッダーだけの監視では物足りないというか、1時間に1回トップページ全体を取得しても1日に24回分のアクセスだし、30分に1回でも48PV……大した負荷じゃないよなぁーとか考え始めます。

自作のスクリプトのみで構成されたWebサイトなら、DBエラーやPHPエラー発生時はバックグラウンドでエラーメールを送信するなり何なり処理をしていると思いますが、ほかから持ってきたスクリプトだとそういう作りになっていない場合もあり、単に空白のページを出力してしまうケースとかもあるわけです。

■なので、トップページが空白かどうか、なんてチェックをはじめる。

<?php
//監視結果を送信するメールアドレス
define('MAIL_TO', 'webmaster@example.com');

$url = 'http://example.com/';
$headers = @get_headers($url, 1);
if (!$headers) {
	mail(MAIL_TO, 'Webサイトが落ちているかも', 'ヘッダーが取れなかったよ', 'From:'.MAIL_TO);
} else if ($headers[0] != 'HTTP/1.1 200 OK' && $headers [0] != 'HTTP/1.0 200 OK') {
	mail(MAIL_TO, 'Webサイトが落ちているかも', $headers[0].'ですってよー', 'From:'.MAIL_TO);
} else if (@file_get_contents($url) == '') {
	mail(MAIL_TO, 'Webサイトが落ちているかも', 'ヘッダーは正常だけどコンテンツがおかしいよー', 'From:'.MAIL_TO);
}
?>

そして、どうせfile_get_contentsでまるごとトップページを読み込むなら、もう少し有効活用したくなるわけです。

■例えば、トップページを取得する時間を計測して記録するとか。

$t1 = microtime (true);
$html = @file_get_contents ($url);
$t2 = microtime (true);

$row['top_page_response'] = floor(($t2 - $t1) * 1000);

■あるいはトップページのtitleタグを取得して記録するとか。

$html = @file_get_contents ($url);
$row['title'] = getTitle($html);

function getTitle($html) {
	if (preg_match ( "/<title>(.*)<\/title>/i", $html, $match )) {
		return $match [1];
	}
	return "";
}

実際、うちの監視サーバーではこれらのデータをMySQLデータベースに格納して、後からチェックできるようにしています。もちろんチェックするプログラムも自動で、

  • titleタグの内容が前回取得時と違っていた場合に警告メールを送信
  • トップページの表示時間が前回と比べて50%以上遅くなっていたら警告メールを送信
  • 時間帯別の表示時間グラフをLAN内のWebページで表示

みたいなことをしています。

DBのテーブル構造とかも含むとえらい長くなるので、サンプルは割愛しますが、ほんとに記録と集計だけなので、PHPでDB操作が出来る方には問題ないでしょう。

まとめ

最後はやや複雑だったかも知れませんが、一番重要なWebサイトが落ちているかどうかの確認だけなら、PHPスクリプト数行で実現できることがわかって頂けるかと思います。

レンタルサーバーのお高いプランならそういう監視オプションもあるし、外部のWebサービスもあるようですが、PHP組めるなら自分でやったほうが後々自由に機能追加できるから面白いんじゃないかなぁーなんて思ってしまいます。

Web運営をはじめる→Webサイトが増える→LAN内にメンテナンス用サーバーが欲しくなる→ついでに監視も任せる、そんな感じで気がつけばビジネスサーバー並のメンテナンス体制になっている、なんて個人も珍しくないんじゃないかなァ。