Passenger 4.0 beta1 の新機能解説

先日、 Phusion Passenger 4.0 beta 1 が公開されました。
近年は Passenger に代わり Nginx + unicorn の構成が Rails業界標準になりつつありますが、Passenger 4 は大きく進化したようなので、そのアップデート内容を紹介します。
なお、この記事は 10/24 に公開された Phusion 公式ブログの記事「Phusion Passenger 4.0 beta 1 is here – Phusion Corporate Blog」を要訳したものです。説明をところどころ省いているので、詳しくは原文を参照してください。

What's new?

複数のRubyバージョンのサポート

1サーバで複数のアプリケーションをホストしている場合、アプリケーション毎に違うRubyインタプリタを指定できるようになりました。

イベント駆動 I/O

nginx や Node.js のように、イベント駆動 I/O に書き換えられました。これは次のような恩恵があります。

  • 同時処理数が事実上無制限になった
    • いままではサーバに設定したスレッド数が同時処理の上限でしたが、Passenger 4 ではCPUやファイルディスクリプタなどのシステムリソースの限界が同時処理の限界になりました。
  • リクエストキューが詰まりにくくなった
    • いままでは複数のアプリケーションをホストしている場合、アクセスの多いアプリケーションに I/O スレッドが専有されて、アクセスの少ないアプリケーションへのリクエストがキューに詰まってしまいがちでした。Passenger 4 では I/O スレッドが無くなったので、アプリケーションプロセスが使用可能であれば直ちに処理できるようになりました。
  • レスポンスのリアルタイムバッファリングのサポート
    • これについては後で述べます。
  • 外部の I/O を多くブロックするアプリケーションのサポート
    • 後述します。
  • 非 HTTP プロトコルのサポート
    • 将来的には、たとえば WebSocket などの非 HTTP プロトコルをサポートします。

レスポンスのリアルタイムバッファリング

転送速度の遅いクライアントからアプリケーションサーバを守るのはWebサーバの課題です。Nginx+unicornアーキテクチャでは、Nginxがクライアントとの通信をバッファリングすることにより、その役割を果たしています。

ただしこれには問題があって、Nginx+unicorn の構成では、レスポンスのバッファリングの影響で、Rails 3.2 のHTTPストリーミングをうまく動かすことができません。

Passenger 4 は、アプリケーションサーバのレスポンスをバッファをしつつ、できるだけ素早くバッファをフラッシュするので、ほぼリアルタイムにクライアントへレスポンスを返すことができます。

また、このリアルタイムバッファリングは Rails の send_file で巨大なファイルを返すときにもうまく動作します。

Zero-Copy アーキテクチャ

Passenger 4 では Linux の Scatter-Gather I/O を活用しています。

通常の I/O は、複数のメモリアドレスに散らばった文字列をまとめて出力するには、一時的にバッファ領域を作って文字列に結合するか、システムコールを複数回呼んで出力する方法を取ります。Scatter-Gather I/O では、複数のメモリアドレスに散らばった文字列を1つのシステムコールで書き込むことができるため、メモリのコピーが発生せず、より効率的です。

ApplicationPool まわりの再実装

Passenger では、アプリケーションプロセスは ApplicationPool として起動し、そこからスレッドに分岐してリクエストを処理します。
ApplicationPool は、 unicorn でいうと、 preload_app を有効にしたマスタプロセスと似たものです。

しかし、 ApplicationPool を再起動している間、アプリケーションはロックされてしまい、レスポンスが返せないという問題がありました。

Passenger 4 では、ApplicationPool の起動は完全に非同期な実装に書き換えられたため、再起動時のロックは発生しなくなりました。また、ApplicationPool のコア部分を Ruby から C++ に書き換えたことで高速かつ省メモリになりました。

アプリケーションプロセスのマルチスレッド化(Passenger Enterprise Edition のみ)

これまで Passenger は マルチプロセス・シングルスレッド アーキテクチャを採用してきたが、近年のアプリケーション要求には問題が出てきていました。例えば、外部の HTTP API との通信が多いアプリケーションなどでは I/O の並行性に問題がありました。

しかし、イベント駆動 I/O を採用したことによる I/O の効率化により、マルチスレッドをうまく動かす準備が整ってきました。

最初の一歩として、 Passenger Enterprise Edition 4 では、マルチプロセスかつマルチスレッドのハイブリッドアーキテクチャを提供します。後方互換性のために、デフォルトではシングルスレッドですが、以下のような設定により、マルチスレッドを有効にできます。

PassengerConcurrencyModel thread
PassengerThreadCount 32

Python WSGI が beta に

Polyglot(多言語)のアプリケーションサーバになるという目標に基づき、Python WSGI の実装をコンセプト版からベータ版に引き上げました。

詰まったプロセスの保護*1

Webサーバをシャットダウンするとき、Passenger はすべてのプロセスをクリーンアップし、より安全にシャットダウンするようになりました。
また、起動途中のアプリケーションプロセスも保護されます。

自動的に .bashrc から環境変数を採用

.bashrc に書かれた環境変数や、ユーザの ulimit の値を Passenger の設定に反映するようにしました。

Apache 環境変数の採用

これまではSpawnマネージャというRubyのプロセスがアプリケーションを立ち上げていたため、Apache環境変数をアプリケーションが参照できませんでした。
Passenger 4 では、アプリケーションプロセスが異なる方法で起動されるようになったため、Apache の PassEnv や SetEnv で設定した環境変数をアプリケーションから参照できるようになりました。

クラッシュレポートの改善

エラーログがより明確かつ有用になりました。C++ のコードがクラッシュした時でさえも詳細なログが記録されます。

また、アプリケーションの起動に失敗した時のエラーページも、環境変数Rubyのバージョンなど、より詳細な情報が表示されるようになりました。
http://blog.phusion.nl/wp-content/uploads/2012/10/errorpage.png

restart.txt を削除しても再起動のトリガにならなくなった

これまで restart.txt を消すと Passenger の再起動が開始されてしまいましたが、そうしなくなりました。

Standalone の自動 Asset Pipeline サポート

Passenger Standalone では、Asset Pipeline に適した Nginx の設定が自動的に行われるようになり、キャッシュの有効期限などが適切に設定されるようになりました。

感想

翻訳は以上です。

Passenger は、 unicorn を含む他の Rack サーバに比べて安定性に特化しており、今回のアップデートもその方向性が強く打ち出たものになっています。
unicorn はシンプルで高速ですが、メモリ消費を管理したり、暴走したプロセスを殺したりという機能が無いので、基本的に運用者が自分たちでコードを書いて使うというハードコアな感じであり、日頃Rubyを書かないタイプの運用者にはけっこう辛いサーバだと思います。

Passenger は明らかに unicorn と方向性が異なっていて、エラーログをきちんと出そうとか、 Enterprise Edition では Passenger 上で簡単に virtualhost が作れる機能があったりとか、現実の運用者へのおもてなし精神を感じます。

そのうちベンチマークを取ってみようと思います。

*1:訳注:保護(protection)が具体的に何を差すのかよくわかりませんでした