«前の日記(2012-05-03 (木)) 最新 次の日記(2012-05-05 (土))»  

まちゅダイアリー


2012-05-04 (金)

tDiaryを Nginx + Unicorn + さくらVPSで動かした (2)

先日の日記の続き。Nginx + Unicorn で tDiary を動かしてみたものの、フィード (index.rdf) が表示されない問題があった。なので、今回は静的コンテンツをRack環境でどう扱うかという話。

現状のおさらい

  • tDiary では日記の更新時にプラグイン (makerss.rb) がフィードを書き出している
  • tDiary を CGI/FastCGI で動作させたときには Web サーバがフィードを返している
  • tDiary を Rack 環境で動かしたときは、リクエストはすべて AP サーバに飛ばす設定になっている
    • JavaScriptやCSSは tDiary 側で assets という仕組みを使って返すが、フィードは対象外

対応方針

  1. Webサーバ (nginx) がフィードを返す
  2. APサーバ (unicorn) でフィードを返す

案1はCGI/FastCGIと同じ方式。APサーバを介さないことで、速度的には有利になる*1。案2は APサーバ単体で動作させられるのがメリット。

前提: フィードのファイルをどこに書き出すか

CGI/FastCGIで動かす場合はtDiaryのルートディレクトリに書き出しているけど、これはtDiaryのルートディレクトリとWebサーバの公開ディレクトリが一致しているから。つまり http://example.com/diary/index.rb へのアクセスは /DocumentRoot/diary/index.rb にマッピングされるし、 http://example.com/diary/index.rdf へのアクセスは /DocumentRoot/diary/index.rdf へマッピングされる。

一方、 Rack 経由で動かす場合は、tDiaryのルートディレクトリとWebサーバの公開ディレクトリは一致しない*2。そこで、Rails系のお作法と同じように、 public ディレクトリを静的コンテンツの置き場所とする。なお、JavaScriptやCSSなどは前述の通り assets によって tDiary がよしなにしてくれる。

makerssプラグインの説明に従って、フィードを public ディレクトリに書き出すように設定する。

@options['makerss.file'] = 'public/'index.rdf'
@options['makerss.no_comments.file'] = 'public/no_comments.rdf'

案1. Webサーバ (nginx) がフィードを返す

見直し前の Nginx 設定ファイルはこれ(一部抜粋)。

        server_name www.machu.jp;

        location ~ ^/diary/ {
                rewrite ([0-9\-]+)\.html$ /diary/index.rb?date=$1 last;
                proxy_pass http://www.machu.jp;
        }

これだと /diary/ 以下のアクセスがすべて Unicorn 側へ飛んでしまう。
代わりに、まずは public/ ディレクトリにファイルが存在するかどうかをチェックし、存在しない場合のみに Unicorn 側へ飛ばすように設定する。
ポイントは try_files $uri @unicorn の設定。 $uri に相当するファイルがなければ、 @unicorn の設定へ飛ぶ。

        location ~ ^/diary/ {
                try_files $uri @unicorn;
        }

        location @unicorn {
                rewrite /diary/([0-9\-]+)\.html$ /diary/index.rb?date=$1 last;
                proxy_pass http://www.machu.jp;
        }

あとは、 tdiary-core/public ディレクトリを Nginx 側の DocumentRoot/diary にリンクを張っておけばよい。

案2. 2. APサーバ (unicorn) でフィードを返す

/diary/index.rdf へのリクエストが来た場合に、 tdiary-core/public/index.rdf を返すように config.ru を修正した。Rack::CascadeとRack::Fileの組み合わせがポイント。

map "#{base_dir}/" do
    run Rack::Cascade.new([
        Rack::File.new("./public/"),
        TDiary::Application.new(:index)
    ])
end

pull requestを送ったので、最新のGitリポジトリには修正が取り込まれている。

まとめ

  • 静的ファイルは public フォルダに置くのが原則
  • Webサーバ側 (Nginx) でフィードを返す場合は try_files を使う
  • APサーバ側 (Unicorn) でフィードを返す場合は Rack::CascadeとRack::Fileを使う

なお、 Heroku などの PaaS 上でフィードを扱う場合には、そもそも静的コンテンツとしてフィードを生成しているところから見直す必要がある。

*1 とはいえ、全体のアクセスに対するフィードの割合は少ないので、トータルの性能面ではそれほど変わらないかも

*2 一致するように設定してもいいけど、Webサーバ側の設定が面倒なのでやめた方が良い