Hateburo: kazeburo hatenablog

SRE / 運用系小姑 / Goを書くPerl Monger

ISUCON4 予選でアプリケーションを変更せずに予選通過ラインを突破するの術

AMIが公開されたのでもう一度やってみた。

AMIについてはこちらのエントリに書かれています

ISUCON4 予選問題の解説と講評 & AMIの公開 : ISUCON公式Blog

まず ami-e3577fe2 を m3.xlargeで起動します。

CPUは

model name      : Intel(R) Xeon(R) CPU E5-2670 v2 @ 2.50GHz

でした。

とりあえず、MySQLのindexを追加する。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

ベンチマークツールのhttp keepaliveが無効なので、sysctrl.conf でephemeral portを拡大したり、TIME_WAITが早く回収されるように変更する

$ 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

ついでに、backlogも増やしている。

適用は

sudo /sbin/sysctl -p

で行う。

セッション管理にmemcachedを使うのでいれる

sudo yum install -y wget libevent-devel perl-devel
cd /tmp
wget http://www.memcached.org/files/memcached-1.4.21.tar.gz
tar zxf memcached-1.4.21.tar.gz
cd memcached-1.4.21
./configure --prefix=/usr/local/memcached && make && sudo make install

Supervisord経由で起動

[program:memcahed]
directory=/
command=/usr/local/memcached/bin/memcached -p 11211 -U 0 -u nobody -m 256 -c 200000 -v -t 1 -C -B ascii
autostart = true

memcachedの起動オプションについてはこのエントリが詳しい

memcached おすすめ起動オプションまとめ - blog.nomadscafe.jp

次に、Perlにアプリケーションを変更し、起動方法も変更

まず、cpanfileに以下を追加してcarton install

$ cat cpanfile
requires "Kossy", 0.38;
requires "Starlet", 0.24;
requires "URL::Encode::XS";
requires "Plack::Middleware::Session::Simple";
requires "Cache::Memcached::Fast";
requires "Sereal";
$ carton install

アプリケーションはStarletをつかって起動。その際にunix domain socketをlistenする

[program:isucon_perl]
directory=/home/isucon/webapp/perl
command=/home/isucon/env.sh carton exec start_server --path /dev/shm/app.sock --backlog 16384 -- plackup -s Starlet --workers=4 --max-reqs-per-child 500000 --min-reqs-per-child 400000 -E production -a app.psgi
user=isucon
stdout_logfile=/tmp/isucon.perl.log
stderr_logfile=/tmp/isucon.perl.log
autostart=true

Starlet + unix domain socketの話はこちらのエントリ

Starlet + Server::Stater で UNIX domain socketに対応しました - Hateburo: kazeburo hatenablog

セッションまわりだけ、PlackのMiddlewareを入れ替える

use Cache::Memcached::Fast;
use Sereal;

my $decoder = Sereal::Decoder->new();
my $encoder = Sereal::Encoder->new();
my $app = Isu4Qualifier::Web->psgi($root_dir);
builder {
    enable 'ReverseProxy';
    enable 'Session::Simple',
        store => Cache::Memcached::Fast->new({
            servers => [ { address => "localhost:11211",noreply=>0} ],
            serialize_methods => [ sub { $encoder->encode($_[0])}, 
                                   sub { $decoder->decode($_[0])} ],
        }),
        httponly => 1,
        cookie_name => "isu4_session",
        keep_empty => 0;
    $app;
};

ここで使っているPlack::Middleware::Session::Simpleはこのエントリで紹介しています

Plack::Middleware::Session::Simple has been released - Hateburo: kazeburo hatenablog

そして、nginxの設定変更

$ 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;
    }
  }
}

cssや画像をnginxから配信するのと、アプリケーションサーバunix domain socketになったのでその切り替え

nginxの設定はこのエントリが参考になるかな

G-WANはなぜ速いのか?をnginxと比べながら検証してみた - blog.nomadscafe.jp

最後にmysqlの設定

$ cat /etc/my.cnf
innodb_buffer_pool_size = 1G
innodb_flush_log_at_trx_commit = 0
innodb_flush_method=O_DIRECT

この3つだけ追加

全ての設定ができたので、mysql、nginxとSupervisordを再起動

$ sudo service mysqld restart
$ sudo /usr/bin/supervisorctl reload
$ sudo service nginx restart

これで準備完了。

いよいよ、benchmarkを動かしてみる

$ ./benchmarker bench --workload 8
07:51:41 type:info      message:launch benchmarker
07:51:41 type:warning   message:Result not sent to server because API key is not set
07:51:41 type:info      message:init environment
07:51:50 type:info      message:run benchmark workload: 8
07:52:50 type:info      message:finish benchmark workload: 8
07:52:55 type:info      message:check banned ips and locked users report
07:52:57 type:report    count:banned ips        value:605
07:52:57 type:report    count:locked users      value:4384
07:52:57 type:info      message:Result not sent to server because API key is not set
07:52:57 type:score     success:188930  fail:0  score:40812

見事予選突破ラインの37,808を、アプリケーションのコードを変更せずに超える事に成功しました。ぱちぱちぱち

// 追記