nginx+Unicornでサブディレクトリでアプリを動かす

Passengerだと簡単だったけどUnicornだとちょっと手こずった。

nginx側

ディレクトリの準備

nginxのroot以下に任意の名前のサブディレクトリを作る。
これはRalisアプリケーションのpublicディレクトリのシンボリックリンクにする。

cd /var/www/root
ln -s /var/www/my-app/current/public my-app

(Capistranoを使っているのでこの例ではcurrentがついている)

nginxの設定

nginxのupstreamとserverの設定を抜粋。

upstream unicorn-of-my-app {
  server www.example.com:8080;
}

server {
  listen 80;
  server_name www.exemple.com
  root /var/www/root;

  error_log /var/www/my-app/current/log/error.log;

  location /my-app {
    try_files $uri $uri.html $uri/index.html @unicorn-of-my-app;
  }

  location @unicorn-of-my-app {
    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-of-my-app;
  }
}

location /my-app の部分は静的ファイルがあればnginxが返すようにtry_files。
ファイルがなければリクエストをunicorn-of-my-appに投げる。

config.ruの修正

以下のように修正する。
Unicornが/my-appの部分もパスとして受け取るので、その辺りを考慮してルーティングしてやる。
ifで分岐させたのは開発時にはサブディレクトリで開発しないだろうから。

if ENV['RAILS_RELATIVE_URL_ROOT']
  map ENV['RAILS_RELATIVE_URL_ROOT'] do
    run MyApp::Application
  end
else
  run MyApp::Application
end

またはconfig.ruをすっぱり削除してもいい。
config.ruがない場合はUnicornがよきにはからってくれる。

config.ruがある場合はconfig.ruの内容をそのまま使うので、上記のように自前で面倒を見ないといけない。
わざとそうしてるのか、単に気が利かないだけなのかちょっと意図が不明。
気が利かないだけの方なら将来的にはこの手順はいらなくなるかも。

Unicornの起動

bundle exec unicorn_rails -E production -D --path /my-app

–pathで指定したパスがさっきのconfig.ruで使ったENV[‘RAILS_RELATIVE_URL_ROOT’]に入る。

さて、これでサブディレクトリでRailsアプリが有効になったはず。

その他

  • sub directoryではなくsub uriの方が検索性がよかった
  • config.action_controller.relative_url_rootを使う方法はDeprecated?
  • config.ruとかいじらなくてもnginxのrewriteだけで一瞬うまくいくかと思ったが、link_toやurl_forが全滅していた
  • nginx+Passengerでもいいならそっち使った方がかなり楽

しかしこの件について情報がなさすぎだったんだが、あえて言うほどでもない基本的な見落としがあるのだろうか。