ISUCON6 にインフラエンジニアとして参加して優勝した!!
ISUCON6 にインフラエンジニアとして参加して優勝してきたので、なにやったかとか感想とか書いてく。 (僕はアプリを触っていない。アプリの話は他の人が書いてくれるはず。)
※ @methane さんが会社ブログに投稿されたので記事を更新
会社で出場してチーム名は「この技術部には問題がある!」、メンバは @methane @mecha_g3 さんと僕。 ちなみに @methane さんと @mecha_g3 さんは兄弟だけど、僕だけ他人です(笑)。
チームメンバ役割
本戦に向けて、会社でチーム練習してたので本戦の役割もできてた。
@methane さんが全体の構成を見てチーム全体をまとめる役割、 @mecha_g3 さんがアプリ、 僕がインフラの役割。@methane さんが戦略をたてて、各自自分で改善できるところは改善してく感じで進めることができた。なので、自分は結局アプリのコードを読まなかったけど、やるべきことはできたのでチーム構成最高だった!!
当日の朝
自分は朝に余裕を持ちたいので、当日は 6 時起床。前日は特になにもせずに寝ることだけに集中してた。
LINE きた 2 年ぶり pic.twitter.com/cXShqDV0OV
— ※遺伝子検査済み (@kizkoh) October 22, 2016
チームでは一番に到着したので、席確保して @methane さん、 @mecha_g3 さんがくるまで twitter 見ながらだらだら過ごす。落ち着きが大事。
競技開始
他のメンバにレギュレーションや構成を見てもらっている間にコードや公開鍵をデプロイしたり、オペレーションに必要なツールやリソースモニタリングに必要なパッケージを流し込んでいったりする。 今回は他のサーバ上からオペレーションできるよう踏台サーバとして Azure 外にサーバを置いていて、アプリ担当のメンバが本戦サーバにログインせずともアプリをデプロイしたりログを収集したりできるスクリプトを整備していた。 さらにその準備をしている片手間で初期状態のベンチをかける。
3500 点くらい。
ここでスタートの準備が整ったので戦略会議をする。
フロントは https で nodejs(react) のアプリケーションが待ち構えていて、バックエンドにアプリ、その後ろに MySQL がある構成だった。
nginx を(ちゃんとした http サーバを立てたくて)使いたいので、フロントを nginx で受けて nodejs とアプリに nginx 分けようという戦略になって、これが最終提出時の構成にもなった。
初期実装は php で動いてて、プロセスに apache がいてこれを切り替えようとして systemctl list-units
するも apache2.service がなくて焦るが、ps
で見ると -DFOREGROUND
で動いていたので、 あぁ docker で動いているんだなと思って docker ps
したら起動中のコンテナ一覧が表示されてホッとした(レギュレーションのスライド読んでなかった)。
起動中のコンテナ一覧には node や MySQL があって、各サービスも docker-compose の docker で動いていた。
これらは Linux ネットワークスタックの処理をいくつか通過するためネットワークパフォーマンスが悪くなるし、何よりも問題が発生したときに追うのが面倒という理由で外だししていくことにした。
@methane さんに nodejs と MySQL のインストールを手伝ってもらいながら、 docker exec
しつつ中身を一通り調査しながら systemd のサービスを作成していった。
すべてのコンテナを潰し、 nginx で 443 を listen するようにして、ベンチかけたらアクセスログで HTTP/2.0
が記録されてたので予想通り!!
9500 点くらい。
ここで 12 時半くらいだったと思う。
/api
へのアクセスはアプリだったのでアプリに飛ばしたり、静的ファイルの配信を nginx で gzip_static
を使って配信するようにしたり、 /api/stream
の proxy_buffering
を off にして 14 時くらい。
16000 点くらい。
たぶん、ここまでアプリのコードは触られていなかったんじゃないかと思う(うろ覚え)。 だいぶ落ち着いてきたのでアクセスログの集計スクリプトを整備していく。
@mecha_g3 さんがデータをオンメモリ化されたり、 @methane さんに img/
の SVG を go で返すようにしてもらったりして、設定変更した nginx のリバプロ先を tcpdump で念のため確認してた。
39000 点くらい。
余裕もできて、16 時になったので複数台に展開したりするための準備を進めていく。
sysctl.conf
や別のインスタンスに MySQL を用意したり、 node を動かす準備をしていく。
17 時になって最終構成。
+ +------------------------------------+-------------------+-------------------+ | | listen 8081 | listen 8081 | isten 8081 | | | | | | +isu01----------+ +isu02+---------+ +isu03+---------+ +isu04+---------+ +isu05+---------+ | | | | | | | | | | | | | | | | +----v--+-+ | | +---------+ | | +----v----+ | | +----v----+ | | +----v----+ | | | nginx | | +----> mysql | | | | nodejs | | | | nodejs | | | | nodejs | | | +----+----+ | | | +---------+ | | +----+----+ | | +----+----+ | | +----+----+ | | | | | | | | | | | | | | | | | +----v----+ | | +---------------+ +---------------+ +---------------+ +---------------+ | | app +----+ | | | | +----^----+ | | | | | | | | | | +---------------+ | | | |listen 8080 | | | +---------------------------------------+-------------------+-------------------+
ここからのスコアはこんな様子。
isu03, isu04, isu05 で nodejs を動かしているんだけど、これを 2 プロセスにするとなぜかスコアが大幅に落ちて 3万点 ぐらいになってしまって、ボトルネックの調査に行き詰まる。 結局、ボトルネックが不明だったので、 1 プロセスに戻した状態で最高記録を叩き出した。 その後、インスタンスの再起動試験をやっていくつかベンチをかけてみたけれど、 8 万点を越えることができなかったのが不完全燃焼。 なぜ、スコアが下がってしまったのか今でも分からない(停止 -> 起動ではなくて、再起動してたんだけど Azure のインスタンスガチャ?)。
蓋を開ければ優勝できてたんだけど、最高点数で表彰されなかったのが悔しかった。
最後にボトルネックになっていたのはネットワークだったんだろうなーと今は思ってる。 けれども、これを早々と見つけていても構成を 17 時の時点で構成を変えるのは難しかっただろうなとも思っていて、早くも来年の課題が見えてきて来年が楽しみになってる。
技術的トピック
直接スコアにコミットしたのは docker を潰すところと nginx の設定をガリガリやったところ。
docker はいつか来るだろうなと思っていたので、割とやることは自明で気持ちよく潰せたので楽しかった。 docker-compose は触ったことがなかったけど(今だと swarm でやることになるのかな?)、 docker-compose がどんな役割かは知っていたので、勘で設定ファイルを systemd 化していくことはできた。
http2 も来るだろうと思っていて let's encrypt の準備をしていた。 ドメインが割り当てられることはやっぱりなかったけれど、 nginx.conf の雛形を放り込んだら http2 をちゃんと喋ってくれたので、よかった。
あとはオペレーションのはなし。
今回はサーバが 5 台用意されていて、 2 年前に出場したときよりも 2 台増えていてオペレーション力が求められた。 けれども、基本的にサーバが何台増えようともできることは同一にしておく必要があって、以下のようにホストやプライベート IP のリストをつくっておいて利用するのが簡単でハマりどころも少なくていい。
ISUIPS="10.6.1.5 10.6.1.6 10.6.1.7 10.6.1.8" for i in ${ISUIPS[@]}; do echo $i; ssh $i 'hostname'; done for i in ${ISUIPS[@]}; do echo $i; rsync -n -avRiK /etc/systemd/system/react.service $i:/; done for i in ${ISUIPS[@]}; do echo $i; rsync -avRiK /etc/systemd/system/react.service $i:/; done echo -n ${ISUIPS[@]} | xargs -d' ' -P${#ISUIPS[@]} -I {} ssh -qt {} 'sudo journalctl -f -u isu-go' echo -n ${ISUIPS[@]} | xargs -d' ' -P${#ISUIPS[@]} -I {} ssh -qt {} 'sudo tail -F /var/log/nginx/access.log'
for i in ${IP のリスト}
みたいな書き方は普段のオペレーションでも使っていて練習ではなくて業務のノウハウ的なもの。
ホスト名の扱いに慣れていれば fabric や pdsh みたいなものを使わなくても ssh や xargs といった標準のツールで十分オペレーションできるし戦える。
こんなのを毎回打つ人として本戦ではインフラエンジニアまたはオペレーションエンジニアが必要なのかなあと思う(ミスるとクリティカルだし)。
感想
初めて出場した ISUCON は ISUCON4 だった。入社 1 年目で、そのときも @methane さんと出場して 5 位。 昨年はアプリ担当で出て go で出場するも、少しスコアを上げただけで予選敗退した。 このときはインフラ担当がいなくて、アプリとインフラを自分だけでやってたので本当つらかった思いがある。
そして、今回。予選はいつもなら各自で戦うんだけど今回は予選からチームで戦って役割を分担した。 予選は nginx.conf の設定をするだけであまり活躍する場はなかったけれど、本戦はインフラエンジニアがかなり活躍できて本当に楽しかった。 一方、途中でオペミスして、ファイルをふっ飛ばしてしまうこともあり @methane さんに ansible をトレースして助けていただいたり、構成の変更を練ってもらったり本当に感謝。
記事だとめっちゃ綺麗にうまくいったようにまとめているけど、実際は準備してた道具がほとんど封印された状態。 mysql の sys スキーマや nginx の webdav, image-filter, mruby, グラフィカルなリソースモニタリングの準備していたけど予想を上回る内容の問題で ISUCON って感じ! HTML5 の SSE や react など聞いたことはあるけど触れたことのない技術が問題として出題されて、どんな技術にもより貪欲でありたいと思わされた。 僕にとって ISUCON は自分自身のベンチマークだと思っていて自分がどれだけ成長できているかを実感できる素晴しい機会なので、ともあれ来年も今後もずっと挑戦していきたい。
最後に運営の皆様、参加者の皆様、会社の皆様、お忙しい中サポート & 応援ありがとうございました。
ISUCON6ありがとうございました!また問題などあらためて公開しますがひとまず運営お疲れ様でしたということで。出題チームへ届くウィッシュリストこちらです!是非ねぎらいを!(後日イベント行いそこで利用いたします) #isucon https://t.co/z2TsrSOTAO pic.twitter.com/qUr61qtfZs
— ISUCON公式 (@isucon_official) October 22, 2016
https://bit.ly/isucon6 からギフトできるようですので是非!!