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

まめ畑

ゆるゆると書いていきます

サーバ自動再起動スクリプト

Ruby Server

自宅で運用しているサーバが最近NICの不調orチップ周りの埃のせいで、トラフィックが大量にやってくるとネットワークに繋がらなくなるという問題が続発してサービスが滞ってしまって、困ったのでRubyの勉強もかねて、NIC周りの不調が起こった時に鯖を自動的に再起動するスクリプトを書いてみた。
買い換えろと言われそうですが、NICを買い換えるのは直ぐには無理(鯖の体的な意味で

家庭内ネットにも繋がらなくなると、NASをマウントしているのでそれを使うようなデーモンが走った時にエラー続発。
復旧後のエラーメールで俺涙目という式が成立←結論

このスクリプトを投下したから少しはましになるかな。
でも、書き方変だし、何か起こりそうな予感・・・。
やってることは簡単だから、もう少し勉強しよう。
おかしいぞ!ってコメントはお気軽にして下さい。

やってることは簡単、/var/log/messagesを参照してネット周りがハングアップした時の特徴を抽出して、最終検出時間を記録して再起動。
再起動後に管理者へメール送信。
ただこれだけ。
このスクリプトをcronに設定して5分間隔で実行。
ログに現れずにハングされると終わり。
これを解決しないといけない←今ここ

Tmailを使っています。
gemでインストールしただけじゃ、使えなかったのでRubyのディレクトリにいるのをコピーした。
なぜだろう?


重大なバグがあったので2/17追記・訂正
ログロテート後に空行のみのlockファイルが出来るが、その処理が甘かったため再起動を繰り返す


lockファイルが無い場合の動作を安定させました。

require "tmail"
require "net/smtp"
require "date"
require "time"

#定数を定義
Logname = "/var/log/messages"
Lockname = "./lock"
MailFlag = "./mlock"
Matchstring = "smb_add_request:"

#ログを取得して、smb_add_request:が最後に出現した時間を取得
def readLogs()
  begin
	lastLog = ""
	tmp = ""
	findFlag = false
	open(Logname,"r"){|io|
	  while line = io.gets
		logArry = line.split(/ /)
		if(logArry[5].index(Matchstring))
		   tmp = logArry
		   findFlag = true
		end
	  end

	  if(findFlag)
		i = 0
		3.times{
		   lastLog += tmp[i]
		   i = i+1
		}
	  end

	  io.close
	  return lastLog.slice(0,lastLog.length-3)
	}
  rescue => ex
	print ex.message+"\n"
	exit
  end
end

#最終ヒット時間を記録
def writeLastLog(writeData)
  begin
	open(Lockname,"w"){|f|
	   f.write(writeData)
	   f.close
	}
  rescue => ex
	print ex.message+"\n"
	exit
  end
end

#時間の比較を実行
def cheackLog(lastData)
	begin
	   open(Lockname,"r"){|io|
	     while line = io.gets	
	       io.close
		   return line == lastData
	     end
	   }
	   return true		#Lockファイルの中身がなかったら
	rescue
	   return false   #ファイルが無かったら
	end
end

#再起動を実行
def doReboot()
	#print IO.popen("ls").gets
	IO.popen("reboot")
end

def sendErrorMail()
	begin
	   d = DateTime.now
	   mail = TMail::Mail.new
	   mail.to = "管理者のアドレス"
	   mail.from = "送信元アドレス"
	   mail.reply_to = "送信元アドレス"
	   mail.subject = "Server was Rebooted"
	   mail.body = "Server was Rebooted. "+d.strftime("%Y/%m/%d %H:%M:%S")

	   mail.date = Time.now
	   mail.mime_version = "1.0"
	   mail.set_content_type 'text', 'plain', {'charset'=>'iso-2022-jp'}

	   mail.write_back

	   Net::SMTP.start("localhost") do |smtp|
	      smtp.sendmail(mail.encoded,mail.from,mail.to)
	   end
	rescue => ex
	   print ex.message+"\n"
	   exit
	end
end

#復帰後メール送信
def cheackMailLog()
	begin
	   isDelete = File.exist?(MailFlag)
	   if(isDelete)
		IO.popen("rm -f ./"+MailFlag)
		sendErrorMail()
	   end
	rescue => ex
	   print ex.message+"\n"
	   exit
	end
end

#メール送信用フラグファイル作成
def makeMailFile()
	begin
	   IO.popen("touch "+MailFlag)
	rescue => ex
	   print ex.message+"\n"
	   exit
	end
end

#nilか判定
def isNil(data)
	return data == nil
end


cheackMailLog()			#メールを送信すべきか確認

lastDate = readLogs()	#NIC異常を確認した最終ログ時間を取得
if(isNil(lastDate))		#該当ログがなかった場合は終了
	exit
end

isReboot = cheackLog(lastDate)	#既に確認済みのログエントリか確認
if(isNil(isReboot))				#エラー発生時は終了
   exit
end

if(!isReboot)					#Rebootが必要ならログに記録して実行
	makeMailFile()				#送信用フラグファイル作成
	sleep(1)
	writeLastLog(lastDate)
	sleep(1)
	doReboot()
end

lockファイルには Feb1501:24 のように書き出されます。
秒まで入れると、このスクリプトが走ってる途中でエラーが書き込まれると再起動後に、また再起動してしまうかも知れないのでということです。
メール送信するかどうかは、mlockというファイルの有無で判断しています。

適当にディレクトリを作って、その中で実行している場合はcronで実行する際に、そのディレクトリ内に移動してからじゃないと、あらぬところにファイルが生成されます。

ちなみに、cronの設定はこんな感じ。

0-59/5 * * * * cd /root/設置Dir/; /usr/bin/ruby reboot.rb

0-59/5 というのは、0-59分の間で5分間隔で実行という事です。