capistrano + unicornではまった。

以外と語られていないような気がするんだが、railscapistranounicornをつかっていると、思わぬ落とし穴がある。

今まで順調だったcapistranoでのデプロイでunicornの立ち上げが失敗するようになる。 unicornのupgrade(SIGUSR2)がきかなくなった。

upgradeってのは、gracefulなんだけど、

/usr/local/rbenv/versions/1.9.3-p448/lib/ruby/gems/1.9.1/gems/bundler-1.5.2/lib/bundler/definition.rb:23:in `build': /xxxxxxx/releases/日付的なやつ/Gemfile not found (Bundler::GemfileNotFound)
        from /usr/local/rbenv/versions/1.9.3-p448/lib/ruby/gems/1.9.1/gems/bundler-1.5.2/lib/bundler.rb:152:in `definition'
        from /usr/local/rbenv/versions/1.9.3-p448/lib/ruby/gems/1.9.1/gems/bundler-1.5.2/lib/bundler.rb:115:in `setup'
        from /usr/local/rbenv/versions/1.9.3-p448/lib/ruby/gems/1.9.1/gems/bundler-1.5.2/lib/bundler/setup.rb:17:in `<top (required)>'
        from /usr/local/rbenv/versions/1.9.3-p448/lib/ruby/1.9.1/rubygems/custom_require.rb:36:in `require'
        from /usr/local/rbenv/versions/1.9.3-p448/lib/ruby/1.9.1/rubygems/custom_require.rb:36:in `require'

みたいな感じでエラーが出て再起動が出来ない。

解決方法

下記URLのBUNDLE_GEMFILE for Capistrano usersを見ればわかると思います。

capistranoをつかってると、多分、RAILS_ROOT/config/unicorn/production.rb とか作ってると思うんで、そこに、

before_exec do |server|
  ENV["BUNDLE_GEMFILE"] = "/path/to/app/current/Gemfile"
end

を追記してやる unicornの実行直前にRAILS_ROOT/current/GemfileのGemfileを見るように``BUNDLE_GEMFILE```を差し替えてやればいい。

普通、大体配置は同じと思うんで、さらしておくけど、下記のように出来るかなーと。

before_exec do |server|
  ENV["BUNDLE_GEMFILE"] = File.join(File.expand_path("../../../../", __FILE__), "current", "Gemfile")
end

注意してほしいのは、一回この現象が起きちゃった人、もしくは、対応する前の起動設定で起動しちゃった人は、Unicornをstop startして再起動してあげないとだめ。SIGUSR2 では解決できないと思う。

原因

原因としては、僕はcapistranoで5世代まで昔のディレクトリを保存しておくようにしているんだけど、それが、始めにunicornを立ち上げたディレクトリが消されたタイミングで Gemfile が存在しなくなってしまうことにある。

capistarano unicornはこんな感じで、unicornを立ち上げる。

RAILS_ENV=vm BUNDLE_GEMFILE=/xxxxxxx/releases/日付的なやつ/Gemfile bundle exec unicorn -c $UNICORN_CONFIG_PATH -E vm -D

この時点でENV["BUNDLE_GEMFILE"]にはRAILS_ROOT/releases/日付的なやつ/Gemfileが入ってしまうので、これをunicornプロセスが覚え続けてしまう。

そうすると、そのうちSIGUSR2がなげられても、一旦BUNDLE_GEMFILEの方にGemfileを確認してしまうので、既に存在しないGemfileを確認にいってしまう。

そして該当のエラーがでてしまうのだっ! だので対応は、解決方法の所で示した通りだっ!

unicornとunicorn_railsのオプションの違い

unicornunicorn_railsはほとんど同じ挙動をして、 application serverを立ち上げてくれますが、一部オプション指定の意味が違います。

ちょっとハマったので書いておくことにします。

結論

  • unicornのオプション-ERACK_ENVを設定
  • unicorn_railsのオプション-ERAILS_ENVを設定

参考: http://unicorn.bogomips.org/

経緯1

Capistrano 3を導入しまして、unicornを使っていたので無邪気に

を使っていました。(僕の環境では0.3.2) 普通に動かしていたんですが、muninがunicornのmemory statusをうまく取得できていないようでした。

該当のmuninのplugin

pluginに目をちゃんと通すとわかるんですが、

   def memory_usage
      result = { :master => {master_pid => nil}, :worker => {} }
      ps_output = `ps auxw | grep unicorn_rails`
      ps_output.split("\n").each do |line|

なるほど。unicorn_railsgrepしてます。

ということで、よく調べてみると、sepastian-capistrano3-unicornのデフォルトでは unicornを使ってunicornプロセスを起動していて、このままではこのunicorn_memory_status に引っかからないわけです。

ということで、unicorn立ち上げをunicorn_railsを通して実行する必要があるわけですがこれはわけなく行えます。 sepastian-capistrano3-unicorn は、unicorn_binというパラメータを用意してまして、これのデフォルトがunicornになっているわけなので、ここを変更する。

RAILS_ROOT/config/deploy.rbに下記記述を追記します。

set :unicorn_bin, ->{"unicorn_rails"}

(ラムダで囲んでんのは特に深い意味は無いんですが、評価時にいろいろ変更するようなモノが多いので念のため標準的にこうするようにしています。)

とここで世界は平和に終ると思っていました。

経緯2

unicornの時の-EパラメータがRACK_ENVであるということは理解していたので、RAILS_ENVunicorn実行前に設定するようにしてたわけです。 こんな感じで

RAILS_ENV=hogehoge; unicorn -E development .....

僕はVM上でrailsを動かすコトが多くて、vmというRAILS_ENVをつくっていました。 しかしunicorn_railsに変更してみたら、僕のVMで急にunicornが立ち上がらなくなりました。

RAILS_ENV=vm; unicorn_rails -E development .....

どうも、dababase.ymlにadapterの指定がない的なエラーがはかれていて、おかしーなーと思ったんです。 と、ここで気付きました。

-Eオプションが、RAILS_ENVを上書いていることを。

んで、下記をみた

なるほどね。

以上!