Unicorn vs Puma vs Passengerの比較まとめ
Rubyのアプリケーションサーバーのエコシステムは、Unicorn、Puma、Passenger 5 の3つを中心に出来上がっています。Rubyにおいて、アプリケーションサーバーが解決しなければならない具体的な問題はなんでしょうか。どのようにして最適なアプリケーションサーバーを選択すればよいでしょうか。2019年にはこれらのアプリケーションサーバーのニーズはあるでしょうか。
この記事ではこら全てを取り上げ、Rubyの主要なアプリケーションサーバーを比較していきます。
How important is an app server's raw speed?
アプリケーションサーバーそのものの速度がアプリケーションの速度に対して多くの要因となることはほとんどありません。アプリケーションコード、データベースのクエリ、HTTPコールのRubyアプリケーションサーバーとの間の応答速度が、マイクロ秒ないしミリ秒速くなる程度の僅かな変化しか無いと言えます。Unicorn、Puma、Passenger はほぼ全ての Ruby アプリケーションにとって十分に高速だからです。
ベンチマークのようなパフォーマンス測定結果にはさほど重きを置きません。特に絞り込みも行わずにアプリケーションサーバーに対し数百の同時リクエスト攻撃するようなパフォーマンス計測には(例えば、 siege -b <URL> のような)。これは全てのWebアプリケーションについて現実的なリクエストパターンから遠く離れていると言えるためです。
If raw speed isn't critical, what is?
Webアプリケーションはソフトウェア、ハードウェア、ネットワークから成る複雑なシステムです。私が担当した全ての高スループットな実運用アプリケーションは、概ね問題なく動作しましたが、30秒間かかるデータベースクエリ、または60秒かかる外部 HTTP API コールなどの日々の問題を抱えていました。大事なことは、これらの問題を可能な限り切り離して考えることです。例えば、極端に遅い30秒間の処理を実行中の全てのユーザーにまで目を向けないでください。その問題のクエリを発行した問題のユーザーのみにフォーカスすることです。
Rubyのアプリケーションサーバーエコシステムはアプリケーションの動作に繰り返し広域に及んで影響を与えうる3つのインシデントを吸収できるように進化してきました。これらの問題と、アプリケーションサーバーがこれらをどのように防ぐのかを見ていきましょう。
1. Slow clients
ビリーは山を車で駆け抜けながら、ヘラジカの素敵な写真を撮りました。彼はいい感じのハッシュタグをいくつか付けている間に、すぐにアプリケーションに写真をアップロードしたいです。残念ながら、山での電波は安定せず、遅い3G回線で詰まってしまっています。4G回線であれば彼の写真は数秒でアップロードされますが、遅い3G回線だと2分ほど山の中を漂うことになります。
低速なクライアントにアプリケーションサーバーはどのように対処すればよいのでしょうか。低速なクライアントからのリクエストを処理することは他の処理の妨げになるのでしょうか。
Rubyのアプリケーションサーバーのエコシステムにおいては、PumaとPassengerが低速なクライアントを同様の手法であるリクエストのバッファリングで処理します。別々のプロセスがリクエストを処理し、リクエストが完了した場合にのみ利用可能なワーカーに処理が受け渡されます。
PumaとPassengerは低速なクライアントを処理するための機能が備わっています。Unicornはそれ単体では低速なクライアントをケアできません。リクエストが直接ワーカーに渡ってしまいます。Unicornはそれを隠すことはしません。Unicornのドキュメントに明記されています:「ローカルネットワークの外のクライアントにUnicornを提供することを許可すべきではありません。しかしながらNginxをリバースプロキシーとして利用してクライアントのリクエストをバッファリングすることで迂回させることが出来ます。」
2. Slow apps
アプリケーションはいくつかの複雑な画像処理ロジックを駆使しています。
アプリケーションサーバーがRubyのコードを処理している間、アプリケーションに到達しようとしているリクエストには何がおきるでしょうか。
アプリケーションがRuby MRI(と、ほとんどのアプリケーション)を実行している場合、そのアプリはグローバルインタプリタロックの対象となります。これは常にRubyのプロセスの中のたった1つのスレッドだけが、アプリケーションコードを実行できるということを意味します。より多くのリクエストを同時に処理するためには、アプリケーションサーバーがクラスタリングをサポートしている必要があります。同じホストで複数のリクエストにまたがってアプリケーションコードを実行するためには、マルチプロセスを生成(spawn)する必要があります。
Puma、Passenger、Unicornはいずれもクラスタリングをサポートしています。
3. Slow I/O
おっと、1万のデータベース行を持つ最大の顧客によって発行されたクエリにlimit(10)を追加するのを忘れていました。アプリケーションサーバーが何も処理をしていない状態だとしてもデータベースクエリの実行中に他のリクエストはブロックされるでしょうか。
クラスタリングは低速なI/Oには有効ですが、最も効率的なアプローチではありません。
- 単一のRailsのプロセスはかなりのメモリ(多くの場合100MB以上)を使用しますが、プロセス内のそれぞれのスレッドは、ほんの数MBしか使用しません。
- ほとんどのアプリケーションサーバーにはメモリ制約があり、プロセスが増えるほどその制約(限界)に近づいてしまいます。
低速なI/Oに対処する最も効率的な方法はマルチスレッド化です。ワーカープロセスは内部に複数のワーカースレッドを生成します。各リクエストはそれらのスレッドの1つによって処理されますが、データベースクエリによる待機のようにI/Oのための待ちが発生すると、別のスレッドがその処理を開始します。この高速な処理の切り替えはメモリ(RAM)制約を最大限に活用し、CPUをビジー状態に保ちます。
マルチスレッド化がオープンソースで利用できる唯一の選択肢はPumaです。Passengerはエンタープライズ版にてマルチスレッドをサポートします。Unicornはマルチスレッドをサポートしません。
Multiple Processes + Multithreading help eliminate hiccups from slow Ruby code and I/O
まとめると、Rubyアプリケーションを提供する単一のホストは一貫して安定した動作を担保するために、2つの要素が必要となります。
- マルチプロセス:ホストは複数のリクエストに対し同時にRubyのコードを実行することができます。
- マルチスレッド:ホストはI/Oを待機している間により効率的にメモリを使用することができます。
マルチプロセスがなければ、Rubyのコードを実行できるのは1つのプロセス内のスレッドだけなので、Rubyコードの実行がアプリケーションのボトルネックになります。また、マルチスレッドがなければ、アプリケーションは低速なI/Oがボトルネックになります。
Pumaとエンタープライズ版のPassengerはマルチプロセス(クラスタリング)に対応し、マルチスレッドを実行することができます。Unicornとオープンソース版のPassengerはクラスタリングのみサポートしています。
Can you get by with just clustering?
Rubyアプリケーションはクラスタリングだけで何年も生き残ってきました。Pumaは2011年に誕生しましたが、Rubyアプリケーションは2011年以前から機能していたことは明らかです。ですので、クラスタリングだけでも大丈夫とは言えます。ただ、効率の問題としてプロセスでスケールアウトを図ることは、スレッドで図るより、はるかにコストがかかります。
また、I/Oバウンドなアプリケーションでない場合、マルチスレッドのサーバーであろうとなかろうと大差はありません。しかし、これはデータベースや他のサービスにアクセスし、長い待機時間が発生しうる殆どのWebアプリケーションにおいて一般的ではありません。
What if your app isn't threadsafe?
当然のことながら、アプリケーションがスレッドセーフでない場合、マルチスレッドなアプリケーションサーバーを利用することはできません。その場合でもPuma、Unicorn、Passengerは全て対応可能です。Pumaの場合、各ワーカープロセスにスレッドを1つだけしか利用できない制約を設定します。そうすると、Unicornまたはオープンソース判のPassengerのように動作します。
Which app server should I use?
Unicorn
Unicornはマルチスレッド機能がなく、低速なリクエストのバッファリングをNginxに依存せざるをえないことから、2019年という時代に利用は難しいと言わざるをえません。
最も利用が適しているのは、低速なクライアントの影響を受けず、かつスレッドセーフでない内部向けのアプリケーションです。とは言え、このケースではPassengerやPumaを利用しても大差ないと思われます。他のアプリケーションサーバーの入出力について学んでみてはいかがでしょうか。
Phusion Passenger
Phusionの開発に携わってきた人々はRubyコミュニティにおいて長年にわたって確固たる信頼を築き上げてきました。2008年のRuby Enterprise Edition リリース以来、Rubyの内部を深く掘り下げてきており、ドキュメントも充実しています。PassengerはRubyの他に、Python、Node.js や Meteorアプリケーションもサポートしています。
Passengerが適するケース
- Passengerがサポートしている複数の言語で開発されたアプリケーションがあり、アプリケーションサーバースタックを統合したい場合。
- リスク回避のためであれば、アプリケーションサーバーにお金をかけてもよい場合。エンタープライズ版のPassengerはPumaと比較して、より充実したドキュメント、多彩な設定オプション、デバッグツール、専用サポートなど魅力的なリスク回避の選択肢を提供している。
Passengerが適さないケース
- Rubyのアプリケーションのみ提供している場合。
- サーバーにお金をかけたくない場合。
- アプリケーションサーバーにお金をかけたくなく、メモリの限られた環境(Herokuのような)で動作していて、マルチスレッドによるI/Oの同時実行性を追加出来ない場合。
Puma
Pumaが新しく生成されたRailsアプリケーションやHeroku上のデフォルトのアプリケーションサーバーであるのには理由があります。設定が簡単で初期状態で大抵、すぐ動きます。Pumaから使い始め、より高度な機能と設定オプションが必要になってきたタイミングでPassengerの魅力が理解できてくるというのは自然な流れです。
Unicorn, Passenger 5, and Puma Feature Comparison
Unicorn、Passenger 5、Pumaの機能比較
以下の表は、いくつかの重要な観点で各アプリケーションサーバーを比較したものです。
Unicorn | Puma | Passenger 5 | |
---|---|---|---|
Clustering | Yes | Yes | Yes |
Multithreaded | No | Yes | Enterprise Only |
Slow client buffering | No | Yes | Yes |
ActionCable | Yes | Yes | Yes |
Support | Open Source | Open Source | Open Source / Paid |
Installation | Gem | Gem | Binary or Gem |
Zero-Downtime Deploys | Yes | Yes | Enterprise |
パフォーマンスを強化する準備はできていますかReady to step up your performance game?
サーバーを追加することは遅いコードの対処になることがあります。Scout APM は非効率的でコストのかかるコードを見つけ修正する役に立ちます。N+1問題のSQL呼び出し、メモリの肥大化、およびその他のコードに関する問題を自動的に識別するため、デバッグにかかる時間を短縮し、プログラミングに専念できる時間を増やせます。
サイトを最適化する準備は出来ていますか?無料トライアルにいますぐ登録