nginx + unicorn + Railsの設定方法


@aki1986です。
何回かに分けて「nginx + unicorn + rails」の連携方法を記載していこうと思います。
今回はnginx連携（最後）まで！

## 背景
監視サイトのダッシュボードを構築することとなり、どうせならrailsでどうせならnginx上で動かすかと思い立ち構築してみた。
今回は前回書いた[Railsインストール](http://qiita.com/akito1986/items/93fb15e7a87953fdd17f "Railsインストール")以降の設定方法について書きます。

nginxのインストール方法はここでは書きません。

環境は以下。

* ubuntu: 12.04
* ruby: 2.1.2p95
* Rails: 4.1.4
* nginx: nginx/1.1.19
* unicorn: 4.8.3

## Unicorn設定
Railsとnginxはunicorn経由でやり取りさせる。
以下のイメージ。

```
nginx <-> unicorn <-> Rails
```

### Unicornインストール
インストールと言ってもRailsアプリ`${my_app}`のGemfileに以下を追記して、`$ bundle install`するだけ。

```conf:${my_app}/Gemfile
gem 'unicorn', '4.8.3'
```

### 設定ファイル編集
`${my_app}/config`配下に以下の内容のunicornの設定ファイルを作成しておく。

```conf:${my_app}/config/unicorn.rb
# -*- coding: utf-8 -*-
# ワーカーの数
worker_processes 2
 
# ソケット
listen  '/tmp/unicorn.sock'
pid     '/tmp/unicorn.pid'
 
# ログ
log = '${my_app}/log/unicorn.log'
stderr_path File.expand_path('log/unicorn.log', ENV['RAILS_ROOT'])
stdout_path File.expand_path('log/unicorn.log', ENV['RAILS_ROOT'])
 
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"
unless old_pid == server.pid
  begin
   sig = (worker.nr + 1) >= server.worker_processes ? :QUIT : :TTOU
   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
```
`listen pid`のpathはご自身の環境に即したpathを指定してください。
また`ENV['RAILS_ROOT']`で環境変数としてRailsのアプリケーションディレクトリを指定していますが、環境変数で指定するほどでもない場合は`s/ENV['RAILS_ROOT']/${my_app}/g`として直書きでもよいかと思います。

### 起動スクリプト作成
以下のコマンドでunicornを起動することが出来るのですが、

```cmd:exec_unicorn
$ bundle exec unicorn -D -c $APP_ROOT/config/unicorn.rb -E $RAILS_ENV
```

いちいち手動で実行するのは面倒なので起動スクリプトを作成しました！・・・私が書いたわけではなく他のサイトから引っ張ってきたものですが。。。

```script:unicorn_script
#!/bin/bash

set -e
 
TIMEOUT=${TIMEOUT-60}
APP_ROOT=${my_app}
PID=/tmp/unicorn.pid
#RAILS_ENV=production
RAILS_ENV=development
CMD="bundle exec unicorn -D -c $APP_ROOT/config/unicorn.rb -E $RAILS_ENV"
action="$1"
set -u
 
old_pid="$PID.oldbin"
 
cd $APP_ROOT || exit 1
 
sig () {
    test -s "$PID" && kill -$1 `cat $PID`
}
 
oldsig () {
    test -s $old_pid && kill -$1 `cat $old_pid`
}
 
case $action in
start)
    sig 0 && echo >&2 "Already running" && exit 0
    $CMD
    ;;
stop)
    sig QUIT && rm -f ${PID} && exit 0
    echo >&2 "Not running"
    ;;
force-stop)
    sig TERM && exit 0
    echo >&2 "Not running"
    ;;
restart|reload)
    sig HUP && echo reloaded OK && exit 0
    echo >&2 "Couldn't reload, starting '$CMD' instead"
    $CMD
    ;;
upgrade)
    if sig USR2 && sleep 2 && sig 0 && oldsig QUIT
    then
        n=$TIMEOUT
        while test -s $old_pid && test $n -ge 0
        do
            printf '.' && sleep 1 && n=$(( $n - 1 ))
        done
        echo
 
        if test $n -lt 0 && test -s $old_pid
        then
            echo >&2 "$old_pid still exists after $TIMEOUT seconds"
            exit 1
        fi
        exit 0
    fi
    echo >&2 "Couldn't upgrade, starting '$CMD' instead"
    $CMD
    ;;
reopen-logs)
    sig USR1
    ;;
*)
    echo >&2 "Usage: $0 <start|stop|restart|upgrade|force-stop|reopen-logs>"
    exit 1
    ;;
esac
```
上記で`PID=/tmp/unicorn.pid`についてですが、これは`${my_app}/config/unicorn.rb`の`pid`で指定したpathと同じにする必要があるので注意してください。

### unicorn起動/停止
以下でunicornの起動/停止が出来るようになります。

```cmd:exec_script
$ cd ${my_app}
$ ./script/unicorn.sh start # 起動
$ ./script/unicorn.sh stop # 停止
```

### サブディレクトリ対応
Railsを`http://localhost/${app_path}`のようにサブディレクトリでRailsにアクセスさせたい場合は`${my_app}/config.ru`にさらに以下の設定が必要です。

```conf:${my_app}/config.ru
# This file is used by Rack-based servers to start the application.
RAILS_RELATIVE_URL_ROOT="/${app_path}"
require ::File.expand_path('../config/environment',  __FILE__)
if RAILS_RELATIVE_URL_ROOT then
        map RAILS_RELATIVE_URL_ROOT do
                run Rails.application
        end
else
        run Rails.application
end
```

## nginx設定
### site-availableファイルの作成
以下のようなファイルを`${nginx}/site-available/`配下に作成します。
`default`ファイルをコピーして作成しているのでいらない記述があるかも。

```conf:${nginx}/site-available/rails_app
upstream rails-unicorn {
	server unix:/tmp/unicorn.sock;
}

server {
	#listen   80; ## listen for ipv4; this line is default and implied
	#listen   [::]:80 default ipv6only=on; ## listen for ipv6

#	root /usr/share/nginx/www;
	index index.php index.html index.htm;

	# Make site accessible from http://localhost/
	server_name localhost;

	location /${app_path}/ {
		try_files $uri $uri.html $uri/index.html @rails-unicorn;
	}
	location ~ ^/assets/(.*) {
		alias ${my_app}/public/assets/$1;
	}

	location @rails-unicorn {
               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://rails-unicorn;
	}
 
	# Only for nginx-naxsi : process denied requests
	#location /RequestDenied {
		# For example, return an error code
		#return 418;
	#}

	error_page 404 /404.html;

	# redirect server error pages to the static page /50x.html
	#
	error_page 500 502 503 504 /50x.html;
	location = /50x.html {
		root /usr/share/nginx/www;
	}

}

```
`upstream rails-unicorn {}`でunicornのソケットファイルを指定しています。

また`location ~ ^/assets/(.*) {}`では、cssファイル、jsファイルの検索パスを指定しています。
Railsのデフォルトのdevelopment環境ではこの指定は不要なのですが、production環境では速度的な観点でデフォルトで`${my_app}/config/environments/production.rb`の中で`config.assets.compile = false`を指定しているため動的にcssファイル、jsファイルを作成しないようになっています。
そのためRailsアプリケーションでロジックが必要なものは、`nginx <-> unicorn <-> rails`という流れで、css、jsファイルの読み込みは`nginx <-> css, jsファイル`という流れとなっています。(多分。。。)

### シンボリックリンクの作成
このままではnginx起動時に作成した`${nginx}/site-available/rails_app`が読み込まれないので、以下のコマンドでシンボリックリンクを作成します。

```cmd:symbolic_link
$ ln -s ${nginx}/sites-available/rails_app ${nginx}/sites-enabled/rails_app
```

## Railsの設定
### Secretキーの設定
production環境でrailsを動かすためには、secretキーを設定する必要があります。
まずは以下のコマンドでsecretキーを作成します。

```cmd:create_secret_key
$ rake secret
xxxxba764fba527f6f6fa99764bb0ff8abe1edf637d0950597c11dacaea5c7f9f5903199811c86d56847086d4206f9987a3f7197651ff7e25b7c0039da75xxxx
```

通常は環境変数に設定するようですが、ここでは`${my_app}/config/secrets.yml`を以下のように直接編集します。

```conf:${my_app}/config/secrets.yml
# Do not keep production secrets in the repository,
# instead read values from the environment.
production:
  secret_key_base: xxxxba764fba527f6f6fa99764bb0ff8abe1edf637d0950597c11dacaea5c7f9f5903199811c86d56847086d4206f9987a3f7197651ff7e25b7c0039da75xxxx
```

### assetsファイルの作成
cssファイルやjsファイルをrails起動前にコンパイルして配置しておきます。

```cmd:create_assets
$ bundle exec rake assets:precompile
I, [2014-08-13T14:24:42.887465 #11940]  INFO -- : Writing ${my_app}/application-xxxxxxxxxxxxxx.js
I, [2014-08-13T14:24:42.887465 #11940]  INFO -- : Writing ${my_app}/application-xxxxxxxxxxxxxx.css
```

## 動作確認

さていよいよ動作確認。
以下のコマンドでunicornとnginxを起動します。

```cmd:start_service
$ cd ${my_app}
$ ./script/unicorn.sh start
$ sudo service nginx start
```

今回は`http://localhost/${app_path}`でアクセスできるはずなのでローカルのブラウザでアクセスしてみます・・・正常に設定ができていれば以下のように表示されるはずです！
ここでは`${app_path}`に`hogehoge`を指定しています。

![img_nginx_access.png](https://qiita-image-store.s3.amazonaws.com/0/46472/72079b13-b3cd-eea0-d1a8-f441775ec1f4.png "img_nginx_access.png")

おしまい。