読者です 読者をやめる 読者になる 読者になる

rubyスクリプト内でタイムアウトした時にプロセスをKILLする

お仕事でいろんな監視スクリプトを組んでミドルウェアやデーモンなどの監視をしています。
大体がrubyスクリプトで作っているのですが、そこで一部はまったことを記録しておきます。

監視の本体は以前このブログでも書いたHobbitというツールを使っています。
監視のスクリプトは各サーバで実行され、実行結果をHobbitのマスターに送信するという感じになっています。

今回は定期的にapacheのmod_statusのデータを取得し、httpdのプロセスの状態を監視するスクリプトで起きた現象です。

スクリプト自体はたいしたことをしていなくて

  1. mod_statusのデータを取得
  2. データを加工して欲しい情報に整形
  3. システムコマンドを叩いて追加データの取得
  4. hobbitにデータ送信

なんてことをしています。

問題はスクリプトがTimeOutした時に起こります。

timeout_time = 60
retry_count  = 3

begin
  timeout(timeout_time) {
    # ここにアレやコレや(上記の1〜3)の処理
  }
rescue Timeout::Error => ex
  retry_count -= 1
  puts Time.now.to_s + "=>" + retry_count.to_s

  if retry_count > 0
    sleep 5
    retry
  end
end

スクリプトではTimeOutした時はエラーをキャッチしてリトライ処理をするようにしています。

大体はリトライで正常終了するのですが、リトライが掛かった時にシステムコマンドで起動したプロセスがスクリプト終了時も残ってしまい、psコマンドを打つと監視スクリプトから呼び出されたプロセスで一杯になってしまっていました。
※ プロセスが生成されるのは上述のシステムコマンドを叩いて追加データを取得するタイミング

対策として、スクリプトから起動したプロセスのpidを取得し、タイムアウト時にKILLするように修正しました。

pidを取るように修正

io = IO.popen('ps --no-headers -C php-cgi | wc -l')
pid = io.pid
cgi = io.read.chomp

プロセスをKILLするように修正

rescue Timeout::Error => ex
  retry_count -= 1
  puts Time.now.to_s + " => " + retry_count.to_s

  # kill timeout process
  if pid > 0
    puts "kill timeout process => " + pid.to_s
    Process.kill('KILL', pid)
  end

  if retry_count > 0
    sleep 5
    retry
  end
end

これでタイムアウト時にプロセスをKILLすることができました。