[動作確認環境]
CentOS-6.3_x86_64
動作環境パターンは2つあります。
1.Apache + Passenger(mod_rails)
2.Nginx + Unicorn(Ruby Rack Application Server)
以下に2パターンそれぞれの構築方法を記載します。
Apache + Passenger編
####yumリポジトリ設定にEPEL追加
rpm -ivh http://ftp.riken.jp/Linux/fedora/epel/6/x86_64/epel-release-6-7.noarch.rpm
####必要パッケージインストール
後々 git が必要になりますが、それは group install "Development Tools" で入ります。
yum groupinstall "Development Tools"
yum install openssl-devel readline-devel zlib-devel curl-devel libyaml-devel
yum install mysql-server mysql-devel
yum install httpd httpd-devel
yum install ImageMagick ImageMagick-devel
####Ruby-1.9.3インストール
cd /usr/local/src
wget ftp://ftp.ruby-lang.org/pub/ruby/1.9/ruby-1.9.3-p194.tar.bz2
tar xjf ruby-1.9.3-p194.tar.bz2
cd ruby-1.9.3-p194
./configure
make
make install
ruby -v
# ruby 1.9.3p194 (2012-04-20 revision 35410) [x86_64-linux]
which ruby
# /usr/local/bin/ruby
####MySQL設定
/etc/my.cnf [追記]
[mysqld]
~ (中略) ~
character-set-server=utf8
[mysql]
~ (中略) ~
default-character-set=utf8
####MySQLユーザ登録
Redmine用のデータベース作成+専用ユーザ登録
MySQL起動
/etc/init.d/mysqld start
MySQL自動起動設定
chkconfig mysqld on
chkconfig --list mysql
# mysqld 0:off 1:off 2:on 3:on 4:on 5:on 6:off
Redmine用データベース作成
mysql> CREATE DATABASE redmine DEFAULT CHARACTER SET utf8;
Query OK, 1 row affected (0.00 sec)
mysql> SHOW DATABASES;
+--------------------+
| Database |
+--------------------+
| information_schema |
| mysql |
| redmine |
| test |
+--------------------+
4 rows in set (0.00 sec)
Redmine用ユーザ登録
mysql> GRANT ALL ON redmine.* TO redmine@'192.168.1.150' IDENTIFIED BY 'password';
:
mysql> FLUSH PRIVILEGES;
:
mysql>
####Redmine (本体) インストール
cd /usr/local/src
wget http://rubyforge.org/frs/download.php/76495/redmine-2.1.2.tar.gz
cd /var/www/html # Apacheドキュメントルート
tar zxvf /usr/local/src/redmine-2.1.2.tar.gz
ln -s redmine-2.1.2 redmine
ls -l
:
lrwxrwxrwx. 1 root root 13 10月 12 20:22 2012 redmine -> redmine-2.1.2
drwxrwxr-x. 16 1000 1000 4096 9月 30 19:42 2012 redmine-2.0.2
####データベース接続設定
[Redmineインストールディレクトリ]/config/database.ymlを作成します。
production:
adapter: mysql2
database: redmine
host: localhost
username: redmine
password: password
encoding: utf8
####Redmineが仕様するgemパッケージインストール
bundlerを使って、RedmineおよびRuby on Railsが依存するgemパッケージをインストールします。
依存するgemパッケージは、Redmineを展開した中のGemfileに記述されています。
####bundlerインストール
gem install bundler --no-rdoc --no-ri
# Fetching: bundler-1.2.1.gem (100%)
# Successfully installed bundler-1.2.1
# 1 gem installed
####Redmineが使用するgemパッケージインストール
gemパッケージは、システム共通の場所にインストールするか、Railsアプリケーション(今回はRedmine)固有の場所にインストールするかを指定できます。同一マシンで異なるバージョンのRailsアプリケーションを動かすときは後者でないと競合が生じます。
そこで、今回はRedmine固有の場所にインストールすることにします。
bundleコマンドは、カレントディレクトリにあるGemfileを読むので、実行時はredmineにカレントディレクトリを移動します。
# cd /var/lib/redmine-2.1.2
# bundle install --path vendor/bundler --without development test postgresql sqlite
Fetching gem metadata from http://rubygems.org/.......
Installing rake (0.9.2.2)
Installing i18n (0.6.1)
Installing multi_json (1.3.6)
Installing activesupport (3.2.8)
Installing builder (3.0.0)
Installing activemodel (3.2.8)
Installing erubis (2.7.0)
Installing journey (1.0.4)
Installing rack (1.4.1)
Installing rack-cache (1.2)
Installing rack-test (0.6.2)
Installing hike (1.2.1)
Installing tilt (1.3.3)
Installing sprockets (2.1.3)
Installing actionpack (3.2.8)
Installing mime-types (1.19)
Installing polyglot (0.3.3)
Installing treetop (1.4.11)
Installing mail (2.4.4)
Installing actionmailer (3.2.8)
Installing arel (3.0.2)
Installing tzinfo (0.3.33)
Installing activerecord (3.2.8)
Installing activeresource (3.2.8)
Using bundler (1.2.1)
Installing coderay (1.0.8)
Installing rack-ssl (1.3.2)
Installing json (1.7.5) with native extensions
Installing rdoc (3.12)
Installing thor (0.16.0)
Installing railties (3.2.8)
Installing jquery-rails (2.0.3)
Installing mysql2 (0.3.11) with native extensions
Installing net-ldap (0.3.1)
Installing ruby-openid (2.1.8)
Installing rack-openid (1.3.1)
Installing rails (3.2.8)
Installing rmagick (2.13.1) with native extensions
Your bundle is complete! It was installed into ./vendor/bundler
Post-install message from rdoc:
Depending on your version of ruby, you may need to install ruby rdoc/ri data:
<= 1.8.6 : unsupported
= 1.8.7 : gem install rdoc-data; rdoc-data --install
= 1.9.1 : gem install rdoc-data; rdoc-data --install
>= 1.9.2 : nothing to do! Yay!
#
####Redmine初期化
####セッションデータ暗号化の鍵生成
cd /var/lib/redmine-2.1.2
bundle exec rake generate_secret_token
bundlerでインストールしたgemのコマンドは、bundle execで実行します。
####データベース初期化
RAILS_ENV=production bundle exec rake db:migrate
# == Setup: migrating #=====================================================#=====
#-- create_table("attachments", {:force=>true})
# :
#== AddBoardsParentId: migrated (0.1603s) #=====================================
####動作確認
WEBrickを実行し、Redmineが正常に起動するかを確認します。
(※WEBrickはRailsインストール時に一緒に入ってます)
####WEBrick起動
cd /var/www/html/redmine
ruby script/rails server webrick -e production
=> Booting WEBrick
=> Rails 3.2.3 application starting in production on http://0.0.0.0:3000
=> Call with -d to detach
=> Ctrl-C to shutdown server
:
[2012-05-24 22:49:39] INFO WEBrick::HTTPServer#start: pid=16590 port=3000
####同一マシン上のブラウザからアクセスして確認
http://192.168.1.150:3000
Redmineの画面が表示されれば成功!
####Passengerインストール Passengerはgemパッケージで提供されていますが、RedmineのGemfileには記載されていないので、bundleではインストールされません。そこで、bundleでインストールできるよう追加の設定をします。
/Gemfile.local を新規に作成し以下を記述
gem "passenger"
で、bundle updateを実行します。
bundle update
:
Installing daemon_controller (1.0.0)
Installing fastthread (1.0.7) with native extensions
:
Installing passenger (3.0.17)
:
※--path 指定を今回は省略することができます。bundleを実行したときのオプションが、/bundle/configファイルに保存されるためです。
Apache2に組み込むライブラリをビルドします。
bundle exec passenger-install-apache2-module
Welcome to the Phusion Passenger Apache 2 module installer, v3.0.17.
:
Press Enter to continue, or Ctrl-C to abort.
Enterキーを押すとビルドが開始されます。しばらくすると、
-------------------------------------------
Checking for required software...
* GNU C++ compiler... found at /usr/bin/g++
* Curl development headers with SSL support... found
* OpenSSL development headers... found
* Zlib development headers... found
* Ruby development headers... found
* OpenSSL support for Ruby... found
* RubyGems... found
* Rake... found at /usr/local/bin/rake
* rack... found
* Apache 2... found at /usr/sbin/httpd
* Apache 2 development headers... not found
* Apache Portable Runtime (APR) development headers... not found
* Apache Portable Runtime Utility (APU) development headers... not found
Some required software is not installed.
But don't worry, this installer will tell you how to install them.
Press Enter to continue, or Ctrl-C to abort.
Apache関連で not found がでます。
必要なパッケージをインストールします。
yum install httpd-devel apr-devel
再度、bundleを実行すると今度はすべてfoundとなり成功します。
そしてコンパイル後にApacheの設定内容が出力されるのでコピーして控えておきます。
--------------------------------------------
The Apache 2 module was successfully installed.
Please edit your Apache configuration file, and add these lines:
LoadModule passenger_module /var/www/html/redmine-2.1.2/vendor/bundler/ruby/1.9.1/gems/passenger-3.0.18/ext/apache2/mod_passenger.so
PassengerRoot /var/www/html/redmine-2.1.2/vendor/bundler/ruby/1.9.1/gems/passenger-3.0.18
PassengerRuby /usr/local/bin/ruby
After you restart Apache, you are ready to deploy any number of Ruby on Rails
applications on Apache, without any further Ruby on Rails-specific
configuration!
Press ENTER to continue.
--------------------------------------------
Deploying a Ruby on Rails application: an example
Suppose you have a Rails application in /somewhere. Add a virtual host to your
Apache configuration file and set its DocumentRoot to /somewhere/public:
<VirtualHost *:80>
ServerName www.yourhost.com
# !!! Be sure to point DocumentRoot to 'public'!
DocumentRoot /somewhere/public
<Directory /somewhere/public>
# This relaxes Apache security settings.
AllowOverride all
# MultiViews must be turned off.
Options -MultiViews
</Directory>
</VirtualHost>
And that's it! You may also want to check the Users Guide for security and
optimization tips, troubleshooting and other useful information:
/var/www/html/redmine-2.1.2/vendor/bundler/ruby/1.9.1/gems/passenger-3.0.18/doc/Users guide Apache.html
Enjoy Phusion Passenger, a product of Phusion (www.phusion.nl) :-)
https://www.phusionpassenger.com
Phusion Passenger is a trademark of Hongli Lai & Ninh Bui.
####Apache設定
/etc/httpd/conf.d/passenger.conf を新規作成して記述します。
LoadModule passenger_module /var/www/html/redmine-2.1.2/vendor/bundler/ruby/1.9.1/gems/passenger-3.0.18/ext/apache2/mod_passenger.so
PassengerRoot /var/www/html/redmine-2.1.2/vendor/bundler/ruby/1.9.1/gems/passenger-3.0.18
PassengerRuby /usr/local/bin/ruby
<VirtualHost *:80>
ServerName 192.168.1.150
# !!! Be sure to point DocumentRoot to 'public'!
DocumentRoot /var/www/html/redmine/public
<Directory /var/www/html/redmine/public>
# This relaxes Apache security settings.
AllowOverride all
# MultiViews must be turned off.
Options -MultiViews
</Directory>
</VirtualHost>
**ApacheからRedmineをアクセスできるように所有者変更
chown -R apache.apache /var/www/html/redmine-2.1.2
Apache起動+自動起動設定
/etc/init.d/httpd start
chkconfig httpd on
Redmineへアクセスして動作確認
http://192.168.1.150/
Nginx + Unicorn編
※作業前にApacheは停止しておいてください。
###Unicorn
RailsサーバーのUnicornを設定します。
Unicornは、Rackアプリケーションを実行するためのHTTPサーバーで、クライアントへの効率的な応答(速さ、低遅延)を目的として設計されています。
#####インストール Unicornは、gem形式で提供されています。そこで、RedmineのGemfile.localに記述し、Redmine固有にインストールします。
[redmineインストールディレクトリ]/Gemfile.local を作成
gem "unicorn"
bundlerの更新実施
# Redmine インストールディレクトリへ移動
cd /var/www/html/redmine
bundle update
Installing kgio (2.7.4) with native extensions
:
Installing raindrops (0.10.0) with native extensions
:
Installing unicorn (4.4.0) with native extensions
:
#####動作確認:フォアグランドプロセスとして起動
# Redmine インストールディレクトリへ移動
cd /var/www/html/redmine
bundle exec unicorn_rails -l 8081 -E production
I, [2012-10-23T16:28:22.687949 #3783] INFO -- : listening on addr=0.0.0.0:8081 fd=9
I, [2012-10-23T16:28:22.688150 #3783] INFO -- : worker=0 spawning...
I, [2012-10-23T16:28:22.689226 #3783] INFO -- : master process ready
I, [2012-10-23T16:28:22.689970 #3786] INFO -- : worker=0 spawned pid=3786
I, [2012-10-23T16:28:22.690106 #3786] INFO -- : Refreshing Gem list
/var/lib/redmine-2.1.2/vendor/bundler/ruby/1.9.1/gems/activesupport-3.2.8/lib/active
_support/dependencies.rb:251:in `block in require': iconv will be deprecated in the
future, use String#encode instead.
I, [2012-10-23T16:28:29.639910 #3786] INFO -- : worker=0 ready
コマンドラインオプションは以下のとおり。
--listen -l [アドレス:]ポート
ソケットのエンドポイントを指定
--config-file -c ファイル
設定ファイルを指定
-D
デーモンプロセス起動指定
-E [RAILS_ENV]
production等を指定
フォアグラウンドプロセスとして起動した場合、Ctrl-Cで停止します。
デーモンプロセスとして起動した場合、シグナルINT(強制終了)またはQUIT(グレースフル停止)をマスタープロセスに送ります。
デーモンプロセスに対する設定ファイル再読み込みトリガーはシグナルHUP、プログラム(Ruby)をデプロイしたらシグナルUSR2を送ります。
マスタープロセスのプロセスIDは、次のように調べます。
pgreg -f 'unicorn_rails master'
#####設定ファイル
[redmineインストールディレクトリ]/config/unicorn.rb を作成
# ワーカープロセスの数。1ワーカーで1つのリクエストを処理する。
# ワーカー数が上限に達すると、先行するリクエストが完了するまで待ちとなる。
worker_processes 2
# リクエスト待ち受け口、TCPとUNIXドメインとが指定可能。
# CASE1: TCP
# listen 8282, :tcp_nopush => true
# CASE2: Socket
listen "/tmp/unicorn_redmine.sock", :backlog => 64
# 稼働中のプロセスのPIDを書いておくファイル。相対パス指定OKか?
pid "tmp/pids/unicorn.pid"
# デーモンで起動すると標準出力/標準エラー出力が/dev/nullになるので、
# それぞれログファイルに出力する。
stderr_path "log/unicorn.stderr.log"
stdout_path 'log/unicorn.stdout.log'
# マスタープロセス起動時にアプリケーションをロードする(true時)。
# ワーカープロセス側でロードをしないのでメモリ消費、応答性良好になる。
# ただし、ソケットはfork後に開きなおす必要あり。
# HUPシグナルでアプリケーションはロードされない。
preload_app true
before_fork do |server, worker|
defined?(ActiveRecord::Base) and ActiveRecord::Base.connection.disconnect!
end
after_fork do |server, worker|
defined?(ActiveRecord::Base) and ActiveRecord::Base.establish_connection
begin
uid, gid = Process.euid, Process.egid
user, group = "redmine", "redmine"
target_uid = Etc.getpwnam(user).uid
target_gid = Etc.getgrnam(group).gid
worker.tmp.chown(target_uid, target_gid)
if uid != target_uid or gid != target.gid
Process.initgroups(user, target_gid)
Process::GID.change_privilege(target_gid)
Process::UID.change_privilege(target_uid)
end
rescue
if RAILS_ENV = "development"
STDERR.puts "could not change user, oh well"
else
STDERR.puts "could not change user, oh well"
raise e
end
end
end
#####起動スクリプト
新規作成
# chkconfig: 345 83 17
# description: unicorn is a HTTP rack application server.
# set -e => 実行したコマンドが0以外の戻り値を返した場合、シェルスクリプト終了
set -e
sig () {
test -s "$PID" && kill -$1 `cat "$PID"`
}
oldsig () {
test -s "$OLD_PID" && kill -$1 `cat "$OLD_PID"`
}
cmd () {
case $1 in
start)
sig 0 && echo >&2 "Already running" && exit 0
echo "Starting"
$CMD
;;
stop)
sig QUIT && echo "Stopping" && exit 0
echo >&2 "Not running"
;;
force-stop)
sig TERM && echo "Forcing a stop" && exit 0
echo >&2 "Not running"
;;
restart|reload)
sig USR2 && sleep 5 && oldsig QUIT && echo "Killing old master" `cat $OLD_PID` && exit 0
echo >&2 "Couldn't reload, starting '$CMD' instead"
$CMD
;;
upgrade)
sig USR2 && echo Upgraded && exit 0
echo >&2 "Couldn't upgrade, starting '$CMD' instead"
$CMD
;;
rotate)
sig USR1 && echo rotated logs OK && exit 0
echo >&2 "Couldn't rotate logs" && exit 1
;;
*)
echo >&2 "Usage: $0 <start|stop|restart|upgrade|rotate|force-stop>"
exit 1
;;
esac
}
setup () {
echo -n "$RAILS_ROOT: "
cd $RAILS_ROOT || exit 1
export PID=$RAILS_ROOT/tmp/pids/unicorn.pid
export OLD_PID="$PID.oldbin"
CMD="bundle exec unicorn_rails -c config/unicorn.rb -E $RAILS_ENV -D"
}
start_stop () {
# either run the start/stop/reload/etc command for every config under /etc/unicorn
# or just do it for a specific one
# $1 contains the start/stop/etc command
# $2 if it exists, should be the specific config we want to act on
if [ $2 ]; then
. $2
setup
cmd $1
else
for CONFIG in /etc/unicorn/*.conf; do
# import the variables
. $CONFIG
setup
# run the start/stop/etc command
cmd $1
done
fi
}
ARGS="$1 $2"
start_stop $ARGS
#####/etc/unicorn/redmine-2.1.2.conf
RAILS_ENV=production
RAILS_ROOT=/var/lib/redmine-2.1.2
#####自動起動設定:chkconfig
chkconfig --add unicorn
chkconfig unicorn on
chkconfig --list unicorn
unicorn 0:off 1:off 2:on 3:on 4:on 5:on 6:off
###Nginx
#####/etc/nginx/conf.d/redmine.conf
server_nameなど適宜変更してください。
proxy_pass
upstreamブロック使用の場合
http://[upstream-block-name];
upstream unicorn-redmine {
server unix:/tmp/unicorn_redmine.sock;
}
server {
listen 80;
server_name redmine.php-server;
root /var/www/html/redmine/public;
location / {
if (-f $request_filename) { break; }
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-redmine;
}
}
#####Nginx+Unicorn起動
/etc/init.d/unicorn start
/etc/init.d/nginx start
[root@centos6 redmine]# netstat -tapon
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name Timer
tcp 0 0 127.0.0.1:9000 0.0.0.0:* LISTEN 1650/php-fpm off (0.00/0/0)
tcp 0 0 0.0.0.0:3306 0.0.0.0:* LISTEN 18107/mysqld off (0.00/0/0)
tcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN 28357/nginx off (0.00/0/0)
tcp 0 0 0.0.0.0:8282 0.0.0.0:* LISTEN 28341/unicorn_rails off (0.00/0/0)
#[Apache+Passenger] VS [Nginx+Unicorn]
どちらもノーチューンでベンチマーク取ってみました。
#####■ Apache+Passenger
[root@centos6 redmine]# ab -c 100 -n 10000 http://redmine.php-server/projects/test-project
This is ApacheBench, Version 2.3 <$Revision: 655654 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/
Benchmarking redmine.php-server (be patient)
Completed 1000 requests
Completed 2000 requests
Completed 3000 requests
Completed 4000 requests
Completed 5000 requests
Completed 6000 requests
Completed 7000 requests
Completed 8000 requests
Completed 9000 requests
Completed 10000 requests
Finished 10000 requests
Server Software: Apache/2.2.15
Server Hostname: redmine.php-server
Server Port: 80
Document Path: /projects/test-project
Document Length: 3905 bytes
Concurrency Level: 100
Time taken for tests: 200.904 seconds
Complete requests: 10000
Failed requests: 0
Write errors: 0
Total transferred: 46370000 bytes
HTML transferred: 39050000 bytes
Requests per second: 49.78 [#/sec] (mean)
Time per request: 2009.040 [ms] (mean)
Time per request: 20.090 [ms] (mean, across all concurrent requests)
Transfer rate: 225.40 [Kbytes/sec] received
Connection Times (ms)
min mean[+/-sd] median max
Connect: 0 0 3.6 0 45
Processing: 59 2000 2055.3 1346 17687
Waiting: 59 2000 2055.3 1346 17686
Total: 59 2000 2055.3 1348 17687
Percentage of the requests served within a certain time (ms)
50% 1348
66% 2158
75% 2770
80% 3221
90% 4660
95% 6134
98% 7963
99% 9799
100% 17687 (longest request)
#####■ Nginx+Unicorn
[root@centos6 ~]# ab -c 100 -n 10000 http://redmine.php-server/projects/test-project
This is ApacheBench, Version 2.3 <$Revision: 655654 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/
Benchmarking redmine.php-server (be patient)
Completed 1000 requests
Completed 2000 requests
Completed 3000 requests
Completed 4000 requests
Completed 5000 requests
Completed 6000 requests
Completed 7000 requests
Completed 8000 requests
Completed 9000 requests
Completed 10000 requests
Finished 10000 requests
Server Software: nginx/1.2.5
Server Hostname: redmine.php-server
Server Port: 80
Document Path: /projects/test-project
Document Length: 172 bytes
Concurrency Level: 100
Time taken for tests: 3.225 seconds
Complete requests: 10000
Failed requests: 98
(Connect: 0, Receive: 0, Length: 98, Exceptions: 0)
Write errors: 0
Non-2xx responses: 9902
Total transferred: 3643854 bytes
HTML transferred: 2085834 bytes
Requests per second: 3100.79 [#/sec] (mean)
Time per request: 32.250 [ms] (mean)
Time per request: 0.322 [ms] (mean, across all concurrent requests)
Transfer rate: 1103.40 [Kbytes/sec] received
Connection Times (ms)
min mean[+/-sd] median max
Connect: 0 2 1.1 2 17
Processing: 1 19 165.7 3 2328
Waiting: 1 18 165.7 2 2328
Total: 2 20 165.9 5 2332
Percentage of the requests served within a certain time (ms)
50% 5
66% 5
75% 5
80% 6
90% 7
95% 9
98% 15
99% 21
100% 2332 (longest request)
#####結果(感想) Nginx+Unicorn **爆速!!** 非同期ソケット(処理)ということでリソース消費がめちゃエコ仕様っす。
Apache+Unicorn 死亡フラグw
接続数分の子プロセス(スレッド?)生成するもんだからリソース食いまくり。そりゃあWEBサーバ台数追加したくもなります。abコマンド実行中はLoadAverageが上がる上がるw(※ベンチマーク見ればわかると思いますが。)
総評
Nginx+Unicorn圧勝
Apacheの方はチューニングである程度の改善は見込めると思いますが、Nginx+Unicornがノーチューンでここまでのパフォーマンスを発揮してしまう現実を目の前にするとApacheのチューニングなんて「メンドクセー」になってしまいますw。
フツーに考えて今回のケースだと勝負になりませんね。