ISUCON5予選 2位で通過しました
ISUCON5予選に@kazeburo、@shmorimo、@cubicdaiya(敬称略)の3人でチーム「GoBold」として参加してきました。
15時過ぎるくらいまではスコアが伸び悩んでいましたが、結果的に2位でフェニッシュすることができました。 以下はスコアの遷移をグラフ化したものです。
GoBoldスコア遷移のグラフ #isucon pic.twitter.com/JKkfjiVJnS
— Shigeki Morimoto (@shmorimo) September 28, 2015
準備と方針
今回は予選に臨むにあたって事前に以下の準備を行いました。
次に事前に軽く打ち合わせして使用言語などの方針を固めました。
- 基本はPerlで行く
- 場合によってはGoやLuaで
- デーモンはsupervisorで管理(systemdかもしれないけどそのときはそのとき)
- 最初はアプリケーションのコードを読んだり、構成を把握するのに時間を使う
また、予選終了後にidobataを見てるとPHPだと思われていたようですが、最終的な構成はnginx + Perl + MySQL + memcachedでした。
まとめ
今回はお題がSNSで日記や足跡、コメント、友達リストなどデータベースを酷使する機能が多く、 ISUCON4の予選の時のようにミドルウェアのチューニングだけで大きくスコアを上げるのは難しそうでした。
また、ベンチマーク実行中にtopを眺めているとMySQLがCPUを3コア以上占有しており、SQLのチューニングが鍵だったことと 初期実装にバグがあるという説明があったのでGoを選択肢から外し、僕以外のメンバーはPerlが得意なので早い段階で以下のような役割分担になりました。
- @cubicdaiya:インフラ周り。主に各種ミドルウェアの設定、ログからアクセスの傾向を探る、ngrepでベンチマーカーの挙動をパケットキャプチャして眺める等
- @kazeburo, @shmorimo:言語はPerl。SQLのチューニングをしていく。必要ならmemcachedにキャッシュする。
なのでこちらのエントリでは僕が主に担当したインフラ周りの話を書きます。アプリケーションやSQLのチューニングの方はあとで@kazeburo先生が書いてくれると思います。
追記:公開されました
ngxtopでログからアクセスの傾向を掴む
ngxtopはnginxのアクセスログをパースして所謂top
みたいなことができるツールです。
これを使ってトップページやログインページ、友達リストページへのアクセスが多いことと、
従来のISUCONと違って静的コンテンツへのアクセスがほとんどないことがわかりました。
また、nginxのアクセスログにリクエスト処理時間が出るようlog_format
に$request_time
を追加しています。
各種ミドルウェアの設定
あらかじめ用意していた各種ミドルウェアの定石設定を適用。supervisor入れたのはsystemdに慣れてなくてそっちにあまり時間を使いたくなかったためです。
あとは時折コード読んで「ここのループで複数回クエリ飛んでて重いので減らしましょう」とか「トップページでまだ100ms以上かかってるのでそっちを速くしましょう」とかPerl書けないのにエラソーに言いながら@kazeburo、@shmorimoの二人を信じてMySQLの負荷が下がるのを待っていました。
nginxのパラメータチューニング
地道なSQLのチューニングが実を結んで15時を過ぎたあたりからスコアが一気に伸び始め、7000点台から一気に18000点台へジャンプすることに成功しました。 また、予選開始直後だとMySQLがCPU使用率が300%を越えていましたが、MySQLのCPU使用率は半分以下になっていました。
この時点でMySQLは大きなボトルネックではなくなったのでnginxのチューニングにも着手しました。
server_tokens off; sendfile on; tcp_nopush on; open_file_cache max=100 inactive=60s; client_body_temp_path /dev/shm/client_body_temp 1 2; keepalive_requests 2000;
そしてappサーバへのリバースプロキシをUnixソケットで行うようにしました。
upstream app { server unix:/tmp/app.sock; }
また、nginxをnginx-buildで最新の1.9.5をビルドして
listen
のパラメータにbacklog=N
とfastopen=M
を指定。合わせてsysctl.conf
の設定も変更しています。(somaxconn
)
listen 80 backlog=32768 fastopen=32768;
あまり効果が無いものも含まれていますが、トータルで2000くらいスコアが上がりました。20000点越えたのも確かこのあたりです。
入念に再起動試験
今回はOSが新しめのUbuntu15.04でデーモンの管理が慣れてないsystemdだったこともあり、いつもより入念に2時間前から再起動試験を行っていました。 もっとも運営の方が追試した際にはFAILしてしまっていたようですが(´・ω・`)
いやー、最初にチェックに通した1日目の上位2チームが続けてチェックに失敗したときはどうしてくれようかと思ったね! (その後いくらなんでもおかしいと詳細を調査した #isucon
— tagomoris (@tagomoris) September 27, 2015
インスタンス放置してたら勝手にアップデートされるのつらい。。。
競技後インスタンス起動してそのままにしておいたら自動updateでkernelの新しいのが入って、それだと起動失敗して/ がread onlyになる。つらい #isucon
— fujiwara (@fujiwara) September 28, 2015
あと、何故か再起動を繰り返す度に(ウォームアップ済みでも)スコアが上下してたので インスタンスガチャかなと思ったのですが、どうやらガチャはないらしいです。
GCEインスタンスはBorgできっちりCPUリソース管理されてるので、ガチャ発生ってたぶんないです #isucon
— Kazunori Sato (@kazunori_279) September 27, 2015
MySQLのウォームアップ
今回はデータベースの容量が大きいのでインスタンスを再起動した直後にそのままベンチマークをかけると Disk I/Oが刺さってスコアが出ないという問題がありました。(具体的にはウォームアップしてないと3000〜4000点ほど下がりました。)
このあたりは@kazeburo、@shmorimoの二人がうまくやってくれてインスタンス起動時にウォームアップするスクリプトを仕込むことで解決しました。
ギリギリまで細かくチューニング
あとはアプリケーション側で地道なチューニングが続いて最終的に25861点でフィニッシュしました。
やり残したこと
アクセスログやtopを眺めていてわかったのですが、今回は例年と比べてベンチマーカーのワークロードが低めで、 スコアが20000点を越えたあたりでCPU1コアあたり30〜40%ほどidleしている状態でした。
また、静的ファイルへのアクセスが少なくベンチマーカーの並列数が少なめだったらしいので前段のnginxを外してすべてのリクエストをPerlのアプリケーションサーバで受けるようにすればリバースプロキシのオーバーヘッドを回避できてもう少しスコア上がったかなと思います。
メルカリに入社して1年経った
昨年の秋にメルカリに入社してから1年経った。今となってはこういう時ぐらいしかブログ書かないだろうし、 せっかくなので入社後1年間でやってきたことをざっくばらんにまとめてみる。
Ansibleのplaybookを書き続ける日々
メルカリで一番書いた量が多いのはYAML・・・もといAnsibleのplaybookだ。二番目がnginx.conf、三番目がtd-agent.confだ・・・多分。以下はplaybookのコミットグラフ。
nginx
メルカリではnginxを多用している。以下その例。
- ZabbixやRedmine、APIのためのリバースプロキシ
- APIエンドポイントのためのTLSターミネーションおよびSPDYゲートウェイ
- L7ロードバランサー(API、検索、プッシュ)
- コンテンツキャッシュ(CSS、JS、検索レスポンス)
- ログ分析基盤用フロントエンド(OpenResty)
- ngx_dynamic_upstreamによるゼロダウンタイムデプロイ
なお、入社した日にはnginxのサーバはまだ1台もなくてちょうど隣の席のエンジニアが APIサーバ群のためのL7ロードバランサーとしてnginxを投入しようとしているところだった。 現在は至る所でnginxが稼働している。
Go
メルカリの主要言語は今でもPHPだが、インフラチームではGoやNode.js、Perlも利用している。 Goは主に自分が書いていて最近書く人が一人増えた。この1年でメルカリのプロダクションに投入したGoプログラムでOSSのものを列挙してみる。
- nginx-build〜nginxのビルドツール〜
- cachectl〜OSのページキャッシュ制御デーモン〜
- slackboard〜Slackプロキシ〜
- gaurun〜プッシュ通知プロキシ〜
エンジニアブログで実際の活用事例について書いている。
tech.mercari.com tech.mercari.com tech.mercari.com tech.mercari.com
あと、最近だとPHPのジョブワーカーの一部をGoで書き直すプロジェクトが進行している。
ゼロダウンタイムデプロイの実現
よく知られているようにダウンタイムなしでWebアプリケーションをデプロイするのは意外と面倒で難しい。 メルカリのデプロイはただのrsyncだが、各サーバへのrsyncの前後でngx_dynamic_upstreamを利用して rsync対象のサーバをロードバランサーから動的に取り外しおよび復帰させることでダウンタイムなしでデプロイできるようにした。
ちなみに実際の仕組みの導入自体はほぼ@kazeburoさんがやった。 そのうちMercari Engineering Blogで取り上げられるかもしれない。
ログ分析基盤
メルカリではログ転送にFluentdを利用していてこのあたりも今は自分がメインでやってる。
メルカリに入社する以前は本格的にFluentdを利用したことがなかったので結構苦労したけど、大分慣れてきて今は自分でプラグインを書いたりもしている。
先月は社内用のOutputプラグインをFilterプラグインで書き換えて遊んでいたチューニングしていた。
また、社内でPascalと呼ばれているログ分析基盤の構築と運用もやってる。
フロントエンドはngx_luaで開発している。
PHP5.3から5.6への移行
メルカリのAPIサーバはPHPで書かれている。長らく5.3を使っていたが今年になってようやく5.6に移行した。 某イラストSNSのインフラチームにいた頃にPHP5.2 -> 5.3 -> 5.5と2回大きなアップデートをメインで担当したので実務でのPHPの大型アップデートはこれで3回目になる。
PHP5.3 -> PHP5.6(左:メモリ、右:レスポンスタイム) pic.twitter.com/y9jPLW9kvx
— bokko (@cubicdaiya) July 31, 2015
来年は多分PHP7の作業が待っている。
会社の技術広報活動
メルカリではエンジニアを絶賛募集中なので宣伝も兼ねて各所でスピーカーとして登壇した。
- 1000万ダウンロードアプリ『メルカリ』を支える技術
- High Performance Backend For Mercari
- 実践nginx〜メルカリの場合〜
- Gaurun / A general push notification server in Go
- Practical nginx module development〜C and Lua〜
- etc...
まとめ
昨年は色々とあって辛い時期もあったけど、今は元気です。あと重要なことなのでもう一度言うとメルカリではエンジニアを絶賛募集中です。
YAPC::Asia 2015でnginxのモジュール開発について発表してきました
YAPC::Asia 2015で「Practical nginx module development〜C and Lua〜」というタイトルで発表してきました。
Practical nginx module development〜C and Lua〜
マニアックなネタなので「聞きに来てくれる人いるかな」とちょっと不安だったのですが結果的に満員で立ち見も出るくらいの方々にお越し頂きました。
今回はCとLuaによるnginxのモジュール開発の仕方について解説したのですが、個人的に若干Cの部分がボリューム不足でライブコーディングみたいな形でいいから一旦ターミナルを開いて即席でソースコードリーディングみたいなことをすればよかったかなぁと少し反省。
時間も5分ほど余ってしまって質疑応答の時間が15分になりましたが、結果的に時間いっぱいギリギリまで質疑応答が続く盛り上がりで非常に楽しい時間を過ごすことが出来ました。
本当にありがとうございました。