2017年度 新卒技術部研修 〜社内ISUCON参加編〜

はじめまして、技術部新卒の池田です。

毎年、カヤックの技術部の新卒研修では、研修の最後に『社内ISUCON』を行います。
このエントリーでは、今年の研修最後の社内ISUCONについて新卒参加者サイドから振り返ります。

社内ISUCONのお題とチームについて

ISUCONのルールとしては与えられたサーバーで稼働しているWebApplicationを『いい感じにスピードアップする』という競技で、競技時間は10:00 ~ 17:00の間の7時間です。

お題

今年の社内ISUCONのお題は、ボケてlikeな JokerというWebApplicationです。

  • ボケて(bokete)  クローン
  • RubyのSinatraで作成
  • DBにはMySQLを使用
  • KVSにはRedisを使用
  • ログイン・ログアウトがある
  • 画像のUpload機能がある

等、WebApplicationの基本のたくさん詰まったお題で、 私含む新卒社員達は事前に2週間の研修で取り扱ってたものです。

チーム編成

社内ISUCONの開催前に、事前にチーム編成の発表が有りました。
チームは2人1組で組まれており、先輩チームが3チームと新卒チームが7チームでした。
また、チーム編成発表と同様に、各チームに渡されるサーバーの構成の発表もありました。 実際のチーム編成は下の表のようになってます。

チーム番号 チーム名 新卒  構成
1 チームイケイケ   8コア1台
2 チームせりまち   8コア1台
3 チームイシビア   8コア1台
4 チームすずきヴォン   8コア1台
5 チームダリかわら    8コア1台
6 チームさるたに     8コア1台
7 チーム新卒のフリ    8コア1台
8 チームmixed contents 2コア4台
11 模範解答チーム 8コア1台
12 チーム実力主義 2コア4台

11番の模範解答チームは、講師の先輩のチームで新卒チームと同じ構成でリアルタイムに模範解答をいただけるそうです
ちなみに私はチームイケイケで、 相方は同じく新卒の入江でした。

社内ISUCON開始。与えられたWebApplicationは・・・

10:00に開始の合図とともに、
『まず1度ベンチマークをやってみましょう。』 という案内がありました。
その結果はスコア0
『では、できたチームはトップページを開いてみてください。』
f:id:ikeda-masashi:20170713173632p:plain

Oh…504! まともに開けないものでした。

f:id:ikeda-masashi:20170713173809p:plain

Oh…ループクエリ! 100+1回クエリーが発行されてると言う状態でした。
最終的には、このループクエリどうにかしないといけないですね。

はじめに取り組んだのは開発環境構築

さて、一通りの説明が終わり各チームが作業をはじめました。
30分後にはスコア1000以上のチームが出てきました。
周りからは『ここにこうやってINDEXを張ってどうだ!』みたいな声が聞こえたりします。

そんな状況の中、私達チームイケイケがやっていたのは環境構築でした。 手順のダイジェストは以下です。

1. サーバーへログインするための鍵を作る

(server)$ssh-keygen -t rsa

2. 現在のアプリケーションをGit管理化

(server)$ cd ~app/joker
(server)$ git init

3. 別のディレクトリでbereリポジトリ作成

(server)$ cd ../
(server)$ mkdir joker.remote.git
(server)$ cd joker.remote.git
(server)$ git clone --bare ~app/joker

4. bereリポジトリをリモートリポジトリとしてローカルにクローン(ssh経由)

sshでログインする設定を終えてから(ssh app_ikeikeでログイン可能に) 

(local)$ git clone ssh://app_ikeike/~/joker.remote.git

5. ローカルに開発用の環境を構築

(local)$ bundle install --path vender/bundle

6. ローカルで簡易ベンチマークテストをできるようにする

(local)$ brew install ab
(local)$ bundle exec rackup config.ru -p 5000
(local)$ ab -n 100 -c 100 http://localhost:5000/

7. bereリポジトリにHook用のスクリプトを追加し、デプロイ自動化

(server)$ cd ~app/joker.remote.git/hooks/
(server)$ vim post-receive
(server)$ chmod +x post-receive

post-receiveの中身にはservice restart jokerとか入れて、更新反映に必要な処理を入れていました。
appのユーザーの権限とか、post-reciveをtypoしてとかで色々トラブルが発生しつつも
ローカル環境でのベンチマークテストとPushで自動更新が出来るようになりました。
ここまで、1時間30分かかりお昼前後で『お腹が空いたぽ〜』の状態になってました。
その間のチームイケイケはもちろんスコア0の状態でした。
先輩方から『チームイケイケ元気ですか?』と心配される状態で前半戦を終了しました。
この時点でTop(ただし1位の模範解答チームを除く)は8000点超え。自分たちは0点のまま。

後半戦。巻き返しの肝は・・・

『ループクエリどうにかしよう!』
相方はフロントサイドエンジニアで、テンプレートのerbファイルを編集し始めました。
自分はサーバーサードエンジニアなのでアプリのapp.rbを編集し始めました。
その結果は動かなくなりました。 (原因はindex.erbapp.rbの変数名の不一致)

相方と話し合い、意思疎通して同時に編集するのは難しいと悟った私たちは方針を変えました。 * 相方は研修で習ったチューニングを本番サーバーでひたすら実施していく。 - unicorn.rbworker_processesをあげる。

pid "joker.pid"
listen "127.0.0.1:8080"
worker_processes 32
stderr_path "stderr.log"
stdout_path "stdout.log"
  • アプリケーションサーバーに渡されている静的ファイルの場所を変える。
  • /etc/init.d/joker内の一文を export RACK_ENV = productionに修正する。
  • mysqlinnodb_buffer_pool_sizeを増やす。
  • mysqlmax_prepared_stmt_countを増やす。
  • 自分はアプリケーションのコードやクエリ関係をどうにかしていく。
    • TOPページの激重100+1クエリをINNER JOINでつなげて、テンプレートからのクエリ呼び出しを消し去る。
    • Indexを張る。

f:id:ikeda-masashi:20170713173831p:plain

完全並行作業で相方が何をやっていたのかは正直把握してないのですが、
Topページのループクエリを消し去るだけで、2位との差は2000点くらい。 ( 模範解答チームはこの時点で同じ時間で18000点くらいで、2位と6000点くらいの大差をつけていました。) Topページのループクエリを消した後は他のページのチューニングをしていました。
チューニングしたら必ずローカルベンチマークを回して、スコアが下がる修正は未然に防ぎました。
INNER JOINしすぎかな?と思い2クエリに分けてみたりしたのですが、
クエリ回数が増える修正は、確実にスコアが落ちるようです。

実際にサーバーに反映してベンチマークする場合、
データがたくさん詰まった重い状態かつ他のチームとのキュー入れ競争なので、そこそこ時間がかかります。
ローカルでベンチマークできることで効率的にチューニングが進められました。

アプリケーションのコードの変更反映もgit pushだけスムーズに変更を反映できました。
前半戦の下準備が、実った瞬間です。

まとめ

今回社内ISUCONに参加して学んだのは、3つ。

  • 開発環境って大事ですね。
  • 意思疎通って難しいですね。
  • ループクエリはパフォーマンスの敵

カヤックでは一緒にgit logを綴るエンジニアも募集しています!