現実的なWebサービス環境において、Raptor(Phusion Passenger 5)によるパフォーマンス向上がどの程度のものか調査するために、 ISUCON4 の予選問題のうち、Unicorn 部分を Raptor 化してベンチマークをとってみた。典型的なWebサービスシステムの3層構造(Proxy, App, DB)を構築し、ベンチマーカーにより高ワークロードを実現できるので、ISUCON の予選問題は適当な題材といえる。

EDIT: Unicornの2倍のパフォーマンスを実現したRackサーバ「Rhebok」をリリースしました - blog.nomadscafe.jp を参考に unicorn の worker_processes を 4 に調節しました。念のため、あわせて raptor の min-instances も 4 に調節して再計測しました (2014/12/19)

ベンチマーク条件

基本的に、ISUCON4予選のレギュレーションに則る。

インスタンスタイプ: m3.xlarge CPU: Xeon E5-2670 v2 @ 2.50GHz 4 vCPU メインメモリ: 16GB RAM ストレージ: EBS Magnetic volumes OS: Amazon Linux 3.14.19-17.43

* ここまで y_uuk1 テンプレ

  1. ISUCON4 予選のデフォルト状態(unicorn)
  2. ISUCON4 予選のデフォルト状態(raptor standalone に変更)
  3. kazeburo の術 を適用(unicorn)
  4. kazeburo の術 を適用(raptor standalone に変更)
  5. kazeburo の術 を適用(raptor on nginx に変更)

で比較する

kazeburo の術 Ruby 版

ISUCON4 予選でアプリケーションを変更せずに予選通過ラインを突破するの術 から以下の3つを除外した形

  • memcached
  • perl 関連 (cpanfile や app.psgi の変更)
  • /etc/supervisord.conf の変更

init.sh に以下を追加

$ cat init.sh 
cat <<'EOF' | mysql -h ${myhost} -P ${myport} -u ${myuser} ${mydb}
alter table login_log add index ip (ip), add index user_id (user_id);
EOF

/etc/sysctl.conf に以下を追加。

$ cat /etc/sysctl.conf
net.ipv4.tcp_max_tw_buckets = 2000000
net.ipv4.ip_local_port_range = 10000 65000
net.core.somaxconn = 32768
net.core.netdev_max_backlog = 8192
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_fin_timeout = 10
sudo /sbin/sysctl -p

で適用

/etc/nginx/nginx.conf の設定

$ cat /etc/nginx/nginx.conf
worker_processes  1;

events {
  worker_connections  10000;
}

http {
  include     mime.types;
  access_log  off;
  sendfile    on;
  tcp_nopush  on;
  tcp_nodelay on;
  etag        off;
  upstream app {
    server unix:/dev/shm/app.sock;
  }

  server {
    location / {
      proxy_pass http://app;
    }
    location ~ ^/(stylesheets|images)/ {
      open_file_cache max=100;
      root /home/isucon/webapp/public;
    }
  }
}

/etc/my.conf の設定

$ cat /etc/my.cnf
innodb_buffer_pool_size = 1G
innodb_flush_log_at_trx_commit = 0
innodb_flush_method=O_DIRECT
Ruby 専用の追加設定


env.sh に以下を追加

export RACK_ENV=production 

unicorn_config を以下のように変更(2012/12/19 追加)

worker_processes 4
preload_app true

Procfile を以下のように変更

unicorn: bundle exec unicorn -c unicorn_config.rb -l /dev/shm/app.sock

再起動して適用

$ sudo service mysqld restart
$ sudo supervisorctl restart isucon_ruby
$ sudo service nginx restart

Raptor への切り替え(standalone)

Gemfile

- gem "unicorn"
+ gem "passenger", "= 5.0.0.beta1"

Procfile (unicorn_config.rb で worker_processes 4 としたので --min-instances を 4 にしてみた)

- unicorn: bundle exec unicorn -c unicorn_config.rb -l /dev/shm/app.sock
+ passenger: bundle exec passenger start --min-instances 4 -S /dev/shm/app.sock 

Raptor on Nginx

passenger を nginxに組み込んで動かす

2.5. Generic installation, upgrade and downgrade method: via RubyGems の手順で

gem install passenger --pre -v 5.0.0.beta1

nginx を passenger module 付きでビルドしなおす。nginx は消さなくてもいいよ、と書いてあったがいちおう消しておいた

sudo yum remove nginx

g++ がない、と怒られたのでいれておく. インストール先ディレクトリも作っておく

sudo yum -y install gcc-c++
sudo mkdir /opt/nginx
sudo chmod a+w /opt/nginx

passenger gem に入っている以下のコマンドでビルド&インストール

$ passenger-install-nginx-module --auto

/opt/nginx/conf/nginx.conf を以下のように変更 (kazeburo の術仕様)

worker_processes  1;

events {
  worker_connections  10000;
}

http {
  passenger_root /home/isucon/.local/ruby/lib/ruby/gems/2.1.0/gems/passenger-5.0.0.beta1;
  passenger_ruby /home/isucon/.local/ruby/bin/ruby;
  include     mime.types;
  access_log  off;
  sendfile    on;
  tcp_nopush  on;
  tcp_nodelay on;
  etag        off;

  server {
    listen 80;
    root /home/isucon/webapp/ruby/public;
    passenger_enabled on;
passenger_min_instances 4; location ~ ^/(stylesheets|images)/ { open_file_cache max=100; } } }

public ディレクトリが config.ru と同じ階層にないとダメなので、symbolic link をはってごまかす

ln -s /home/isucon/webapp/public /home/isucon/webapp/ruby/

起動

sudo /opt/nginx/sbin/nginx

ベンチマーク結果

  • benchmarker の workload 指定は全て 8 (一定)
構成スコア
unicorn(default)2433
raptor standalone(default)2483
unicorn(kazeburo)40983
raptor standalone(kazeburo)40359
raptor on nginx(kazeburo)40358

unicorn の場合は kazeburo の術で4万超えていなかったが Raptor で 4万超えの結果となった。Introducing Phusion Passenger 5 beta 1, codename “Raptor” の記事で彼らが「Hello World ベンチの結果なんてたいして意味がないんだよ」と言っていた通り現実に近いアプリで評価すると確かに4倍とはならなかったが、それでも ISUCON 民としてこの結果はうれしい。

(2014/12/19) unicorn とあまり変わらない結果となった。Introducing Phusion Passenger 5 beta 1, codename “Raptor” の記事で彼らが「Hello World ベンチの結果なんてたいして意味がないんだよ」と言っていたのでそんなものかもしれない。後述するが fail があったので、その影響は大きそう。

passenger module 組み込みの nginx をビルドして動かしてみたが、スコアは変わらなかった。

Raptor のエラー

なお、Raptor で以下のようなエラーが出ていた。こちらの Issue で対応中のようだ。

03:33:52 passenger.1 | App 6297 stderr: /home/isucon/.local/ruby/lib/ruby/gems/2.1.0/gems/passenger-5.0.0.beta1/lib/phusion_passenger/config/system_metrics_command.rb:32:in `exec'
03:33:52 passenger.1 | App 6297 stderr: : No such file or directory - /home/isucon/.local/ruby/lib/ruby/gems/2.1.0/gems/passenger-5.0.0.beta1/buildout/support-binaries/PassengerAgent (Errno::ENOENT)
03:33:52 passenger.1 | App 6297 stderr:         from /home/isucon/.local/ruby/lib/ruby/gems/2.1.0/gems/passenger-5.0.0.beta1/lib/phusion_passenger/config/system_metrics_command.rb:32:in `run'
03:33:52 passenger.1 | App 6297 stderr:         from /home/isucon/.local/ruby/lib/ruby/gems/2.1.0/gems/passenger-5.0.0.beta1/lib/phusion_passenger/config/main.rb:75:in `run!'
03:33:52 passenger.1 | App 6297 stderr:         from /home/isucon/.local/ruby/lib/ruby/gems/2.1.0/gems/passenger-5.0.0.beta1/bin/passenger-config:37:in `<main>'

また、これとはおそらく原因が別だと思われるが benchmarker では以下のような結果がでて、ちょくちょく fail していた。

04:57:45 type:fail      reason:Expected selector is not found: //*[@id='notice-message']        method:GET      uri:/
04:57:45 type:fail      reason:Expected html text is match: This account is locked., got Wrong username or password     method:GET      uri:/

画面を直接さわると正常に動いているように見えるので、並列で高負荷をかけた時の問題だろうか、と推測している。

04:45:33 type:score     success:185820  fail:5584       score:40141

stable 版になって、全てが success に転じるともっと得点が伸びる期待がある。