sessanの日記

主に開発技術関連でお勉強したことをまとめていくサイトです。

Herokuでunicornを使うときの注意点

Herokuのダッシュボードには、次の画像のように、稼働しているアプリのリソース使用状況をMetricsとして表示する機能がある。

f:id:sessan:20150113211319p:plain

最近某アプリで、このMemoryのところが常に上限を超えている状況が発生したのだけど、原因は意外に気づかないところだったので、ブログに書いておく。

ふつーの人は、

Getting Started with Rails 4.x on Heroku | Heroku Dev Center

とかを見ながら、config/unicorn.rbをコピペしてしまうと思う(自分もそうだった)。

worker_processes Integer(ENV["WEB_CONCURRENCY"] || 3)
timeout 15
preload_app true

before_fork do |server, worker|
  Signal.trap 'TERM' do
    puts 'Unicorn master intercepting TERM and sending myself QUIT instead'
    Process.kill 'QUIT', Process.pid
  end

  defined?(ActiveRecord::Base) and
    ActiveRecord::Base.connection.disconnect!
end

after_fork do |server, worker|
  Signal.trap 'TERM' do
    puts 'Unicorn worker intercepting TERM and doing nothing. Wait for master to send QUIT'
  end

  defined?(ActiveRecord::Base) and
    ActiveRecord::Base.establish_connection
end

この

worker_processes Integer(ENV["WEB_CONCURRENCY"] || 3)

の部分がポイントで、これでunicorn.rbのワーカープロセスの数が決まる。

環境変数WEB_CONCURRENCYを設定していればその数が、設定していなければ3がunicornのワーカープロセスの数になる。

これが僕がハマった罠だった。

Herokuの1Xのdynoは、メモリの最大値が500Mである。自分のアプリはWebアプリとしてはごく普通のアプリだと思うが、unicornのワーカープロセス1つのメモリ使用量の平均値は300Mであった(これはNewRelicで計測)。

したがって、何も考えずにunicorn.rbの設定ファイルをパクっちゃうと、環境変数がなければ、unicornのワーカープロセスの数が3つになってしまい、平均300 x 3 = 900Mもメモリを使用してしまう。これは1Xのdynoの最大値を大きく超えている。

自分は、これに気づかずに2Xのdynoを購入してしまったが、1Xですませるなら、環境変数WEB_CONCURRENCYを1に設定しておくべきであった。

ちなみに、2Xにするとdynoのメモリの最大値は1Gまで上がるが、これでも900Mが平均だと結構な頻度でswapが発生し、メモリ使用量のエラー(R14)が出てしまっていた。2Xの場合、WEB_CONCURRENCYを2に設定したら、R14エラーは出なくなった。某アプリでは1Xには戻さずに、並列処理ができるようにするために結局2Xで稼働している。