dropooiの日記

Linux/クラウド/スマホ/IoT関連の情報集め

サバフェス参戦ログ

IDCフロンティア主催のサバフェスに参戦して来ました。
twitter
レギュレーション

チーム名「DOM」
Dropsystemの僕(大井)と同僚の南の頭文字で適当に決めたチーム名です。

「最速インフラを構築せよ!!!
WordPressに一切手を加えずに、どこまで高速化できるのか!?
OSチューニング、サーバチューニング、負荷分散…最適解を探せ!」

Noahセルフクラウドサーバのサーバを最大5台まで使って最速のwordpress環境を構築せよ!という事でした。
WordPress自体には一切触れては行けない(プラグインもだめ)
DBはMySQL、webサーバはApacheかnginxでStable以外のミドルウェアは使用禁止。
wordpressをスキップするような事もだめ。ただし、キャッシュエンジンの利用はOK。
GETとPOSTのスコアでランキングする。
作業はリモートで可能で、猶予時間は2013年11月18日(月)10:00 〜 2013年11月25日(月)09:00まで1週間有りました。

ベンチマークツールは非公開でしたので、想定でGET/POSTをランダムに、投稿、コメント、参照が入り乱れ、投稿してすぐにGETが来て、反映されていないとFail、というものかなと考えていました。
(単純なキャッシュが効かないようなイヤらしいアクセスをしてくるのだろうと)
※実際はGETでTOPページを1分、POSTでid=5のページに2分程度同じコメントを大量POSTする仕様でした)

WordPress自体はほとんど触った事が無かったので事前にインストールして管理画面等の動作を見ておきました。
というのも、結局は参照系をどれだけキャッシュ出来るか、キャッシュをどれだけ効率的にBAN出来るかが鍵と思っていたからです。
しかし見てみると更新系はPOST以外にもAjaxがあったりGETでもコメントの許可などしているような更新系があったりと、ソースを見る気が失せて来ましたので調査は一瞬で断念しました。(問題先送り)

1日目
参加登録したまま放置していたら当日朝にサーバアカウントの連絡来ず。
しかし、起きたのが昼過ぎだったので問題無し。
担当の方がメールアドレスを探してくれて夕方にアカウントを教えてくれました。
有り難う御座います、ちょうど良かったです。

ただし、初日に1台は構築しておかないと最初のベンチが来ないようだったので急いで最小構成の構築を始めました。
ビールをしばきながら頑張りました。

元々考えていた構成は下記どちらか(画像とかたくさんアクセス来るなら、と思って結局Varnish構成にしておきました)
Varnish > app > mysql
LVS > app > mysql

webサーバはnginxでもapacheでもいいやと思っていました。
(getはほとんどvarnishで返す予定だし)
台数が限られているので全部入りサーバ5台で並べた方が速いかとかも考えましたが時間が有ればという事にしました。

基本的なサーバ構築ですが、ユーザーとか環境作るの面倒なので
ARRAY=(ooi minami)
KEYS=('ssh-dss aaaaaaa' 'ssh-dss xxxxxxxx')
i=0;
for item in ${ARRAY[@]};
do
adduser $item -g dev;
mkdir /home/$item/.ssh;
echo ${KEYS[$i]} > /home/$item/.ssh/authorized_keys;
chmod 700 /home/$item/.ssh;
chmod 600 /home/$item/.ssh/authorized_keys;
chown -R $item:dev /home/$item;
i=$( ( $i+1 ) )
done
など、ずるして一気にチームの2ユーザー作成しました。

これがいけなかった。
サバフェスではwordpressの改変が一切禁止で、その確認にchecksumを使うんだけど、合わない。
よく考えたら、運営の確認コマンドは
cd /home/csf ; tar cf – wordpress | md5sum
tar圧縮したユーザーのUID/GID次第で変わってしまうじゃないか。
チュートリアルを見てなかったので無駄に2時間くらい消費。
ユーザーを作る順番が重要でした。
csfユーザのUIDを501に入れ替えて何とか一致。
これもどうかと思いましたが、ここで一定の達成感を味わいました。
もう帰ろうかと葛藤しましたが、アクセスログは欲しいので頑張って踏みとどまりました。

varnish+(nginx/php-fpm)+mysqlの三台構成
チューニングは特にせず、GETは1分キャッシュ、POSTはpass、nginxはキャッシュせずそのままphpに処理を渡すだけ。
php-fpmはtcpではなくソケットにしておきました。
mysqlはデフォルトのmy.cnf状態。

とりあえずabでベンチして想定通りなので帰りました。

インストールメモ
全部yumでさくっと入れる

Varnish
rpm --nosignature -i http://repo.varnish-cache.org/redhat/varnish-3.0/el6/noarch/varnish-release/varnish-release-3.0-1.el6.noarch.rpm
yum install varnish

PHP
yum install --enablerepo=remi --enablerepo=remi-php55 php.x86_64 php-fpm.x86_64 php-mbstring.x86_64 php-mysqlnd.x86_64 php-opcache.x86_64 php-pecl-xdebug.x86_64

MySQL
yum -y install --enablerepo=wing mysql56.x86-64 mysql56-server.x86_64 mysql56-bench.x86-64 mysql56-devel.x86_64 mysql56-libs.x86_64

カーネルのチューニングも特にしていない。


2日目〜

あんまり覚えてない。。申し訳なし。

確か最初の計測は3位くらいでした。
意外とまあまあな結果にちょっとやる気が出ました。

最初8万くらいだったGETスコアですが、3日目くらいにログを見ていたらレスポンスサイズがあきらかにGZIP圧縮されておらず、もしやと思い、
VarnishのアクセスログにAccept-Encodingを記録するようにしました。
やはりベンチマークツールはAccept-Encodingを付けていない。
文献が怪しいですが
RFC的には
「Accept-Encoding 欄が要求中に示されていない場合、 サーバーはクライアントがどの内容符号かも受け入れると仮定しても構いません。」
とあるので、AcceptEncodingが無い場合は上位サーバ(varnish > nginx)にAccept-Encoding: gzipを強制付与するようにしました。

これでGETスコアが8万から18万くらいになりました。
この時点で短くも暫定1位になりましたので満足して帰宅した気がします。
(スクリーンショット取っておけば良かった。。)

他の参加者さんのPOSTが200万とか行ってる
何が起きた、と思いました。
もしや302返してるだけ?それはずるいと思い、一度だけでもそのスコアを味わいたくvarnishでPOST時に302 Foundを返すように設定。
楽しみにベンチを待ちます。
来た!
ベンチが終わるとすぐにリザルトサイトを確認しました。
POST … 0点!
なんという事でしょう。
ちょうどベンチツールを改修したようです。
ちっぽけな自分に憤慨しました。

コメントページがタイムアウト
POSTのスコアが実際に投入されたコメント数の1/3程度しか出ていない。。
どうもコメントが1万件とか超えるとベンチマークツールが最後にPOSTされているかチェックしに来る際にタイムアウトしているようだ。
php自体のout of memoryなども出ていたので設定修正。

※設定ファイルを全くバックアップしていなかったのでかなり怪しい。
最終集計後でもいいからもう一度サーバにアクセスさせて欲しいです。。
こんな感じだったかな。

php-fpm www.conf
rlimit_files = 10240
rlimit_core = unlimited
request_terminate_timeout = 0

php.ini
max_execution_time = 0
max_input_time = -1
memory_limit = 1024M

nginx
worker_rlimit_nofile 10240;
location ~ \.php$ {
try_files $uri =404;
fastcgi_pass unix:/var/run/php-fpm.sock;
fastcgi_index index.php;
fastcgi_connect_timeout 120;
fastcgi_send_timeout 600;
fastcgi_read_timeout 600;
include /etc/nginx/fastcgi_params;
fastcgi_param PATH_TRANSLATED $document_root$fastcgi_path_info;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}

5日目〜
他のチームがGETもPOSTも盛大にブレークスルーして来た。
うちは5位前後を彷徨う感じ。
もうちょっと頑張ろうかと思い、sysctlのチューニング(大して考えず全台に設定)

vi /etc/sysctl.conf
net.ipv4.ip_local_port_range = 1024 65536
net.core.rmem_max=16777216
net.core.wmem_max=16777216
net.ipv4.tcp_rmem=4096 87380 16777216
net.ipv4.tcp_wmem=4096 65536 16777216
net.ipv4.tcp_fin_timeout = 3
net.core.netdev_max_backlog = 30000
net.ipv4.tcp_no_metrics_save=1
net.core.somaxconn = 262144
net.ipv4.tcp_syncookies = 0
net.ipv4.tcp_max_orphans = 262144
net.ipv4.tcp_max_syn_backlog = 262144
net.ipv4.tcp_synack_retries = 2
net.ipv4.tcp_syn_retries = 2

sysctl -p /etc/sysctl.conf

※CentOS6だと下記エラーが出る
error: "net.bridge.bridge-nf-call-ip6tables" is an unknown key
error: "net.bridge.bridge-nf-call-iptables" is an unknown key
error: "net.bridge.bridge-nf-call-arptables" is an unknown key

echo $?
255
失敗
どうやらCentのバグのようだけど、とりあえずロードする
modprobe bridge
sysctl -p /etc/sysctl.conf
ok
まあいいか、rc.localにでもbridgeロードを仕込んでおいた。(やっつけ)

net.core.somaxconnのチューニングは実際にGET性能向上を感じ、18万くらいのGETスコアは20万を超えた。
でもPOSTは最大でも1万5千弱。
このあたりでもたまに1位を取った瞬間は有りました。(嘘くさいのでスクリーンショット取っておけば良かった。)

しかし、上位チームは2万超えたり5万くらいのPOSTスコアが出て来た。

俺はもうだめだ。
でも、どうやっているんだと純粋に興味津々。

※競技終了後にブログで公開してくれてました。
よこになりたい
なるほど。
nginx+Luaで直接MySQLにinsertか。。
その手が有ったか、けしからん!!
自由な発想に激しく嫉妬。

この時点の構成は
varnish x 1
nginx+php x 3
mysql x1

当初通りで面白くも何とも有りません。
優勝者のサーバをNoahセルフのwordpressテンプレにするという話だったので一応真面目に使える設定していました。(言い訳)
VarnishのVCL設定はadmin系はキャッシュしないようにしたり、POSTされたらキャッシュをBANするようになるべくWordPressで実際に使えるように設定していました。(ベンチ的には意味ないですけど)

MySQLは途中でInnodbに変更したり、mysql-cluster(vanish以下4台にインスト)にしたりしましたが、MyIsam一台に勝てず断念。
唯一、BlackHoleエンジンはさすがに2、3倍の性能は出たけどmysqlprpoxyがアルファ版でありレギュレーション上使えないのと、autoincrementが使えないためこれも断念。

参考
varnish起動オプション
キャッシュストレージはメモリオンリーに変更。

VARNISH_STORAGE_MEM="malloc,4G"
DAEMON_OPTS="-a ${VARNISH_LISTEN_ADDRESS}:${VARNISH_LISTEN_PORT} \

  • f ${VARNISH_VCL_CONF} \
  • T ${VARNISH_ADMIN_LISTEN_ADDRESS}:${VARNISH_ADMIN_LISTEN_PORT} \
  • t ${VARNISH_TTL} \
  • w ${VARNISH_MIN_THREADS},${VARNISH_MAX_THREADS},${VARNISH_THREAD_TIMEOUT} \
  • u varnish -g varnish \
  • S ${VARNISH_SECRET_FILE} \
  • p thread_pool_min=200 \
  • p thread_pool_max=4000 \
  • p thread_pool_add_delay=2 \
  • p session_linger=100 \
  • p cli_timeout=20 \
  • s st1=${VARNISH_STORAGE}"

VCLは設定を保存していなかったので手元に無い。
やった事と言えば、POSTはpipeでバックエンドに丸投げ。
GETは画像とかJS、CSSとか細かく区切ってTTL設定していたけどベンチ上、実際意味無し。
phpは60秒のTTLとし、POST時に全キャッシュをBANリストに保存するようにした。(WordPressの更新箇所や影響範囲が分かればもっと細かくしたいところだったが。)

デバッグに使ったもの
xdebug
cachegrindファイルを吐き出させるようにする。
php.ini
xdebug.auto_trace = 1
xdebug.profiler_enable=1
xdebug.trace_output_dir = /tmp/svfes.xdebug

xdebugtoolkitでグラフ生成
scpでロールマシンにsvfes.xdebugをコピー
for i in `find . -regex "\./svfes.*" `; do fix-profile.sh $i;done
for i in `find . -regex "\./svfes.*\.fixed" `; do ~/work/xdebugtoolkit/cg2dot.py $i | dot -Tpng -o$i.png;done

その時のグラフ。

xdebugボトルネックを確認するとwordpressの日本語化モジュールあたりの読み込みで7割くらい食っていた。
wordpressの改変は出来ないし、opcacheも入れてるし、wordpress自体のチューニングは無理と判断。

sysbench
mysql単体のチューニング
これだとInnodbの方が性能が出る。
wordpressのシナリオでテスト出来ないと意味無しと悟る。

ApacheBench
ab -n 10000 -c 100 -p post_data.txt -T "application/x-www-form-urlencoded" http://xxx.xxx.xxx.xxx/wp-comments-post.php
ひたすらabとvmstatでアタック掛けまくり。
jmeter使おうと思っていたけどベンチツールがhtml以外来なくて無駄だし重いのでやめました。

外部環境からPOST 1000件
ab -n 1000 -c 100 -p post_data.txt -T "application/x-www-form-urlencoded" http://xxx.xxx.xxx.xxx/wp-comments-post.php
Requests per second: 172.35 [#/sec] (mean)
Time per request: 580.213 [ms] (mean)
Time per request: 5.802 [ms] (mean, across all concurrent requests)
Transfer rate: 114.60 [Kbytes/sec] received
47.53 kb/s sent
162.13 kb/s total
大体こんなもんで変わらず。。これ以上出ない。がっかり。

結果はどうあれ、nginxやvarnishの設定、カーネルのチューニング等見直す機会をもらえて大変良かったです。
wordpressはデフォルトくそ重いです。

やっておけば良かった事)
もうちょっとwordpressのクエリ見ておけばよかった。(クエリログは一度出してふーんと思った程度)
インデックス調整が足りなかったかな。
LVS(DSR構成)でGZIP圧縮かませばもっとGET性能出てた気がする。(ねこのてさんはもっと性能出てたはず)
chefを最初から使う予定だったけど、なあなあで使わず終わった。
(アラキンさんのansible playbooksというのは知らなかった。良さそうな気がする。)
phpのチューニング自体はopcache使った意外あんまりやってない、というか大して変わらなかった。もう少し頑張ってみれば良かった。

nginx+luaは面白そうなので実務で強引(密かに)にやってみたいと思いました。

IDCフロンティアサバフェス運営の皆様、有り難う御座いました。
とても充実した1週間だった気がします。

android横幅配列

html
androidの横幅がdpi指定時等、うまく取れない場合
とりあえず現時点(2013/2)の大体の、端末名=>横幅配列

var android_arr={"HW-03E":720,"N-05E":540,"L-04E":1080,"F-02E":1080,"SO-02E":1080,"SH-05E":540,"N-04E":720,"P-02E":1080,"SH-04E":720,"F-03E":540,"L-02E":720,"N-03E":720,"SC-03E":720,"N-02E":480,"SH-02E":720,"F-04E":720,"HW-01E":720,"SC-02E":720,"SO-01E":720,"L-01E":720,"SH-01E":540,"N-08D":800,"L-06D":768,"T-02D":540,"L-05D":480,"F-09D":480,"P-04D":540,"P-05D":540,"SH-04D":540,"SO-01D":480,"F-12C":480,"SO-01B":480,"SO-02C":480,"SC-02B":480,"SO-01C":480,"SC-01C":600,"SH-03C":480,"SO-02C":480,"T-01C":480,"N-06C":480,"SO-04D":720,"P-07D":720,"T-01D":720,"SO-03D":720,"SH-10D":720,"SH-09D":720,"SH-06D":720,"SH-01D":720,"SC-06D":720,"P-06D":720,"N-07D":720,"N-05D":720,"N-04D":720,"L-01D":720,"F-10D":720,"F-08D":720,"F-05D":720,"SO-05D":540,"SH-13C":540,"SH-12C":540,"SH-02D":540,"SH-01EVW":540,"P-02D":540,"WX04K":480,"SO-03C":480,"SC-03D":480,"SC-02C":480,"P-07C":480,"P-01D":480,"N-04C":480,"N-02E":480,"N-01D":480,"L-07C":480,"F-07D":480,"F-03D":480,"ISW11HT":480,"ISW11F":720,"ISW13F":720,"ISW13HT":540,"IS03":640,"HTL21":1080,"KYL21":720,"FJL21":720,"PTL21":720,"SOL21":720,"SHL21":720,"SCL21":720,"CAL21":480,"LGL21":720,"IS17SH":540,"IS15SH":540,"ISW16SH":720,"PROGRESSO":480,"IS12S":720,"IS12M":540,"INFOBAR C01":480,"ISW11SC":720,"IS11LG":480,"IS12F":480,"INFOBAR A01":540,"ISW12HT":540,"ISW11M":540,"IS14SH":540,"IS13SH":540,"IS12SH":540,"IS11SH":540,"ISW11K":480,"IS11T":480,"IS11S":480,"IS11PT":480,"IS11N":480,"IS11LG":480,"IS11CA":480,"IS05":480,"IS04":480,"203SH":720,"201K":480,"201M":540,"201HW":540,"101F":540,"107SH":480,"102P":540,"101DL":540,"101K":480,"103SH":540,"101N":480,"101P":540,"009Z":480,"101SH":480,"007HW":480,"009SH":540,"008Z":480,"003P":480,"007SH":480,"006SH":540,"005SH":480,"003Z":480,"003SH":480,"X06HT":480,"GS03":540,"GS02":480,"S51SE":320,"200SH":720,"106SH":720,"104SH":720,"102SH2":720,"102SH":720,"DM012SH":540};

$.each(android_arr, function(i,val) {
re=new RegExp(i,"i");
if(window.navigator.userAgent.match(re)){
scr_w=val;
return false;}
});

ソーシャルアド関連のお勉強

えらい人達の色々な記事を読んで勉強中。
インタレストグラフやらソーシャルグラフやらは概ね分かっていたつもりだったけど、
ターゲティング配信の最適化に必要なデータマイニングが理論も数式も難しい。
数式は正直理解出来そうもないので、こういうのが必要というところだけピックアップ出来るようにしておこう。