2009-11-10
■ [ruby] 大規模Railsサイトのための新しいHTTPサーバ、Unicorn
githubの中の人が、ブログで「Unicorn使い始めて一ヶ月くらい経つけどいい感じだよ」と書いています。 適当に要点だけ拾ってみました。
Unicornって何よ?
UnicornはRubyのためのHTTPサーバ。MongrelやThinのようなものだけど、全く違う設計と思想を持っている
ありがちな構成
[mongrel] [mongrel] .. [nginx] -> [haproxy] -> [mongrel] [mongrel] .. [mongrel] [mongrel] ..
問題点:
- あるactionの処理に60秒以上かかったとき、Mongrelが当該スレッドをkillしようとして固まることがある
- メモリが一定量を超えたときMongrelを再起動するのが遅い。
- デプロイ時に9個のmongrelが同時に再起動するので重い(対策もややこしい)
- mongrel1つの再起動もけっこう遅い
- push方式のバランシングなので、本当にうまいインスタンスが選ばれるか分からない
そこでUnicornですよ
Unix [unicorn worker] [unicorn worker] .. [nginx] -> ドメイン -> [unicorn worker] [unicorn worker] .. ソケット [unicorn worker] [unicorn worker] ..
nginxを、Unixドメインソケット*1経由でUnicorn workerにリクエストを送るように設定
- Unicorn masterは起動すると、まずアプリケーションをロードする。ロードが終わり次第、16個のworkerをforkする (つまり、workerが16個でもロードは1回だけ)
- 各workerはselect()でソケットからの入力を待つ (つまり、workerが暇になったらリクエストを処理する。いわばカーネルによるロードバランシング)
問題点がどう変化するか:
- workerの処理が30秒*2以上かかると、masterはこのworkerをkillして新しいのをforkする。(forkなので、インスタンスの生成は一瞬で終わる)
- workerがメモリを食いすぎると、godやmonitからSIGQUITが送られる。すると、workerは「リクエストが終わり次第」exitする。(リクエストの途中でkillされることがない)
- デプロイ時は、Unicorn masterにSIGUSR2を送る。
- するとmasterは新しいmasterを起動し、新しいアプリケーションコードが読み込まれ、workerがforkされる。
- 最初のworkerが古いmasterの存在を検知し、SIGQUITを送る。古いmasterは自分のworkerたちを終了してexitする。
- 常にいずれかのworkerが動いているので、ダウンタイムがない。また、新旧のworkerは同じソケットを使うので、nginxは変化に気づかない。
- Unicornの再起動は速い(masterが1回コードをロードするだけなので)。workerの再起動もkillしてforkするだけなのでとても速い。
- pull方式なので、バランシングはworkerが暇になったら自分で取りに行くだけと単純そのもの。
本文ではこのあと移行のためのヒントと、githubで使われている設定、ベンチマークが紹介されています。
関連しそうな書籍
4873114004
4873114160
おまけ
姉妹品のRainbows!も気になってるんだけど、いまいちよく分かんないです (workerの粒度と並列化方法を切り替えられるようにすることで、long-pollとかに対応する??)
Rainbows! は低スペックなマシン用のやつらしいです。