FrontPage  Index  Search  Changes  RSS  Login

[Ruby] Unicorn 概要

はじめに

Unicornは、Unix系システムで動作するRackアプリケーション用サーバ。接続時間が短いことを前提とした設計となっている。

- http://unicorn.bogomips.org/

preforkモデル

Unicornはpreforkモデルを採用している。

Reverse Proxy
 └TCP Socket[0.0.0.0:8080] > Unicorn(Master)
  ├(fork)─Worker[0] < TCP Socket[0.0.0.0:8080]
  ├(fork)─Worker[1] < TCP Socket[0.0.0.0:8080]
  &#9478;
  └(fork)─Worker[N-1] < TCP Socket[0.0.0.0:8080]

マスタプロセスからforkした複数のワーカープロセスがあり、クライアントからのリクエストをワーカープロセスで処理する。上図ではTCP Socketと書いているが、Unixドメインソケットも利用できる。 マスタプロセスは、起動時に決められた数のワーカープロセスの数をforkし、稼働中にその数に保つように管理する。

詳細は後述するが、アプリケーションのプリロード機能が利用できる。これは、マスタプロセス起動時点でRackアプリケーションをロードしてしまう仕組み。デフォルトではfork後に、各ワーカープロセス内でアプリケーションをロードする様になっているが、この機能を利用することでCopy-on-Writeフレンドリな運用が可能となる。

- http://unicorn.bogomips.org/README.html

設定ファイルは Ruby DSL

Unicornサーバのパラメータ設定を行う設定ファイルは、Rubyの内部DSLで記述することが出来る。DSLと書いたが、evalされるだけなのでRubyコードだと考えれば良い。 evalは、Unicorn::Configuratorクラスのインスタンスをコンテキストとして実行される。

:例(http://unicorn.bogomips.org/examples/unicorn.conf.minimal.rb):

# Minimal sample configuration file for Unicorn (not Rack) when used
# with daemonization (unicorn -D) started in your working directory.
#
# See http://unicorn.bogomips.org/Unicorn/Configurator.html for complete
# documentation.
# See also http://unicorn.bogomips.org/examples/unicorn.conf.rb for
# a more verbose configuration using more features.

listen 2007 # by default Unicorn listens on port 8080
worker_processes 2 # this should be >= nr_cpus
pid "/path/to/app/shared/pids/unicorn.pid"
stderr_path "/path/to/app/shared/log/unicorn.log"
stdout_path "/path/to/app/shared/log/unicorn.log"

- http://unicorn.bogomips.org/Unicorn/Configurator.html

基本的にはパラメータ値の設定を行うことになるが、いくつか注意すべきポイントがある。

フック

以下のフックが利用できる。

before_exec

reexecによって新しいマスタプロセスを作る際のexec()実行の直前。

before_fork

ワーカープロセスをfork()する直前。

after_fork

ワーカープロセスをfork()した後、いくつかの内部処理を終えた後。

preload_app

アプリケーションのプリロード機能を有効にするスイッチ。有効にした場合、マスタプロセス起動時にアプリケーションをロードし、ワーカープロセス側でのロードを行わなくなる。これにより、Copy-on-Writeフレンドリな環境でのメモリ消費を抑えることが出来る。

注意すべきは、マスタとワーカーとではリソースを共有するという点。ログファイルはアトミックに書き込めるようになっているため問題ないが、ソケットはfork後に開き直す必要がある。

また、HUPシグナルによって再起動をした場合の挙動も変わる。機能が無効であれば、ワーカープロセスを再起動する際にアプリケーションをリロードする。機能が有効な場合、マスタプロセスでロード済みのアプリケーションを使い回す。

シグナルハンドラ

Unicornでは、基本的にマスタプロセスにシグナルを送信することで管理する。ワーカープロセスでもシグナルを受け付けているが、基本的にユーザからワーカープロセスに直接シグナルを送ることは想定していない。

- http://unicorn.bogomips.org/SIGNALS.html

マスタプロセスのシグナルハンドラ

HUP

設定ファイルを再読込し、すべてのワーカープロセスを処理を中断せず完了を待ってから再起動させる。

INT, TERM

ワーカープロセスも含め、直ちに終了させる。

QUIT

処理中のワーカープロセスの処理の完了を待ってから終了させる。

USR1

マスタプロセスとワーカープロセスのログファイルを開き直す。

USR2

現行マスタプロセスからexec()して新しいマスタプロセスを生成する。古いマスタプロセスはQUITで別途終了させないと生き残り続ける。

WINCH

マスタプロセスは稼働させたまま、すべてのワーカープロセスを処理を中断せずに完了を待ってから止める(マスタプロセスによる自動forkも行われなくなる)。デーモンモードでのみ有効。

TTIN

ワーカープロセスを一つ増やす。

TTOU

ワーカープロセスを一つ減らす。

ワーカープロセスのシグナルハンドラ

INT, TERM

ワーカープロセスを直ちに終了させる。マスタプロセスにWINCHシグナルを送ってforkを止めない限り、マスタプロセスによる自動forkが働いて新しいワーカープロセスが作られる。

QUIT

ワーカープロセスを処理を中断せず完了を待って終了させる。マスタプロセスにWINCHシグナルを送ってforkを止めない限り、マスタプロセスによる自動forkが働いて新しいワーカープロセスが作られる。

USR1

ログファイルを開き直す。リクエストを処理中の間はログファイルを開き直さないため、一つのリクエストでログファイルがばらけることはない。マスタプロセスとワーカープロセスのユーザが異なる可能性があるため、通常はマスタプロセスにUSR1シグナルを送ってログを開き直すべき。

切断なしにバイナリをアップグレードする手順

以下の手順を踏むことで、サービスダウンなしにマスタプロセスおよびワーカープロセスを置き換えることが出来る。つまり、Unicornのアップグレード時に切断せずに済む。

  1. マスタプロセスにUSR2を送る
  2. 新しいマスタプロセスが生成されたことをプロセスマネージャかPIDファイルで確認する
    1. PIDファイルを使っているなら、古いプロセスのPIDファイル名には".oldbin"が追加されている
    2. 新しいマスタプロセスと元々のマスタプロセスの2つが稼働していて、どちらに属しているワーカーでもリクエストを処理できる
  3. 古いマスタプロセスにWINCHを送って新しいマスタプロセスのワーカーでだけ処理させる
    1. Unicornが対話的なターミナルにバインドされている場合、この手順は飛ばせる
  4. 古いワーカーを終了させて、新しいワーカーが稼働している状態
  5. うまくいっている様であれば、古いマスタプロセスにQUITを送る
    1. 何かがおかしいようであれば、古いマスタプロセスにHUPを送って再起動させ、新しいマスタプロセスにQUITを送って終了させる
unicorn master (old)
├ unicorn worker[0]
├ unicorn worker[1]
├ unicorn worker[2]
├ unicorn worker[3]
└ unicorn master
   ├ unicorn worker[0]
   ├ unicorn worker[1]
   ├ unicorn worker[2]
   └ unicorn worker[3]

※ 上記は古いマスタプロセスと新しいマスタプロセスが同居している状態(手順2-2)。

哲学

- http://unicorn.bogomips.org/PHILOSOPHY.html

根底にあるのはUnixの哲学

Write programs that do one thing and do it well.

- http://en.wikipedia.org/wiki/Unix_philosophy

複雑さの排除

- 接続(占有)時間の長い用途は想定しない(必要な部分ではリバースプロクシに任せる) - preforkモデルを採用し、スレッドやイベント駆動による複雑さを排除

時間がかかるクライアント

ローカルネットワーク外から接続しにくるクライアントについて。

HTTP/1.1の持続的接続によってネットワーク接続のオーバーヘッドを軽減できるが、これは同時にワーカープロセスの占有時間の長期化を意味する。UnicornのワーカープロセスはシングルスレッドのブロッキングI/Oを採用しているため、持続的接続を許した場合にほかのクライアントの接続をブロックしてしまう問題が起きる。

そのため、Unicornでは持続的接続をサポートしない。

リバースプロクシ

Unicornは基本的にリバースプロクシとしてのNginxと組み合わせた運用を想定している。

設計

- http://unicorn.bogomips.org/DESIGN.html

RubyじゃないコードはHTTPパーサのみ

HTTPパーサをMongrelから持ってきている。これはRagelとCで書かれているが、UnicornでRubyでないコードはこれらのみ。

ワーカープロセスの管理

一つのマスタプロセスが、ワーカープロセスの除去や作成を行う仕組み。

ワーカープロセスのロードバランシング

カーネルに任せる。

- select - accept

ワーカープロセスのスケーリング

TTIN/TTOUシグナルによるオンデマンドなスケーリングを自動的に行うことはない。

最後に

Unicornでの運用実績はないため、上記はすべてドキュメントを参考にまとめただけのものになります。

Last modified:2011/10/08 00:25:34
Keyword(s):[ruby]
References: