CentOS5.6 で MySQL+Nginx+Unicorn な Rails アプリを動かす

完全に自分用の備忘録。VirtualBox にインストールした CentOS5.6 に表題の環境を構築してサーバを動かすまで。
サーバのメモリは512Mでパーティションは40Gと、(記事執筆時では)一般的な低価格帯 VPS を意識したスペックに調整してあります。
また、すべての作業は su で root ユーザに昇格した上で行う前提になっているので sudo を使う場合適宜置き換えて下さい。

必要なパッケージのインストール

今後の作業に使うパッケージを yum からインストールしておきます。

yum install gcc gcc-c++ openssl* readline* ncurses* zlib* libxml* libjpeg* libpng* libxslt* libtool*

MySQL のインストール

これも yum からインストールします。

yum install mysql-server mysql-devel

mysql-devel を入れておかないと Ruby から MySQL を使う際にうまくいかないようです。インストールが完了したら my.cnf を修正します。
/usr/local/mysql/share/mysql 以下にサンプルが入っているので、好きなものを /etc/my.cnf としてコピーします。細かい設定については、今回は省略します。

次に MySQL を chkconfig の自動起動一覧に追加しておきます。

chkconfig mysqld on

これで、サーバの起動時に MySQL が(設定をミスしていなければ)自動起動するようになりました。

ここで MySQL の起動が正しく完了するか確認します。

/ect/rc.d/init.d/mysqld start

うまく起動すれば問題ありませんが、エラーになってしまう場合もあると思います。そのときは /var/log/mysqld.log にエラーの詳細が記述されているので、参考にして修正します(私の場合、データベースエンジンに innodb を指定したのですが、そのデータディレクトリが mysql ユーザ向けに作成されていなかったためエラーになりました)。

サービスの起動が完了したら、ルートユーザのパスワードを設定します。

/usr/bin/mysqladmin -u root password 'new-password'
/usr/bin/mysqladmin -u root -h localhost password 'new-password' -p

new-password を好きなパスワードに書き換えて下さい。

設定した情報で MySQL を利用できるか確認してみます。

mysql -u root -p

設定がうまくいっていれば、パスワードの確認後に対話型インターフェースが立ち上がるはずです。

ImageMagick のインストール

Rails アプリから画像を扱いたいので ImageMagick をインストールします。
最新版をインストールしたいので、ソースをコンパイルします。ImageMagick のサイトから最新版の tgz をダウンロードして、インストールコマンドを実行します。

./configure --without-fpx
make
make install

私の環境では configure のオプションに --without-fpx をつけないとどうしてもインストールが成功しませんでした。

Python のインストール

easy_install に含まれる Pygments ライブラリを使いたいので Python をインストールします。

yum install python*

自動的に easy_install も使えるようになるのでインストールコマンドを実行。

easy_install pygments

Ruby のインストール

これも最新版を用いるので、ソースからインストール。特にオプションも必要なく make するだけで完了です。

RubyGems でライブラリを管理したいので、それもインストールします。ソースをダウンロードして展開したディレクトリ内に setup.rb というファイルが入っているので、実行します。

ruby setup.rb

Rails のインストール

Rails アプリの中核になる Ruby on Rails の gem をインストールします。

gem install rails

インストールが完了したら、動かす Rails アプリのルートディレクトリに移動して、他の使用する gem をインストールします。

bundle install

このとき JavaScript のランタイムが見つからないとエラーが発生する事がありますが、その場合 Gemfile に以下の行を追加してください。

gem install 'therubyracer'

その後、再度 bundle install を実行します。

ここまで完了したら config/database.yml を環境にあわせて修正した後で、データベースを初期化します。

bundle exec rake db:create:all
RAILS_ENV=production bundle exec rake db:migrate
RAILS_ENV=production bundle exec rake db:seed

また Rails 3.1 系のアセットパイプラインを使っている場合 production 環境向けにプリコンパイルしておきます。

bundle exec rake assets:precompile

Unicorn の動作確認

Unicorn のコンフィグファイルを作成し config/unicorn.rb などの名前をつけて保存します。ファイルのフォーマットは公式サイトを確認してください。

http://unicorn.bogomips.org/
http://unicorn.bogomips.org/examples/unicorn.conf.rb

参考までに、私の環境で使った設定はこんな感じ。

# coding:utf-8
# unicron.rb
worker_processes  4
working_directory '/path/to/rails/app/'

listen '/tmp/unicorn.sock', :backlog => 1
listen 4423, :tcp_nopush => true

pid '/tmp/unicorn.pid'

timeout 10

stdout_path '/path/to/rails/app/log/unicorn.stdout.log'
stderr_path '/path/to/rails/app/log/unicorn.stderr.log'

preload_app  true
GC.respond_to?(:copy_on_write_friendly=) and GC.copy_on_write_friendly = true

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
      sig = (worker.nr + 1) >= server.worker_processes ? :QUIT : :TTOU
      Process.kill(sig, File.read(old_pid).to_i)
    rescue Errno::ENOENT, Errno::ESRCH
    end
  end

  sleep 1
end

after_fork do |server, worker|
  defined?(ActiveRecord::Base) and ActiveRecord::Base.establish_connection
end

注意すべき点は working_directory を Rails アプリのドキュメントルートに変更することと listen するソケットの位置を自分の環境にあわせて変更する事と stdout_path, stderr_path を適当なディレクトリ(Rails.root/log 以下等)に書き換える事。後はサンプルがあちこちに載っているので、それらを参照すれば問題ないはずです。

次に production モード動作時の設定を変更します。config/environments/production.rb を編集します。

config.assets.compile = true # アセットパイプラインの設定
config.servre_static_assets = true # フロントは Nginx にするので、動作を確認したら false に戻す

ここまで完了したら Unicorn が動作するか確認します。

unicorn_rails -c config/unicorn.rb -E production -D

ブラウザを開いて、対象のサーバの IP にポート4423番でアクセスすると、アプリケーションの動作を確認できるはずです(できない時は /path/to/rails/app/log/production.log を見るとエラー内容が確認できます)。

bundle コマンドで unicorn をインストールしていると、ここで unicorn_rails コマンドが使えない可能性があります。その場合は gem install unicorn を改めて実行します。
コマンドが通ればサーバが動いているはずなので、適宜アクセスして確認して下さい。確認が終わったら、次のようにプロセスを探して停止します。

pgrep -f 'unicorn_rails master'
kill -QUIT PID

Nginx インストール

インストールの前にユーザを追加します。

useradd nginx

また PCRE を使うので、それもインストールしておきましょう。

yum install pcre*

Nginx 本体はソースからインストールします。

./configure --prefix=/usr/local/nginx --user=nginx --group=nginx
make
make install

インストールが完了したら prefix で指定したディレクトリの所有権を nginx ユーザに変更。

chown -R nginx:nginx /usr/local/nginx

ここで一旦 Nginx の起動テストを行います。Unicorn を80番ポートで動かしている場合、事前に停止してください。

/usr/local/nginx/sbin/nginx -c /usr/local/nginx/conf/nginx.conf

この状態で対象マシンの IP を指定してブラウザからアクセスすると Nginx のデフォルト動作画面が表示されるはずです。確認が完了したら、サーバを一旦停止します。

/usr/local/nginx/sbin/nginx -c /usr/local/nginx/conf/nginx.conf -s stop

Nginx はソースからインストールしたので、起動時スクリプトへの追加は手動で行います。公式サイトからスクリプトのサンプルを拾ってきて、必要な箇所を書き換えて /etc/init.d/nginx に保存します。

http://wiki.nginx.org/RedHatNginxInitScript

対象のファイルに実行権限を付与して、起動時スクリプトの一覧に追加、起動時スクリプトをオンにします。

chmod +x /etc/init.d/nginx
chkconfig --add nginx
chkconfig nginx on

最後に Nginx のコンフィグファイルを設定して Unicorn と連携させます。Unicorn は80番以外のポートで起動させて UNIX ソケットで通信します。

/usr/local/nginx/conf/nginx.conf を開いて http ブロック内に upstream の設定を追記します。サーバには先ほど設定した Unicorn のソケットを指定しました。

upstream unicorn {
  server unix:/tmp/unicorn.sock;
}

さらに server ブロック内にドキュメントルートの設定を追加します。ドキュメントルートは Rails アプリ内の public ディレクトリを指定して下さい。これにより、静的ファイルが Rails のルーティングを介さずに読み出せるようになります。

root /path/to/rails/root/public;

そして、上で設定した upstream へのプロキシ設定を行うため location ブロックを書き換えます。

location {
  proxy_set_header X-Real-IP $remote_addr;
  proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
  proxy_set_header Host $http_host;
  proxy_pass http://unicorn;
}

Rails3.1 を使っている場合 /assets 以下の静的ファイルを処理するよう記述します。(参考サイト

  location  ~* ^/assets {
    expires max;
    add_header Cache-Control public;
    break;
  }

この状態で Nginx を再起動すれば、80番ポートから Rails アプリの動作を確認できるはずです。お疲れさまでした。