数日前、CentOSサーバーをyum updateした辺りからWebサーバー(Apache)が日に数回に無反応になる現象がでました。プロセスは存在しているもの、ブラウザからアクセスしてもページが表示されず。telnet localhost 80はできるけど、リクエストに応答が返ってこない感じ。systemctl restart httpdすれば治るがまた数時間経つと再発する、という感じ。ログをみてもこれといった情報はつかめず。
そもそもこのサーバーはConoHaのKUSANAGIイメージから構築していて、Apacheの裏にphp7-fpmがいたりしてちょっと構成や設定ファイルの関係が理解しきれていないところがあり、数日粘ったもののお手上げ。
とりあえずsite24x7.comの無料プランで10分おきの死活チェックをしていてサイトが死んでいたらメールやアプリ通知でわかるようにしておき、気づきしだいsshしてhttpd再起動ということをしてましたが、さすがに面倒だし寝ている間に発生すると数時間落ちたままになり、同居している他サイトにも迷惑になるので、とりあえず自動再起動の仕組みを用意することにしました。
■Monitが最初から入ってた
ググったところMonitというオープンソースの死活監視ツールが手軽そうだと思い、インストールしようとしたらKUSANAGIイメージには最初から入っているようでした。/etc/monit.d/下にはKUSANAGIで作成したWordPressの数だけ設定ファイルがありました。しかし現時点で役に立っていないので一旦全部削除(正確には退避)。新規で設定しなおしてみました。
/etc/monitrc
メインの共通設定ファイル。これはとくにいじっていません。監視周期(デフォルト30秒)やログをどこに吐くかなどが指定できます。
/etc/monit.d/alert
メールの設定。/etc/monitrc上で/etc/monit.d/下のファイルをすべて読み込む指定になっているので、別にファイル名はこれでなくても構わないと思います。KUSANAGIイメージではこうなっていましたというだけ。
今回のホストではメールサーバーは運用していないのでgmailを使います。一番上が送信したいアドレス(gmailアドレスでなくてOK)。usernameとpasswordで自分のgmailアカウントを使って認証します。二段階認証を設定している場合、パスワードはアプリパスワードを作成して使います。というかこんなところにGoogleアカウントの本パスワードを書くのも不用心なので必ずそうしましょう。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
set alert xxxxxxxxxxx@gmail.com set mailserver smtp.gmail.com port 587 username "xxxxxxxx@gmail.com" password "xxxxxxxxxxxx" using TLSV12 with timeout 30 seconds set mail-format { from: xxxxx@xxxxxxxxxx.com subject: [MONIT] $SERVICE $EVENT at $DATE message: Monit alert this action. Please check monit status on $HOST. Service: $SERVICE Event: $EVENT Action: $ACTION Data: $DATE Host: $HOST $DESCRIPTION. } |
fromのところはメールの差出人名。これはgmailの場合gmailアドレスに上書きされてしまうようです(ここで指定するアドレスを本人アドレスとしてgmail側に登録すればいけるかも?)
subjectが題名、message以下が本文で$がついたキーワードにその時の状況に応じた情報が入ります。2バイト文字を入れるとちゃんとUTF-8とかで文字化けせずに届くかは試してません。
/etc/monit.d/logging
これもKUSANAGIイメージに最初から入ってました。
1 2 |
# log to monit.log set logfile /var/log/monit.log |
とだけ書かれており、ログの保存先を指定しているだけ。
/etc/monit.d/httpd.conf
こちらをゼロから作りました。
1 2 3 4 5 |
check process httpd with pidfile "/var/run/httpd.pid" start program = "/usr/bin/systemctl start httpd" stop program = "/usr/bin/systemctl stop httpd" if failed port 80 protocol http request "/index.php" then restart if 10 restarts within 10 cycles then unmonitor |
start programとstop programは文字通り、起動、終了に必要なコマンドです。
ポイントは
1 2 |
if failed port 80 protocol http request "/index.php" then restart |
です。今回のウチの状況だとhttpdプロセスはいきていてtelnetまではできます。なので単にポートを監視するだけでは生きているように見えてしまうので、実際にURLを指定して応答があるところまでチェックしています。ホストは自身なので省略可能。ポートもたぶんなくてもいいかも。指定するページはどこでもいいんでしょうが、PHPスクリプトがあまり含まれていない軽いページが良いでしょう。Apache側でアクセスログが30秒に1回発生してしまうので、通常のコンテンツとしては閲覧されない隠しURLみたいなところだと除外指定がしやすいかも知れません。まぁ自分の場合は次節のようにUser-Agentで除外したので、とりあえずトップページ(/index.html)にしてあります。
その下は10回試してダメなら諦める(unmonitor)ということのようです。なにかもっと致命的な原因で落ちている時に無闇に無限試行しないためだと思われます。現状不都合がないので初期状態で。
monitコマンドの操作
CentOS7の場合、httpd同様、systemctlコマンドで起動や終了ができます。またmonit -tで設定ファイルの文法チェックができ、monit reloadで設定ファイルの再読み込みができます。
メール例
実際に再起動が行われた際にこんなメールが来ていました。
1通目で検知して復旧を知らせるまでに30秒ちょっとかかってますね。これは単に30秒間隔設定だからかな?実際には1通目の直後に復帰はしているのかも知れません。
Apacheのログからmonitを除外する
さて、上にも書きましたがリクエストを投げて死活監視をするとApacheのログがとんでもないことになってしまいます。
1 2 3 4 5 6 7 8 |
1551 - ::1 - - [01/Oct/2021:18:00:26 +0900] "GET /index.php HTTP/1.1" 200 7046 "-" "Monit/5.26.0" - 964 - ::1 - - [01/Oct/2021:18:00:56 +0900] "GET /index.php HTTP/1.1" 200 7046 "-" "Monit/5.26.0" - 45978 - ::1 - - [01/Oct/2021:18:01:26 +0900] "GET /index.php HTTP/1.1" 200 7046 "-" "Monit/5.26.0" - 956 - ::1 - - [01/Oct/2021:18:01:56 +0900] "GET /index.php HTTP/1.1" 200 7046 "-" "Monit/5.26.0" - 3797 - ::1 - - [01/Oct/2021:18:02:26 +0900] "GET /index.php HTTP/1.1" 200 7046 "-" "Monit/5.26.0" - 26580 - ::1 - - [01/Oct/2021:18:02:56 +0900] "GET /index.php HTTP/1.1" 200 7046 "-" "Monit/5.26.0" - 1069 - ::1 - - [01/Oct/2021:18:03:26 +0900] "GET /index.php HTTP/1.1" 200 7046 "-" "Monit/5.26.0" - |
なので、/etc/httpd/httpd.confのログ設定に除外指定を追加します。log_config_moduleのIfModuleディレクティブの中の、SetEnvIFで画像ファイルを除外している辺りに追加しました。User-Agent(ブラウザ名)もSetEnvIFで指定できますが、それ専用にBrowserMatchというのがあったので使ってみました。実際のUAは「Monit/5.26.0」ですが、将来的にバージョンがかわる可能性もあるので正規表現で「Monit」だけマッチさせるのが望ましいですが、BrowserMatchは最初から部分一致で判定してくれるので第一引数に「Monit」と書くだけで済みます。
1 2 3 4 5 6 7 8 |
<IfModule log_config_module> (中略) #CustomLog "logs/access_log" combined SetEnvIF Request_URI "\.(jpg|jpeg|gif|png|css|js|swf|ico|pdf|svg|eot|ttf|woff|woff2|map)$" no_log BrowserMatch Monit monit no_log CustomLog /var/log/httpd/access.log kusanagi env=!no_log </IfModule> |
識別子にmonitとno_logをつけておきます。no_logがついていればメインのaccess.logからは除外されます。monitは専用ファイルでログを残す時などに使いますが現状はつけただけです。
■まとめ
Apacheが無反応になる根本原因を掴めていないまま泥縄式に対策をしてお恥ずかしい限りですが、とりあえず最低限Webサーバーの稼働は継続できるようになったようです。
送られてくるログをみると、もともとsite24x7(10分毎チェック)が知らせてくるのより頻繁に再起動が行われていることがわかります。つまり放置すれば自動復帰しているような小規模なロックがもっとたくさん起きてたっことでしょうか。やはりきちんと原因究明が必要なようですね…