rubyで常駐プロセス作りたくてgod 使ってみた


Ruby gems

今日はオダイリー先生と渋谷でmokumokuしております。
特に何やるか決めてなかったっていうか、やりたいことありすぎるんでまず最初の1hでblogを書くことにしますた。

streamingAPIのコネクション貼ったままにしたい

そもそも常駐プロセスなんで作りたいのかっていうと、以前からTwitterのStreamingAPIからバサバサとTweetを取得するアプリを書いてはたまに動かしていたのですが、スポットでしか使わなかったのでcronで回したり自分で常駐化させるコード書いたり色々やってました。
常駐している間はTwitter側から切られない限りTCPコネクション張りっぱなしっていう。

ところが、gemでプロセスを簡単に常駐させてくれるgodなる存在を最近知りました。
なんか、めっちゃ昔からあるそうでして、今まで知らなかった事が非常に恥ずかしい限りでございます。

godのサイトはこちら

使ってみる

god て ゴッドて読むんでしょうか。
なにそれ神なの? 万物を創造するの?

インストール

$ gem install god

$ god –version
でバージョンが表示されればインスコ成功。 なお、これはgemで入ってくるので rbenv を使用してた私は rbenv rehash コマンドの実行を忘れ少しハマった模様。

概要

さぁ、さっそく使っていくぜ。
god は常駐させたいrubyのソースとは別に .god というスクリプトを書いてこやつを実行する事で実現するようです。

なお、 .god はrubyでコーディングできるのでvimのシンタックスに *.god を追加しました。
けどなぜ .rb にしなかったのか? は謎。 見分けるのを容易にしたいから?

vi test.god

FILE_PATH = "/home/ebi/sample/test.rb" # あなたの常駐化させたいアプリ
God.watch do |w|
  w.name = "test"
  w.start = "ruby #{FILE_PATH}"
  w.keepalive

この最小限のゴッドファイル(?)を作成し、
$ god -c test.god -D
と実行するだけで、見事にアプリが常駐します。

-c のオプションはgodのconfigrationファイルの指定オプション。 てかこれ必須になるんじゃないのかな。
-D は実行状況がコンソールに表示されるオプション。
最初にいじった時や、godをリスタートさせたい時、後述するCPU/メモリ監視がちゃんと実行されるかを確かめたい時に -D は便利。

$ ps aux | grep ruby
してみるとちゃんと生きてました。

godのお便利機能

常駐アプリを実行させている最中、マシンのCPUやメモリ使用率がユーザの設定した閾値を超えた場合再起動させてくれるそうです。
ガチなサービスは別の監視アプリを入れてると思うけど、自分みたいにちょっとStreamingAPIつないでおきたいといった遊びアプリにとってはお手軽で非常にありがたい。

godファイルの keepalieにハッシュでオプションを渡してあげると良いそうですよ。

FILE_PATH = "/home/ebi/sample/test.rb" # あなたの常駐化させたいアプリ
God.watch do |w|
  w.name = "test"
  w.start = "ruby #{FILE_PATH}"
  w.keepalive(:cpu_max => 50.percent
              :memory_max => 150.megabytes)

また、起動や再起動など複数のコマンドを指定しておいてCPUやメモリの閾値を超えた際に実行するコマンドを使い分ける事も可能の模様。
さらにさらに、プロセスが落ちていた際に再起動する場合指定時間間隔を待ってからコマンドを実行したり・・ などいたれりつくせり。
本当に助かったです。

本家のソースをみるとわかりやすいと思います。

RAILS_ROOT = "/Users/tom/dev/gravatar2"

%w{8200 8201 8202}.each do |port|
  God.watch do |w|
    w.name = "gravatar2-mongrel-#{port}"

    w.start = "mongrel_rails start -c #{RAILS_ROOT} -p #{port} \
      -P #{RAILS_ROOT}/log/mongrel.#{port}.pid  -d"
    w.stop = "mongrel_rails stop -P #{RAILS_ROOT}/log/mongrel.#{port}.pid"
    w.restart = "mongrel_rails restart -P #{RAILS_ROOT}/log/mongrel.#{port}.pid"

    w.pid_file = File.join(RAILS_ROOT, "log/mongrel.#{port}.pid")

    w.behavior(:clean_pid_file)

    w.start_if do |start|
      start.condition(:process_running) do |c|
        c.interval = 5.seconds
        c.running = false
      end
    end

    w.restart_if do |restart|
      restart.condition(:memory_usage) do |c|
        c.above = 150.megabytes
        c.times = [3, 5] # 3 out of 5 intervals
      end

      restart.condition(:cpu_usage) do |c|
        c.above = 50.percent
        c.times = 5
      end
    end

    # lifecycle
    w.lifecycle do |on|
      on.condition(:flapping) do |c|
        c.to_state = [:start, :restart]
        c.times = 5
        c.within = 5.minute
        c.transition = :unmonitored
        c.retry_in = 10.minutes
        c.retry_times = 5
        c.retry_within = 2.hours
      end
    end
  end
end

これだけじゃない!他にも色々と痒い所に手の届く機能が実装されていました。一部を抜粋しご紹介。
・環境変数の設定
・ディレクトリの移動(chroot)
・プロセス実行ユーザのUID/GIDの指定

おりこうですね。

毎回configファイルを指定しなくてもいいみたい

あまり使う機会は無いと思うのだけれども、アプリをコンソールで手動で再起動させる時はwatchで指定したアプリの名前を指定して・・
$ god restart test
などとすれば勝手に再起動してくれました。 もちろん god stop {APP_NAME} 等でも使える。

logも出せるんですか?

ログの出し方分からなかったけど、ちゃんと読んだら書いてった。

Redirecting STDOUT and STDERR of your Process

By default, the STDOUT stream for your process is redirected to /dev/null. To get access to this output, you can redirect the stream either to a file or to a command.

To redirect STDOUT to a file, set the log attribute to a file path. The file will be written in append mode and created if it does not exist.

だ、そうです。 あと、STDERRはどうなの? って思ったけどデフォルト設定ではSTDOUTにリダイレクトされるそうですよ。 つってもログ見ててもどっちから出てきたログなのか未だよく理解できてないんだけど。。

w.log = '/var/log/myprocess.log'

などとすればちゃんとログが記録されている事を確認できました。

まとめ

2週間くらい前に試してみて動かしていますが、いまのところ停めたり再起動したりしても安定して動いてくれています。 
ただ全然リソースを食うアプリじゃないので、監視はうまくいってるのかは謎。

twitterのstreamingAPI叩いてDBに貯めたりしてる人はオヌヌメかもです。

おわり

2015-02-11 | Posted in RubyNo Comments » 


関連記事

Comment





Comment



*