はじめに
Railsアプリを本番環境で稼働させるには、クライアントからのリクエストを捌くWebサーバーを導入する必要があります。WebサーバーはクライアントからのリクエストをRailsアプリに伝達し、Railsアプリで処理されたレスポンスをクライアントに返すという役割を担います。
本記事では、Webサーバー「Unicorn」の使い方についてまとめています。
Unicornの基本
Unicornについて
UnicornとはRackアプリケーション用のWebサーバーです。
開発者のみがリクエストを送信する開発環境と違い、本番環境ではいつ、どれくらいのリクエストが送信されてくるかはわかりません。24時間365日、間断なくリクエストが送信されてくるかもしれませんし、複数のリクエストが同時発生的に送信されてくる可能性もあります。
Unicornは送信されてきた多数のリクエストを捌き、分散してアプリケーションに伝達するという機能を持ちます。リクエストをアプリケーションへ伝達するとアプリケーションからレスポンスが戻ってくるまで待機し、最終的にリクエストに対するレスポンスをクライアントに返します。
この画像はクライアントからRailsアプリへリクエストを送信したときの概念図です。リモートサーバーにあるUnicornがクライアントからのリクエストを受け取り、アプリケーションに伝達していることがわかります(レスポンスシーケンスは省略しています)。
この概念図を見て、「Rackってなに? Railsはどこ?」「なんでWebサーバーがふたつあるの?」など疑問があるかと思いますので、詳しく解説していきます。
Rackってなに? Railsはどこ?
Rackとは、WebサーバーとRubyで作られたWebフレームワークを繋ぐためのAPIを提供するアプリケーションサーバーです。「Rubyで作られたWebフレームワーク」とはもちろんRuby on Railsのことですが、他にもSinatraというWebフレームワークがあります。
Rackというアプリケーションサーバーという土台があり、その上にRuby on RailsやSinatraが乗っていると思ってください。概念的にはRack = Ruby on Railsと思っていただいて構いません。
Webサーバーには本記事の主役であるUnicorn以外にも、PumaやThin、WEBrickといったたくさんの種類があります。これらのWebサーバーとWebフレームワーク(Ruby on Rails/Sinatra)が、互いを意識することなく柔軟に通信できるようにしているのがRackというわけです。
なんでWebサーバーがふたつあるの?
Webサーバーがクライアントからのリクエストを捌く機能を持っていることは理解していただけたかと思います。では、上の画像にはなぜUnicornとNginxというふたつのWebサーバーが存在しているのでしょうか。UnicornあるいはNginxのどちらかだけではだめなのでしょうか。
まず、Rackは一般的なWebサーバー(Nginx/Apache)と繋ぐことを想定して作られていません。上の画像において、NginxとRackの間の「×」は互いに接続できないことを示しています。そのため、Rackと繋ぐことができる専用のWebサーバーが必要となります。
「Rackってなに? Railsはどこ?」セクションで、Webサーバーにはたくさんの種類があるということを書きましたが、このWebサーバーとは「Rack専用のWebサーバー」を意味します。ですので、UnicornもPumaもThinもWEBrickもすべて「Rack専用のWebサーバー」です。
しかし、Rack専用のWebサーバーはブロッキングI/Oモデル1で実行されているため、クライアントからのリクエストを非同期で処理することができません。通信状態が不安定なクライアントがUnicornのプロセスを占有している場合、その間は他のクライアントからのリクエストを処理することができなくなってしまいます。
こうしたUnicornなどのRack専用のWebサーバーの弱点を補うのがNginxです。Nginxは低速クライアントをバッファリングします。NginxとUnicornの間の接続は非常に高速なため、Unicornが低速なクライアントのリクエストを受信することがなくなります。
このようにUnicornとNginxは互いの弱点を補間する関係にあるので、どちらのWebサーバーも必要になるというわけです。
- I/O処理時に対象が準備完了になるまで待機し続けること。
Unicornの動作
Unicornにはマスターとワーカーという概念があります。マスターはアプリケーションのインスタンスを持つ唯一のプロセスで、複数のワーカープロセスを生成し、所有します。マスタープロセスがワーカープロセスを生成することをフォークと呼びます。
Unicornを起動するとマスタープロセスが生成され、マスタープロセスはアプリケーションの読み込みと複数のワーカープロセスの生成を行います。ワーカープロセスは一定時間が経つとマスタープロセスによって破棄され、また新たなワーカープロセスが生成されます。このワーカープロセスのライフサイクルはUnicornを停止するまで繰り返されます。
UnicornがNginx(概念的にはクライアント)からリクエストを受信すると、マスタープロセスはワーカープロセスにリクエストを処理するよう命令します。
Unicornの使い方
Unicornのインストール
Unicornをインストールするには、Gemfile
に以下を追記してbundle install
を実行します。
Gemfile
group :production do
# 以下を追記
gem 'unicorn'
end
Unicornの設定
Unicornを起動する際に必要となる設定ファイルを作成します。作成するディレクトリとファイル名はなんでもいいのですが、config/
ディレクトリ配下にunicorn.rb
というファイル名で作成するのが一般的です。
設定ファイルは大別して「変数の設定」と「フォーク前後の処理」に分けられます。ここではそれらを分けて説明していますが、実際にはひとつの設定ファイルにまとめて書いてください。
変数の設定
変数の設定は以下の通りです。
unicorn.rb
# Railsアプリのルート
rails_root = File.expand_path('../../', __FILE__)
# Gemfileの場所
ENV['BUNDLE_GEMFILE'] = rails_root + "/Gemfile"
# Unicornの設定
worker_processes 2
timeout 15
working_directory rails_root
pid File.expand_path 'tmp/pids/unicorn.pid', rails_root
listen File.expand_path 'tmp/sockets/.unicorn.sock', rails_root
stdout_path File.expand_path 'log/unicorn.log', rails_root
stderr_path File.expand_path 'log/unicorn.log', rails_root
preload_app true
CapistranoでUnicornを起動する際、Gemfile
の場所が必要になるので、ここで環境変数に設定しています。
その他の変数の設定は以下の通りです。
設定 | 説明 |
---|---|
worker_processes |
同時起動するワーカーの最大数。ワーカーが多ければ多いほどメモリ消費も激しいので、サーバーのスペックとアプリケーションのアクセス数によって決定する。 |
timeout |
ワーカーが処理を開始し終了するまでの最大時間(秒数)。設定した時間を超えた場合、そのワーカーは破棄される。 |
working_directory |
Unicornの起動コマンドが実行されるディレクトリ。 |
pid |
UnicornのプロセスIDを保存しておくファイル。 |
listen |
リクエストを受信するポート番号。Nginxとの接続に使用。 |
stdout_path |
Unicornの実行ログを出力するファイル。 |
stderr_path |
Unicornのエラーログを出力するファイル。 |
preload_app |
Unicornの再起動をダウンタイムなしで行う。この設定によりアプリケーションがダウンすることなくデプロイができるようになる(ホットデプロイ)。詳細は後述。 |
フォーク前後の処理
フォーク前後の処理は以下の通りです。
unicorn.rb
# フォークが行われる前の処理
before_fork do |server, worker|
defined?(ActiveRecord::Base) and ActiveRecord::Base.connection.disconnect!
old_pid = "#{server.config[:pid]}.oldbin"
if old_pid != server.pid
begin
Process.kill "QUIT", File.read(old_pid).to_i
rescue Errno::ENOENT, Errno::ESRCH
end
end
end
# フォークが行われた後の処理
after_fork do |server, worker|
defined?(ActiveRecord::Base) and ActiveRecord::Base.establish_connection
end
フォーク前後の処理では、大きく分けて「UnicornとActive Recordの切断/接続」と「マスタープロセスの再起動」のふたつを行っています。まず、「UnicornとActive Recordの切断/接続」の処理を抜き出すと以下になります。
unicorn.rb
# フォークが行われる前の処理
before_fork do |server, worker|
defined?(ActiveRecord::Base) and ActiveRecord::Base.connection.disconnect!
end
# フォークが行われた後の処理
after_fork do |server, worker|
defined?(ActiveRecord::Base) and ActiveRecord::Base.establish_connection
end
フォーク前にActive Recordとの接続を切断し、フォーク後に再び接続しています。これは、マスタープロセスで使用しているActive Recordとの接続をワーカープロセスで使用させないために行っているそうです(明確に説明している情報が見つかりませんでした)。
続いて「マスタープロセスの再起動」の処理を抜き出すと以下になります。
unicorn.rb
# フォークが行われる前の処理
before_fork do |server, worker|
old_pid = "#{server.config[:pid]}.oldbin"
if old_pid != server.pid
begin
Process.kill "QUIT", File.read(old_pid).to_i
rescue Errno::ENOENT, Errno::ESRCH
end
end
end
preload_app
を有効にしていると、ダウンタイムなしでUnicornの再起動を行うことができます(-USR2
オプション指定時)。具体的な処理の流れは以下のようになります。
- 現在のプロセスIDが保存してある
unicorn.pid
をunicorn.pid.oldbin
にコピー - 新しいUnicornのプロセスを起動(新しいプロセスIDを
unicorn.pid
に保存) unicorn.pid.oldbin
を参照して古いプロセスを停止
一時的にふたつのプロセスが起動されることになり、それによってダウンタイムがなくなるという仕組みです。そして、3.を実行しているのが上記の「マスタープロセスの再起動」になります。
Unicornの起動/停止/再起動
Unicornの起動
Unicornを起動するには以下のコマンドを実行します。
$ bundle exec unicorn -c config/unicorn.rb [-E $RAILS_ENV] [-D]
-E
オプションをつけると環境を指定できます。-D
オプションをつけるとデーモンプロセス(常駐プロセス)として起動します。
Unicornの停止
Unicornを停止するには以下のコマンドを実行します。
$ kill -QUIT `cat /path/to/unicorn.pid`
Unicornの再起動
Unicornを再起動するには以下のコマンドを実行します。
# 通常の再起動
$ kill -HUP `cat /path/to/unicorn.pid`
# 緩やかな再起動
$ kill -USR2 `cat /path/to/unicorn.pid`
Capistranoによるホットデプロイを行うには、必ず-USR2
オプションを指定して再起動を行います。なお、アプリのデプロイ時にはUnicornの再起動を行う必要がありますが、-HUP
や-USR2
の再起動では修正が反映されないときがあります。そのときはUnicornの停止と起動を行なってください。
Capistranoとの連携
Capistranoを使ってデプロイの自動化を行っている場合、Unicornの起動または再起動を自動化することができます。Capistranoの使い方については以下の記事を参照してください。
まとめ
Railsアプリを本番環境で稼働させるにはUnicornなどのWebサーバーが必要になります。初めは何をしているかわかりづらい分野ではありますが、本番環境で安定した運用をするためにも頑張ってUnicornを導入してみてください。
本記事を参考にして、Unicornの使い方を覚えていただければと思います。