SmallStyle


2012-06-19

_ tDiary を unicorn で動かすまでのもろもろ

サーバーの引っ越し作業を機に tDiary を Unicorn で動かすようにしたので,このサーバーでの設定について記録しておきます.サーバーの環境は以下の通り.

文中でのコマンドなどは必要に応じて sudo などで実行しています.

最新の tDiary を入手する

最新の安定版は 3.1.3 ですが,リリース後に特に Rack 環境まわりの変更が充実しているので,最新版を github から clone します.ちなみにこのサーバーでは tDiary を/opt/tdiary 以下で管理し,所有者を www-data ユーザーとしています.環境構築時は作業ユーザーの権限にしておいて,最後に必要に応じて権限を変更するほうがいろいろ楽かもしれないです.

$ mkdir /opt/tdiary
$ cd /opt/tdiary
$ git clone git://github.com/tdiary/tdiary-core.git

必要なライブラリのインストール

Rack 環境で動作させるために必要なライブラリについては,Gemfile で定義されているので,bundler を利用してインストールします.特にまっさらな環境の場合,coffeescript から execjs を介して JavaScript ランタイムを実行するので,nodejs などなんらかの JavaScript ランタイムがインストールされている必要があります.ここでは Gemfile に therubyracer を追加して対応することにしました.現在は Gemfile に記述されているので,追加する必要はありません.

diff --git a/Gemfile b/Gemfile
index 9ff550f..ee45113 100644
--- a/Gemfile
+++ b/Gemfile
@@ -2,6 +2,7 @@ source :rubygems

 gem 'rake'
 gem 'coffee-script'
+gem 'therubyracer'

 # Use rack environment
 gem 'rack'

Gemfile へ therubyracer を追加したら,bundler をインストールし,その後,必要なライブラリをインストールします.インストール先は tdiary-core/.bundle 以下とし,test,development,production グループのライブラリは除外しています.production グループは主に Heroku 向けのライブラリが定義されていて,今のところ,DB も memcached も使わないので除外しています.

$ cd /opt/tdiary
$ rbevn local 1.9.3-p194
$ gem install bundler
$ rbenv rehash
$ cd tdiary-core
$ bundle install --path .bundle --without test development production

tdiary.conf を作成して rackup してみる

tDiary の設定ファイル tdiary.conf を作成します.tdiary.conf.sample をコピーして必要な部分を修正すれば基本的に問題ありません.日記データの保存先を指定する @data_path に適切なパスを指定します.書き込み先は,最終的に unicorn を実行するユーザーが書き込み権限のある場所を指定します.

$ cp tdiary.conf.sample tdiary.conf

ここまでこればひとまず rackup コマンドでアプリケーションを実行して動作の確認ができます.

$ bundle exec rackup

ブラウザでアクセスしてみて表示されればとりあえず問題なし.tdiary.conf で @index が未設定の場合は,リダイレクト先の URL がデフォルト値として"./"が指定されてアクセスできない場合がありますが,"."を消せばアクセスできます.バグのようだったので修正しました.この現象が起きるのは初回アクセス時のみのよう.

更新ページに認証をかける

認証関係は CGI の時は Web サーバーで行っていたけど,Rack 環境では OmniAuth を利用した Twitter や Github のアカウントでの認証もできるようになっています.このサーバーでは従来と同じような .htpasswd を利用した認証にしています.apache2-utils パッケージなどがインストールされていると,htpasswd コマンドを実行して,,htpasswd ファイルの作成もできますが,tDiary では .htpasswd を作成するための rake コマンドが用意されているので,わざわざインストールしてなくてもよいので便利です.

$ RACK_ENV=production bundle exec rake auth:password:create

ユーザー名とパスワードの入力をすると.htpasswd ファイルが出力されます.

Unicorn で動かす

Unicorn で実行するために,設定ファイルとして unicorn.rb を作成します.中身は,https://github.com/blog/517-unicorn を参考にしています.PID ファイルやログの出力先などは,Ubuntu で標準的に出力される場所に合わせています.また,unicorn のプロセスは master プロセスを root ユーザーで実行し,fork したプロセスは www-data ユーザーで実行するようになっています.

worker_processes 2

app_dir = File.expand_path( File.dirname( __FILE__ ) )
working_directory app_dir

preload_app true

timeout 30

listen "/tmp/tdiary.sock", :backlog => 64
listen 3000, :tcp_nopush => true

pid "/var/run/tdiary.pid"

stderr_path "/var/log/tdiary/stderr.log"
stdout_path "/var/log/tdiary/stdout.log"

before_fork do |server, worker|
        old_pid = "/var/run/tdiary.pid.oldbin"
        if File.exists?( old_pid ) && server.pid != old_pid
                begin
                        Process.kill( "QUIT", File.read( old_pid ).to_i )
                rescue Errno::ENOENT, Errno::ESRCH
                end
        end
end

after_fork do |server, worker|
        begin
                uid, gid = Process.euid, Process.egid
                user, group = "www-data", "www-data"
                target_uid = Etc.getpwnam( user ).uid
                target_gid = Etc.getpwnam( group ).gid
                worker.tmp.chown( target_uid, target_gid )
                if uid != target_uid || gid != target_gid
                        Process.initgroups( user, target_gid )
                        Process::GID.change_privilege( target_gid )
                        Process::UID.change_privilege( target_uid )
                end
        rescue => e
                STDERR.puts "couldn't change user, oh well"
                raise e
        end
end

unicorn をインストールし,unicorn.rb で指定したログ出力先のディレクトリを作成します.

$ gem install unicorn
$ rbenv rehash
$ sudo mkdir /var/log/tdiary

これで tDiary を unicorn で実行することが可能になります.実行ユーザーは root 権限でないと,上記の設定では PID ファイルの作成で権限がなくエラーとなるので,以下のように実行すると確認できます.

$ sudo /usr/local/rbenv/versions/1.9.3-p194/bin/unicorn -c /opt/tdiary/tdiary-core/unicorn.rb 

ここから先は主に運用する際に必要な設定手順について.

tDiary をサービスとして登録する

upstart を利用して tDiary をサーバー起動時に立ち上がるように設定します./etc/init 以下に tdiary.conf ファイルを作成し,以下のような設定にしました.

 #!/bin/bash
description "tDairy"

start on runlevel [2345]
stop on runlevel [016]

script
        export LANG=en_US.UTF-8
        exec /usr/local/rbenv/versions/1.9.3-p194/bin/unicorn -c /opt/tdiary/tdiary-core/unicorn.rb 
end script

respawn

これにより service コマンドで tDiary の stop/start が行えるようになります.

$ sudo service tdiary start

ログをローテーションする

しばらく運用していくとログファイルが大きくなってくるのが気になってきたので,logrotate を利用してログのローテーションを行うようにしました./etc/logrotate.d 以下に設定ファイルを配置してローテーションさせるようにします.最初に起動したときに出力されたログファイルの所有者が root の場合,reopen に失敗するので出力するログファイルの所有者は www-data とするように指定します.また,Unicorn は USR1を送るとログファイルを reopen してくれるのでローテーション後に USR1を送るように処理を含めている.

/var/log/tdiary/*.log {
        daily
        missingok
        rotate 52
        compress
        delaycompress
        notifempty
        create 0640 www-data adm
        sharedscripts
        postrotate
                [ ! -f /var/run/tdiary.pid ] || kill -USR1 `cat /var/run/tdiary.pid`
        endscript
}

肥大化した Unicorn プロセスを止める

運用して気になるのは fork したプロセスが肥大化している点.一定数のリクエストをさばくか,既定のメモリ使用量を超えるとプロセスを Kill する便利な unicon_killer を config.ru に追加してみました.

diff --git a/config.ru b/config.ru
index 1a169c7..299e968 100644
--- a/config.ru
+++ b/config.ru
@@ -17,6 +17,12 @@ use OmniAuth::Builder do
        # provider :twitter, ENV['TWITTER_KEY'], ENV['TWITTER_SECRET']
        # provider :github, ENV['GITHUB_KEY'], ENV['GITHUB_SECRET']
 end
+
+# Unicorn Killer
+require 'unicorn_killer'
+use UnicornKiller::Oom, 60 * 1024
+use UnicornKiller::MaxRequests, 1000
+
 map "#{base_dir}/auth" do
        run TDiary:rackauthomniauth:CallbackHandler.new
 end

というわけで,このサーバーで tDiary が動くまでの手順でした.Unicorn で実行してしばらくたちますが,特に問題もなく安定して動作しています.レスポンスもなかなかよいので,新しく環境を作るタイミングなどで移行してみてもよいのではないでしょうか.


about me

いろいろと興味を持ったことを書いてます.ちょっとしたことは hb(@smallstyle) on Twitter で書いてます.

Archive

2003|01|02|03|04|05|06|07|08|09|10|11|12|
2004|01|02|03|04|05|06|07|08|09|10|11|12|
2005|01|02|03|04|05|06|07|08|09|10|11|12|
2006|01|02|03|04|05|06|07|08|09|10|11|12|
2007|01|02|03|04|05|06|07|08|09|10|11|12|
2008|01|02|03|04|05|06|07|08|09|10|12|
2009|01|02|03|04|05|06|07|08|09|10|11|12|
2010|01|02|03|04|05|06|07|08|09|10|11|12|
2011|01|02|03|04|05|06|07|08|09|10|11|12|
2012|01|02|03|04|05|06|07|08|09|10|11|12|
2013|01|02|03|04|05|06|07|08|09|10|11|12|
2014|01|02|03|04|05|06|07|08|09|10|11|12|
2015|01|02|03|04|05|06|07|08|09|10|11|12|
2016|01|02|03|04|05|06|07|08|09|12|