Ruby on RailsアプリケーションでUnicornプロセスを最適化する方法



あなたがRails開発者であれば、おそらくUnicornについて聞いたことがあるでしょう。Unicornは、同時に多くの要求を処理できるhttpサーバーです。



並行性を確保するために、Unicornは複数のプロセス作成を使用します。 なぜなら 作成された(フォークされた)プロセスは互いのコピーです。つまり、railsアプリケーションはスレッドセーフでなければなりません。



これは素晴らしい コードがスレッドセーフであることを確認するのは困難です。 これがわからない場合は、 Pumaなどの並列Webサーバーや、 JRubyRubiniusなどの並列処理を実装する代替Ruby実装についても話をすることはできません。



そのため、Unicornは、レールアプリケーションがスレッドセーフでない場合でも、レールアプリケーションに並列処理を提供します。 ただし、これには手数料が必要です。 Unicornで実行されるRailsアプリケーションには、より多くのメモリが必要です。 アプリケーションのメモリ消費量に注意を払わないと、最終的にクラウドサーバーが過負荷になることがあります。



この記事では、消費されるメモリの量を制御しながら、Unicornの同時実行性を使用するいくつかの方法を見ていきます。



Ruby 2.0を使用してください!



Ruby 1.9を使用している場合は、2.0へのアップグレードを真剣に検討する必要があります。 理由を理解するには、プロセスの作成に少し対処する必要があります。



プロセス作成とコピーオンライト


子プロセスが作成されるとき、それはまさにその親プロセスのコピーです。 ただし、物理メモリをすぐにコピーする必要はありません。 相互の正確なコピーであるため、子プロセスと親プロセスの両方が同じ物理メモリを使用できます。 記録プロセスが発生した場合にのみ、子プロセスを物理メモリにコピーします。



これらはすべてRuby 1.9 / 2.0とUnicornにどのように関連していますか?


Unicornはフォークを使用していることを思い出します。 理論的には、オペレーティングシステムはCopy-on-Writeを使用できます。 残念ながら、Ruby 1.9はこれを不可能にします。 具体的には、Ruby 1.9でのガベージコレクターの実装により、これが不可能になります。 簡易バージョンでは、このように見えます-1.9のガベージコレクターが起動すると、書き込みが行われ、Copy-on-Writeは役に立たなくなります。



詳細を説明することなく、Ruby 2.0のガベージコレクターがこれを排除し、Copy-on-Writeを使用できると言うだけで十分です。



Unicornを構成する



Unicornを最大限に活用するために、config / unicorn.rbで設定できる設定をいくつか示します。

worker_processes

実行中のプロセスの数を指定します。 プロセスが使用するメモリ量を知ることが重要です。 これは、VPSのRAMに過負荷をかけることなく、必要な数のワーカーを実行できるようにするために必要です。

タイムアウト

小さい数値を指定する必要があります。通常は15〜30秒が適切です。 時間のかかるリクエストが他のリクエストの処理を遅らせないように、比較的小さな値が設定されます。

preload_app

trueに設定する必要があります-これにより、ワーカーの起動時間が短縮されます。 Cope-on-Writeのおかげで、残りのワーカーが起動する前にアプリケーションがロードされます。 ただし、重要な注意事項があります。 すべてのソケット(データベース接続を含む)が正しく閉じられ、再度開かれることを確認する必要があります。 before_forkとafter_forkを使用してこれを行います。

例:

before_fork do |server, worker| # Disconnect since the database connection will not carry over if defined? ActiveRecord::Base ActiveRecord::Base.connection.disconnect! end if defined?(Resque) Resque.redis.quit Rails.logger.info('Disconnected from Redis') end end after_fork do |server, worker| # Start up the database connection again in the worker if defined?(ActiveRecord::Base) ActiveRecord::Base.establish_connection end if defined?(Resque) Resque.redis = ENV['REDIS_URI'] Rails.logger.info('Connected to Redis') end end
      
      





この例では、ワーカーを作成するときに接続が閉じられ、再び開かれることを確認します。 データベースへの接続に加えて、ソケットの操作を必要とする他の接続も同じ方法で処理されるようにする必要があります。 上記はResqueの構成です。



ユニコーンワーカーのメモリ制限



明らかに、虹だけでなくユニコーンも周囲にあります。 (著者のしゃれた「虹とユニコーン」-約翻訳者がいました)。 Railsアプリケーションにメモリリークがある場合、Unicornは事態を悪化させます。



作成された各プロセスはメモリを占有します。 railsアプリケーションのコピーです。 したがって、より多くのワーカーが存在するということは、アプリケーションがより多くの着信要求を処理できることを意味しますが、システムの物理RAMによって制限されます。



Railsアプリケーションでのメモリリークは非常に簡単です。 ただし、すべてのメモリリークを「プラグイン」できたとしても、まだ不完全なガベージコレクタ(MRIでの実装を意味する)に対処する必要があります。



上の画像は、Unicornによって起動されたメモリリークレールアプリケーションを示しています。



時間が経つにつれて、メモリ消費量は増え続けます。 多くのワーカーを使用しても、空きメモリがなくなるまでメモリ消費の速度が加速するだけです。 アプリケーションがクラッシュし、多くの不幸なユーザーや顧客につながります。



これはユニコーンのせいではないことに注意することが重要です。 ただし、これは遅かれ早かれ遭遇する問題です。



Unicorn Worker Killerに会う



私が遭遇した最も簡単な解決策の1つは、 ユニコーンワーカーキラー宝石です。

READMEからの引用:

ユニコーンワーカーキラーgemを使用すると、以下に基づいてUnicornワーカーを自動的に再起動できます。

1)リクエストの最大数

2)要求を処理しないプロセス(RSS)が占有するメモリのサイズ。

これにより、サイトの安定性が大幅に向上し、アプリケーションノードの予期しないメモリ不足が回避されます。



Unicornが既にインストールされ、実行されていると仮定していることに注意してください。

ステップ1:

ユニコーンよりも低いGemfileにユニコーンワーカーキラーを追加します。

 group :production do gem 'unicorn' gem 'unicorn-worker-killer' end
      
      





ステップ2:

バンドルインストールを実行します。

ステップ3:

次に、楽しい部分が始まります。 config.ruファイルを開きます。

 # --- Start of unicorn worker killer code --- if ENV['RAILS_ENV'] == 'production' require 'unicorn/worker_killer' max_request_min = 500 max_request_max = 600 # Max requests per worker use Unicorn::WorkerKiller::MaxRequests, max_request_min, max_request_max oom_min = (240) * (1024**2) oom_max = (260) * (1024**2) # Max memory size (RSS) per worker use Unicorn::WorkerKiller::Oom, oom_min, oom_max end # --- End of unicorn worker killer code --- require ::File.expand_path('../config/environment', __FILE__) run YourApp::Application
      
      





最初に、実稼働環境にいることを確認します。 その場合、残りのコードを実行します。

unicorn-worker-killerは、リクエストの最大数と消費される最大メモリの2つの条件に基づいてワーカーを殺します。



各アプリケーションには、独自のメモリ要件があります。 通常の操作中のアプリケーションのメモリ消費量の一般的な見積もりが必要です。 これにより、ワーカーが占有するメモリの最小量と最大量をより適切に推定できます。



アプリケーションのデプロイ中にすべてを正しく構成した場合、不安定なメモリの動作がはるかに少なくなります。



グラフの過剰に注意してください-この宝石はその仕事をします!



おわりに



ユニコーンは、スレッドアプリケーションであるかどうかに関係なく、レールアプリケーションに並行性を実現するための簡単な方法を提供します。 ただし、これはRAM消費の増加とともに達成されます。 メモリ消費のバランスは、アプリケーションの安定性とパフォーマンスにとって非常に重要です。

最大のパフォーマンスを得るためにUnicornワーカーを調整する3つの方法を検討しました。

  1. Ruby 2.0を使用すると、ガベージコレクターが改善され、コピーオンライトを利用できるようになります。
  2. config / unicorn.rbでさまざまな構成オプションを設定します。
  3. ユニコーンワーカーキラーを使用して、肥大化した労働者を止める問題を解決します。




資源






All Articles