きたるISUCON6向け武者修行その1 -仮想環境を構築して負荷試験を実行してみよう-
強い意志の元、今年こそISUCONに参加しようと思います。
ISUCONとは?
お題となるWebサービスを決められたレギュレーションの中で限界まで高速化を図るチューニングバトル、それがISUCON
そこで、まずはチームメンバーとISUCON4(2014年開催)の復習をしようということになりました。
ISUCONの過去問を構築するためのVagrantfileを用意してくださった素晴らしい方のリポジトリがあるので、そちらをベースに仮想環境を構築して進めていこうと思います。
github.com
1.Vagrantfileを準備する
前述のリポジトリをcloneし、isucon4-qualifierにcdします。
その階層にVagrantfileが用意されています。
$ git clone https://github.com/matsuu/vagrant-isucon
$ cd isucon4-qualifier
2.ローカルにVirtualBoxとVagrantをインストール
ここでは手軽にbrew caskでインストールします。
$ brew cask install virtualbox $ brew cask install vagrant
VirtualBoxは、仮想的なPCを作成し、特定のOSをインストール・実行できるソフトウェアです。
Vagrantは、仮想環境の構築と制御を行うソフトウェアです。
検証環境は下記の通りです。
- Mac OS X 10.9.5
- VirtualBox 5.0.20
- Vagrant 1.8.4
3.起動・プロビジョニング
Vagrantfileがあるディレクトリで下記コマンドにて環境を構築していきます。
$ vagrant up
するとエラーが発生しました。
Failed to load R0 module /Applications/VirtualBox.app/Contents/MacOS/VMMR0.r0: World writable: '/Applications' (VERR_SUPLIB_WORLD_WRITABLE). Failed to load VMMR0.r0 (VERR_SUPLIB_WORLD_WRITABLE).
どうやらVirtualBoxのバグらしく、下記コマンドを実行して再度vagrant upしたらすんなり通りました。
# VirtualBox.app配下全てについて、権限がないユーザ・グループ以外のユーザに対して書き換えを禁止する $ sudo chmod -R o-w /Applications/VirtualBox.app/ # パーミッションが正しくない場合は修復する $ sudo /usr/libexec/repair_packages --repair --standard-pkgs --volme / --debug
4.VagrantインスタンスにSSHでログインする
$ vagrant ssh Last login: Wed Jun 22 04:08:36 2016 from 10.0.2.2 [vagrant@localhost ~]$
5.isuconユーザにスイッチする
[vagrant@localhost ~]$ sudo su - isucon # 構成はこんな感じ [isucon@localhost ~]$ ll -lrt total 8984 drwxr-xr-x. 4 isucon isucon 4096 Jun 22 03:10 gocode drwxr-xr-x. 9 isucon isucon 4096 Jun 22 03:29 webapp -rwxr-xr-x. 1 isucon isucon 428 Jun 22 03:29 init.sh -rwxr-xr-x. 1 isucon isucon 734 Jun 22 03:29 env.sh -rwxr-xr-x. 1 isucon isucon 9177962 Jun 22 03:29 benchmarker drwxr-xr-x. 2 isucon isucon 4096 Jun 22 03:30 sql
この用意されたbenchmarkerを使ってサーバに負荷をかけて、スコアを競います。
6.参考実装の言語切り替え
今回我々のチームはpythonで戦う予定なので、まずはpythonでベンチマークを実行してみることとします。
デフォルトがRubyになっているので、supervisordを終了させます。
※Supervisorとは、常駐起動させたいスクリプトなどを容易にプロセス管理/デーモン化するツールです。
$ supervisorctl stop isucon_ruby
次に、/etc/supervisord.confを編集します。
[isucon_ruby]をautostart=falseにします。
[program:isucon_ruby] directory=/home/isucon/webapp/ruby command=/home/isucon/env.sh foreman start user=isucon stdout_logfile=/tmp/isucon.ruby.log stderr_logfile=/tmp/isucon.ruby.log autostart=false
切り替えたい言語をautostart=trueにします。
pythonはgunicornでHTTPサーバを立ち上げているようです。
Gunicorn - Python WSGI HTTP Server for UNIX
[program:isucon_python] directory=/home/isucon/webapp/python command=/home/isucon/env.sh gunicorn -c gunicorn_config.py app:app user=isucon stdout_logfile=/tmp/isucon.python.log stderr_logfile=/tmp/isucon.python.log autostart=true
最後にpythonにてsupervisordをstartさせます。
$ supervisorctl start isucon_python
supervisordは、よくあるinit.d配下での管理ではなかったことを知りました
/etc/init.d/supervisord stop
/etc/init.d/supervisord start
7.ベンチマーク実行
[isucon@localhost ~]$ ./benchmarker b 09:52:07 type:info message:launch benchmarker 09:52:07 type:warning message:Result not sent to server because API key is not set 09:52:07 type:info message:init environment 09:52:10 type:info message:run benchmark workload: 1 09:52:29 type:fail reason:Connection timeout method:GET uri:/images/isucon-bank.png 09:52:39 type:fail reason:Connection timeout method:POST uri:/login 09:52:49 type:fail reason:Connection timeout method:GET uri:/ 09:52:59 type:fail reason:Connection timeout method:GET uri:/ 09:53:09 type:fail reason:Connection timeout method:GET uri:/ 09:53:19 type:fail reason:Request cancelled because benchmark finished (1min) method:GET uri:/ 09:53:19 type:info message:finish benchmark workload: 1 09:53:24 type:info message:check banned ips and locked users report 09:54:24 type:fail reason:Request cancelled because benchmark finished (1min) message:Report checking is failed. Do not send score. 09:54:24 type:score success:654 fail:6 score:142
無事pythonで実行できて、スコアも表示されたけど、failばかり…
しばらく調べてみると、pythonのプロセスがおかしいことに気づきました。
$ ps aux | grep unicorn isucon 13124 0.0 1.6 247772 31332 ? Sl 08:25 0:00 unicorn master -c unicorn_config.rb -p 8080 isucon 13154 0.0 2.0 259700 39108 ? Sl 08:25 0:00 unicorn worker[0] -c unicorn_config.rb -p 8080 isucon 13157 0.0 1.9 258840 38304 ? Sl 08:25 0:00 unicorn worker[1] -c unicorn_config.rb -p 8080 …
謎のunicornプロセスが起動しています。
"gunicorn"が正式名称です。
masterプロセスをkillして、再度supervisorで起動します。
$ sudo pkill -f 'unicorn' $ supervisorctl restart isucon_python
ちゃんとgunicornのプロセスが起動しています。
$ ps aux | grep unicorn isucon 16685 0.1 0.7 200628 14740 ? S 10:03 0:00 /home/isucon/.local/python/bin/python2.7 /home/isucon/.local/python/bin/gunicorn -c gunicorn_config.py app:app isucon 16690 0.0 1.0 231740 19476 ? S 10:03 0:00 /home/isucon/.local/python/bin/python2.7 /home/isucon/.local/python/bin/gunicorn -c gunicorn_config.py app:app …
再度ベンチマーカーを実行します。
[isucon@localhost ~]$ ./benchmarker b 10:06:04 type:info message:launch benchmarker 10:06:04 type:warning message:Result not sent to server because API key is not set 10:06:04 type:info message:init environment 10:06:07 type:info message:run benchmark workload: 1 10:07:07 type:info message:finish benchmark workload: 1 10:07:12 type:info message:check banned ips and locked users report 10:07:36 type:report count:banned ips value:1 10:07:36 type:report count:locked users value:2554 10:07:36 type:info message:Result not sent to server because API key is not set 10:07:36 type:score success:5820 fail:0 score:1258
それっぽいスコアになりました!
何もチューニングしないときのスコアは
success:5820 fail:0 score:1258
です。
8.ブラウザからいすこん銀行を表示させる
ベンチマーカーも無事動いたので、ブラウザから画面を表示してみます。
Vagrantfileのポートフォワーディング行のコメントアウトを消して有効にして、hostを8080→3333にします。
config.vm.network "forwarded_port", guest: 80, host: 3333
Vagrantファイルを更新したので再読み込みします。
vagrant reload
これによってホストの特定ポートへのアクセスが、ゲストに転送されます。
ここではホストの3333番ポート宛のアクセスが、仮想マシンの80番ポートに転送されます。
無事表示されました!
9.再プロビジョニングによって設定が上書きされないようにしておく
再度vagrant upしたら、これまで変更した内容が消えてしまうので、
Vagrantfileの下記行を念のためコメントアウトしておきます。
config.vm.provision "shell", inline: <<-SHELL set -e yum install -y epel-release git yum install -y ansible rm -rf ansible-isucon git clone https://github.com/matsuu/ansible-isucon.git ( cd ansible-isucon/isucon4-qualifier PYTHONUNBUFFERED=1 ANSIBLE_FORCE_COLOR=true ansible-playbook playbook.yml -i local ) rm -rf ansible-isucon SHELL