さくらのVPSでRedmineとかTracをApacheからNginxに切り替える

「さくらのVPS」でっていうのがいいタイトルなのかどうかわからないけど、実際のところはDebina 6で構築する話。ApacheをWebサーバとして公開していたRedmine/Trac/Subversionをある機会でNginxに切り変えることにした。

この記事の段階ではDebian 6+Nginx 1.0.14の内容となっています。(1.2.0からはパッケージが変わっているようです)

Nginxにする動機

正直なところ、個人利用のRedmineやTracでパフォーマンスを気にする必要もあまりなく、これ単独でNginxにする動機はない。背景にはレンタルサーバで公開しているWordPressのblogをNginx+php-fpmを利用する形でさくらのVPS 2Gに移行しようと思っていたことがある。するとPort 80をNginxに明け渡さないといけなくなるため他のサービスも対応が必要になる。

方針

今回は以下の方針でいくことにする

  • RedmineはNginx+passengerで公開
  • Trac/SubversionはApacheをバックエンドにしてNginxをリバースプロキシとして公開

Trac/Subversionはもうreadonlyでほとんど使っていないので本来はRedmine/Gitに移行したいところなのである。必要なものは移行したが開発を辞めてしまったYahoo! WidetやMovableTypeプラグインなどは移行コストと構築コストを考えたときに後者を取ってしまっているのが現状である。今回もさくらのVPS 2Gに構築したときも悩んだがやはり面倒になったのでそのままTrac/Subversionを構築してしまった。パフォーマンスを気にする必要もなく、メンテナンスコストを掛けたくないので別にApacheのままでいいという判断である。

対してRedmineについては現在も利用しているし、Railsはそれほど設定に悩まなそうなので(これは甘い見通しだったが)Nginxに移行してみようと考えた。

Nginxのインストール

なお、ここからの手順はすべてrootで実行している。

Debian squeezeからインストールしたら、Nginx 0.7だった。ちょっと古いと思いunstableから入れようかと思ったがdotdebという便利なリポジトリがあるようなのでこれを使ってみることに。

dotdebリポジトリを追加

/etc/apt/sources.listに以下を追加。

# dotdeb.org
deb http://packages.dotdeb.org stable all
deb-src http://packages.dotdeb.org stable all

GnuPG キーの追加のため以下を実行する。

cd /tmp
wget http://www.dotdeb.org/dotdeb.gpg
cat dotdeb.gpg | apt-key add -

とりあえず更新
aptitude update

nginx-extrasのインストール

dotdebのnginx 1.2.0からpassengerモジュールはnginx-extrasから取り除かれ、代わりにnginx-passerngerというパッケージが公開されています。従って1.2以降でpassengerを使う場合に実行するコマンドは aptitude install nginx-passerngerが正解です。各パッケージごとのモジュール一覧は以下のリンクを参照ください。
https://docs.google.com/a/moolfreet.com/spreadsheet/ccc?key=0AjuNPnOoex7SdG5fUkhfc3BCSjJQbVVrQTg4UGU2YVE#gid=0

nginx-extrasではPassengerに対応してくれている。サイコー。
http://www.dotdeb.org/2011/05/07/rails-user-dotdeb-now-supports-passenger/

というわけでインストールする。

aptitude install nginx-extras

デフォルトだとport 80を使うようになっているのでApacheを動かしているとNginxは開始できないと思われる。

Nginxの設定

細かい調整は今後必要になってくる、とりあえずサーバのバージョン情報を返すのを無効にしておく。

server_tokens off;

Passenger(Ruby On Rails)のために以下のコメントを外しておく。

        ##
        # nginx-passenger config
        ##
        # Uncomment it if you installed nginx-passenger
        ##

        passenger_root /usr;
        passenger_ruby /usr/bin/ruby;

Virtual Hostの設定

Debian/UbuntuでApacheとおなじみの管理方式になっている。nginx.confよりsites-enabled/*がIncludeされており、sites-availableにサイトごとの設定を書いて、sites-enabeldにシンボリックリンクを張る方式である。

a2ensite/a2dissiteと同様のコマンドngxensite/ngxdissiteがインストールされていた。これを使うとsites-availableにファイルを置いておけば有効・無効(シンボリックリンクの生成・削除)は以下のように行える。

# 有効にする
ngxensite (ファイル名)
# 無効にする
ngxdissite (ファイル名)

a2ensiteでは複数のファイルを指定できたが、ngxensiteで複数指定しても最初のファイルしか有効にならなかった。

Apacheの設定変更

Apacheはバックエンドとして動かすことになるのでいくつかの設定変更が必要になってくる。

Portの変更

VirutalHostで公開しているのでそれぞれのPortを変更する。まずListeningのPort変更。

NameVirtualHost *:8081
Listen 8081

mod_rpafのインストール

NginxをリバースプロキシとしてApacheをバックエンドで動作させる場合、Apacheから見て接続してくるクライアントはNginxである。同じサーバで動かすので同じホストからアクセスされることになる。この場合、ログのリモートホストが127.0.0.1で出力されたり、グローバルIPで接続を制限していても127.0.0.1からのアクセスなので素通りになったりして面倒なことになる。

これらを解決するのがmod_rpafである。mod_rpafはリバースプロキシが送ってくるX-Forwarded-Forなどのヘッダを見てリバースプロキシのリクエスト元をリモートホストとして処理してくれる。

mod-rpafもaptでインストールできた。

aptitude install libapache2-mod-rpaf
a2enmod rpaf

デフォルトの設定は以下のようになっていたのでApacheとNginxを同一サーバで動かすならこのままでいい。

<IfModule mod_rpaf.c>
RPAFenable On
RPAFsethostname On
RPAFproxy_ips 127.0.0.1
</IfModule>

X-Forwarded-Forのヘッダの設定などはNginx側で行う必要があるがそれはこのあとに記載する。

サイトの設定

さて、いよいよここからApacheからNginxへの移行のためのサイト設定になる。
まずApacheのVirutalHostのポートを以下のようにして変更しておく。

<VirtualHost *:8081>

次にNginxのサイト設定を行う。説明の都合で一つにまとめると以下のような感じになる。

server {
    root /usr/share/nginx/www;
    index index.html
    server_name example.com;

    location / {
        try_files $uri $uri/ /index.html;
    }

    location /redmine {
        passenger_enabled on;
        passenger_base_uri /redmine;
    }

    location /trac {
        proxy_set_header    X-Forwarded-Host    $host;
        proxy_set_header    X-Forwarded-For     $proxy_add_x_forwarded_for;
        proxy_pass          http://127.0.0.1:8081;
    }

    location /svn {
        proxy_set_header    X-Forwarded-Host    $host;
        proxy_set_header    X-Forwarded-For     $proxy_add_x_forwarded_for;
        proxy_pass          http://127.0.0.1:8081;
    }
    location ~ /\.ht {
       deny all;
    }
}

Trac/Subversionはそれぞれサブディレクトリで公開していたので、それぞれのlocationでproxy_passtとして同じサーバでポート8081で動いているApacheを指定し、X-Forwarded-Forの設定を行なっている。

ドキュメントルートがデフォルト(/usr/share/nginx/www)のままだが実際にはApacheの際のドキュメントルートと同じものを設定している。そのためApacheの.htで始まるファイルを返してしまわないようにアクセスを拒否する設定をいれている。

Redmineもサブディレクトリで公開している。nginx.confでpassenger_rootとpassenger_rubyの設定は有効にしたのでここでは、passenger_enabled、passenger_base_uriの設定を追加している。ちなみにRailsアプリをサブディレクトリで公開するための設定はPassengerのサイトに書いてあった。他にもいろいろな情報があって参考になる。
http://www.modrails.com/documentation/Users%20guide%20Nginx.html#deploying_rails_to_sub_uri

Nginxのログローテーション

Apacheにはrotatelogsという専用モジュールへログファイルをpipeして日付をファイル名につけてローテーションできたがNginxではそのような機能はなく、logrotateを使うのが基本のようである。デフォルトでは以下のようなファイルがインストールされていた。

/var/log/nginx/*.log {
        daily
        missingok
        rotate 52
        compress
        delaycompress
        notifempty
        create 0640 www-data adm
        sharedscripts
        prerotate
                if [ -d /etc/logrotate.d/httpd-prerotate ]; then \
                        run-parts /etc/logrotate.d/httpd-prerotate; \
                fi; \
        endscript
        postrotate
                [ ! -f /var/run/nginx.pid ] || kill -USR1 `cat /var/run/nginx.pid`
        endscript
}

logrotateを調べたところdateextというオプションがあるのでdailyと併用すれば日付の名前をつけてローテーションできるようだ。確かlogrotateにはログを恒久的に残すという仕様はなかったと思うので、rotateの設定で世代を沢山残すようにするか自分でファイルをコピーするかしないといけない。

置き換え

ひと通り設定が終わったので、Apache/Nginxを再起動する。Nginxのデフォルトサイトは無効にしておく。ブラウザでアクセスできれば成功となる。

ngxdissite default
ngxensite example
/etc/init.d/apache restart
/etc/init.d/nginx restart

参考になったもの

このエントリを書いている時点で日本語の本はこれしかないと思われる。最初の情報としてはかなり訳にたつ。

Nginxの設定に書けるDirective一覧。新しいサービスはドラスティックに変わっていくのでまずはオフィシャルな情報をチェックするに限る。
Nginx – Directive Index

RedmineのようなRuby On RailsをApache/Nginxで実行させるPassengerのNginx向けのドキュメント、各種設定について参考になる。
Phusion Passenger users guide, Nginx version

まとめ

細かい設定などまだまだ勉強することは多いが、設定の書き方など結構フレキシブルな感じがある。例えばaccess_logがlocationディレクティブでも書けるので今回のようにサービスごとにサブディレクトリに分けている場合はサービスごとにログファイルを運用することも可能である。(Aapcheでも同様のことはできるがちょっと面倒というか設定に可読性があまりないと思う)

まだまだ何をするにせよApacheからNginxへの翻訳が必要な状況だが使っていくうちに覚えるであろうと信じたい。今回はあくまでWordpressを持ってくるための事前準備なので、次はWordpressの移行に取り組みたい。

試行錯誤集

うまくいった手順だけを書くと簡単に見えてしまうが、ここにたどり着くまでいろいろあった。同じことにハマった人のために紹介しておく。

X-Forwarded-Forの設定は明示的に必要

Apacheにmod_rpafを入れたらそれでokと思っていたがApacheのログが127.0.0.1になる。Nginxはproxy_passを設定してもX-Forwarded-Forの設定は自前でする必要があるようだ。しばらくApache側の設定反映を疑ったため時間を浪費した。

gem passengerは使わなくていい

Passengerのインストール方法をみるとNginxへのインストールには、

gem install passenger
passenger-install-nginx-module

を実行すればよくて、最初はこれに従ってみたところ以下のような質問が表示された。

Automatically download and install Nginx?

Nginx doesn't support loadable modules such as some other web servers do,
so in order to install Nginx with Passenger support, it must be recompiled.

Do you want this installer to download, compile and install Nginx for you?

 1. Yes: download, compile and install Nginx for me. (recommended)
    The easiest way to get started. A stock Nginx 1.0.10 with Passenger
    support, but with no other additional third party modules, will be
    installed for you to a directory of your choice.

 2. No: I want to customize my Nginx installation. (for advanced users)
    Choose this if you want to compile Nginx with more third party modules
    besides Passenger, or if you need to pass additional options to Nginx's
    'configure' script. This installer will  1) ask you for the location of
    the Nginx source code,  2) run the 'configure' script according to your
    instructions, and  3) run 'make install'.

Whichever you choose, if you already have an existing Nginx configuration file,
then it will be preserved.

え、せっかくaptでnginx入れたのに何いってんの?ちょっと待ってちょっと待ってちょっと待って。でキャンセルした。そう、この方法でインストールする場合、Passenger機能の付いたnginxをビルドして新規にインストールすることになる。でもやっぱり管理上、aptで運用したい。

libapache2-mod-passengerがあるんだからnginxのパッケージもあるんじゃないのとapt-cache search passengerで検索してnginx-extrasが見つかったのでgem uninstall passengerで消えてもらった。

passenger-install-nginx-moduleを実行するためにいろんなパッケージを入れたのはここだけの秘密。

nginx-fullはnginx-extrasの上位版ではない

dotdebからnginxをインストールするとnginx-fullがインストールされる。さらにnginx-extrasをインストールしようとするとnginx-fullと衝突したので、nginx-fullはnginx-extrasを包括しているんだろうと勝手に勘違いしてしまった。そのためpassengerの設定をしようとすると「 unknown directive “passenger_root” 」とか言われて怒られる。

nginx-fullとnginx-extrasのパッケージの情報を表示してみたところ以下のようにモジュールが異なっていた。

(nginx-full)
Standard HTTP Modules:
   Core, Access, Auth Basic, Auto Index, Browser, Charset, Empty GIF, FastCGI,
   Geo, Gzip, Headers, Index, Limit Requests, Limit Zone, Log, Map, Memcached,
   Proxy, Referer, Rewrite, SCGI, Split Clients, SSI, Upstream, User ID, UWSGI
 Optional HTTP Modules:
   Addition, Debug, GeoIP, Gzip Precompression, HTTP Sub, Image Filter, IPv6,
   RealIP, Secure link, Stub Status, SSL, WebDAV, XSLT
 Other Modules:
   File AIO
 Mail Modules:
   Mail Core, IMAP, POP3, SMTP, SSL
 Third Party Modules:
   Auth PAM, Cache purge, Echo, Syslog, Upstream Fair Queue
 (nginx-extras)
Standard HTTP Modules:
   Core, Access, Auth Basic, Auto Index, Browser, Charset, Empty GIF, FastCGI,
   Geo, Gzip, Headers, Index, Limit Requests, Limit Zone, Log, Map, Memcached,
   Proxy, Referer, Rewrite, SCGI, Split Clients, SSI, Upstream, User ID, UWSGI
 Optional HTTP Modules:
   Addition, Debug, WebDAV, Flash Streaming Video, GeoIP, Gzip Precompression,
   HTTP Sub, Image Filter, MP4, RealIP, Stub Status, SSL, XSLT, IPv6, Embedded
   Perl, Secure Link, Random Index
 Mail Modules:
   Mail Core, POP3, IMAP, SMTP, SSL
 Third Party Modules:
   Echo, Embedded Lua, http push, Nginx Development Kit, Upload module,
   Upload Progress, HttpHeadersMore, Upstream Fair Queue, Chunkin, Auth PAM
 Modules added by Dotdeb:
   File AIO, Syslog, Cache purge, Passenger

というわけで nginx-extras > nginx-full である。nginx-fullを削除して、nginx-extrasを入れなおすはめになった。