ISUCON11予選に参加して失格になってきた
概要
去年(予選, 本戦)に引き続きISUCON11 予選に参加し、失格になってきた。
その振り返り。
また、去年参加したチームは、今年社会人になった僕以外今年も学生ということで、学生枠での本戦出場を目指して解散した。
ということで会社の同期に声を掛けて、次のチームで出場した。
事前準備したこと
利用言語は、メンバー全員が普段業務でKotlin, ScalaといったJVM系の言語を書いており、実装が無いので可読性の高さなどからGoでやることにした。
ミドルウェアはNginx, MySQLが使用されると想定して、設定や計測の練習や準備を事前に行った。
準備した計測ツールは下記
- プロファイラー
- pprof
- アクセスログ解析
- alp
- スロークエリ解析
- pt-query-digest
- リソース確認
- htop
毎年ツールの導入方法をISUCON前に思い出しているので、自分のメモのために導入方法も書いておく。
pprof
net/http/pprofに書いてあるように、こんな感じで追記すればOK。便利。
import _ "net/http/pprof" func main() { go func() { log.Println(http.ListenAndServe("localhost:8080", nil)) }() }
プロファイルする際はローカルから
$ go tool pprof -http=":8888" "http://example.com:8080/debug/pprof/profile?secondos=60"
みたいな感じで、計測をした。
alp
下記コマンドでインストール
$ curl -OL https://github.com/tkuchiki/alp/releases/download/v1.0.7/alp_linux_amd64.zip $ unzip alp_linux_amd64.zip $ sudo install alp /usr/local/bin/alp
Nginxのログ出力をLTSV形式に設定して、こんな感じのコマンドでアクセス合計時間で降順にソートしてログを解析できる。
$ alp --sort=sum -r --file=/var/log/nginx/access.log ltsv | head -n 10
pt-query-digest
下記コマンドでインストール
$ wget https://www.percona.com/downloads/percona-toolkit/3.0.3/binary/debian/jessie/x86_64/percona-toolkit_3.0.3-1.jessie_amd64.deb $ sudo apt-get install -y gdebi $ sudo gdebi -n percona-toolkit_3.0.3-1.jessie_amd64.deb
MySQLの設定でslow_queryを出力するようにして、下記コマンドで解析できる。
$ pt-query-digest /var/log/mysql/mysql-slow.log
htop
topコマンドで もリソース確認できるが、特に何もしなくても見やすいのでhtopを使用することにした。
$ sudo apt install -y htop
でインストールして
$ htop
で使用する。
セットアップスクリプト
当日、各サーバーにセットアップする手間を減らすために簡単なスクリプトを書いた。
デプロイスクリプトも書きたかったが、準備する時間が無く、準備できなかった。絶対にあった方が便利なので、来年は頑張りすぎない程度に事前に用意する。
当日やったこと
当日のチームメンバーの動きは、下記のように予め大まかに決めておいた。
- takahiro
- インフラ
- tomonori
- 問題の読み込み+計測+App改善
- shutarou
- App改善
当日行ったことを、自分が行ったことと記憶を元におおまかな時系列順にまとめていく。
10:00 競技開始と初回ベンチ2396点
競技開始され、AWS CloudFormationのテンプレートが配布されたので適用した。
競技用サーバーのIPが確定したら、ssh用のconfigファイルを用意してメンバーに共有した。
ここで実行した初回ベンチマークのスコアは2396点
だった。
10:10 諸々のセットアップ
事前に用意しておいたセットアップスクリプトを実行し、tomonoriに問題の読み込み、shutarouに競技のコードをGitHubへのpushをお願いした。
自分は、サーバーのスペックと実行環境を確認した。
サーバーのスペックはAWSコンソールから確認し
$ sudo systemctl status nginx
でnginxが動作してることを安心して
$ sudo systemctl status mysql
でMariaDBだったことに少し驚いたが、MySQLと互換性あるからなんとかなるだろうと思い、alpとpt-query-digestで計測できるようにNginxとMatiaDB側の設定を書き換えた。
10:30 AppとDBの分離
AppとDBを別のサーバーに分けたほうが計測しやすいと考え、最初に下記の様にAppとDBを別サーバーに分離した。
- サーバー1
- Nginx
- App(Go)
- サーバー2
(限界まで1サーバーでチューニングした方が良い気もするので、来年は事前の準備段階でそのことについてもうちょっと考えておいた方が良さそう。)
ここで、ローカルからもDBへアクセスできるように、AWSのセキュリティーグループのインバウンドへ3306を特に何も考えずに追加した。
11:00 MariaDBのチューニングを少しして6825点
MariaDBのチューニングを少しした。
git管理していなかったので微かな記憶だが、下記の値を設定した気がする。
(来年はこの辺の設定ファイルもちゃんとgit管理しておきたい)
innodb_buffer_pool_size = 3GB innodb_flush_log_at_trx_commit = 2 innodb_flush_method = O_DIRECT
サーバーのメモリが大きかったので、雰囲気でquery_cache_size
も大きくしといた気がする。
この時点で、スコアは6825点
12:00 DB使用率が100%
ベンチマークを実行した際のサーバーリソースを計測すると、DB使用率が100%だった。
alpの実行結果と
pt-query-digestの実行結果
を確認して、isu_condition
テーブルのreadとwriteが原因でCPU利用率が100%になっていると考え、POST /api/condition/[0-9a-z\\-]+
があまりにも多かったので、write側のチューニングをしようとチームで決めた。
(pt-query-digestの結果から、INDEXを貼ることによりreadの改善が期待されたが、write優先で後回しにした。パッとできる改善ではあったので、パッとやっちゃえば良かったと後から思った。)
12:30 isu_conditionのBulkInsert化
他のメンバーがisu_condition
のwriteを減らすために複数回分のリクエストをまとめてInsertしようとしたりしてくれていたが、非同期周りのハンドリングが難しくて、1リクエスト内のBulkInsertだけ実装した。この辺はチームメンバーに任せていたので、詳しいことは分かっていない。
その間に自分は、Appのソースコードを読んだり、DBの中身を確認してた。
13:30 GoからDBへのコネクションの制限を無くす
ソースコードを読んでいたら
db.SetMaxOpenConns(10)
と、最大コネクション数が10に制限されていたので、コメントアウトした。
スコアは全然上がらなかった。
14:00 jia_service_urlを変数で持つように
isu_association_config
に保存されているjia_service_url
が、初期化処理時以外に変化して無さそうだったので初期化処理時に変数で持つようにしてDBから取得しないようにした。
これもスコアは全然上がらなかった。
14:30 DBを2台にしようと妄想する
相変わらず、DBサーバーがCPU100%のままなのでなんとかしようと考える。
水平分割すると良いのかな?とか妄想しながらとりあえず、ボトルネックになっているisu_condition
テーブルだけ別サーバーのDBにしてなんとかしようとGoとDBのセッティングをしてた。
↓が起きたので、PRだけ作成して反映はしなかった
15:00 isu_conditionに貼り忘れてたINDEXを貼る 23000点
isu_condition
にINDEX貼ると良さそうって話をしていたもののINDEXを貼っていなかったので、tomonoriがINDEXを貼ってくれた。
ベンチマークを実行すると唐突に23000
点になって衝撃を受けた。
サーバーのリソースを確認するとDBのCPU使用率が低下し、App側が100%になっていたので、サーバー構成を
- サーバー1
- Nginx
- App(Go)
- サーバー2
- App(Go)
- サーバー3
にしようと考える。
16:30 App2台構成で30000点
Nginxでロードバランシングすれば、App2台に簡単に構成にできると思ってたら、少しハマってベンチマークが失敗し、時間を取られてしまった。
ハマった点としては下記
- 初期化に必要なSQLファイルが追加したサーバーに配置されていなかった
- 初期化に必要な
/sql/1_InitData.sql
をgitignoreしており、元々存在していたファイルを削除してしまっていたため、初期化に失敗していた。全然意識していなかったので、気づくのに遅れてしまった。
- 初期化に必要な
- jia_service_urlを取得できない
- jia_service_urlを初期化時に変数で持つようにしたことにより、初期化をしていないサーバーだと失敗するようになっていた。
上記を改善して、App2台構成で30000
点
17:00 trend APIの改善をしようとする
App2台構成で負荷分散すると、DBのCPU使用率が100%になった。
alpの結果が
とtrend APIの呼び出し回数が、増えていたので他のチームメンバーに改善をお願いした。
任せてしまったので、詳細な変更内容は把握できてないが、処理の高速化を行ったらAPI呼び出しが増えてスコアが下がってしまい、revertした。
17:10 Nginxの設定をチューニングしたりした
nginxのworker_connections
を増やしてみたり、ロードバランシングをラウンドロビンとLeast Connectedで比較してみたりした。
スコアはあまり変わらなかった。
この辺で、運営側に障害が発生し、ベンチマークが実行できなくなった。
17:30 撤退準備してたら51865点
ベンチマークが実行できなくなってる間に、撤退準備をした。
行ったこととしては、nginx,mysqlのログ出力停止、goのpprofを削除したりした。
その間にベンチマークが復活し、実行すると51865
点。
pprofの削除が効いたっぽい?
18:30 dropProbabilityに願いをかける
ベンチマークの障害により延長された時間でdropProbabilityに願いをかけた。
0, 0.5, 0.9の値で検証してみたものの、デフォルトの0.9が最もスコアが高かったため、0.9を採用。
18:45 競技終了 43797点
51865
点を取った際と特に差分はないはずだが、何故か再現することができず43797
点でフィニッシュ。
結果
失格になりました。
DB等をローカルからアクセスできるようにするために、AWSのセキュリティーグループのインバウンドを追加した事が原因です。 レギュレーションをちゃんと読んでおらず、禁止されていることを認識せずに何も考えずに行いました、、、
振り返り
良かったこと
- チームメンバーの役割分担を予め決めておいたこと
- 計測ツールの導入、実行方法を予め確認できていたこと
- サーバーリソースの確認、ログの解析を行いながらチューニングが行えたこと
悪かったこと
- レギュレーション、アプリケーションマニュアルをちゃんと読んでいなかったこと
- INDEX追加等、とりあえずできることをすぐにやらなかったこと
- ミドルウェアのチューニングをなんとなくで進めてしまっていたこと
今年できなかったけど来年はやるとよさそうなこと
まとめ
チューニングを愚直にやることで、着実に点数が上がる問題だった。
結果として失格にはなってしまったが、ちゃんと計測し、チームで色々議論しながらパフォーマンスチューニングできて楽しかった。
来年も出場して本戦出場したい。
appendix
shutarouの参加ブログ chouxcreams.hatenablog.com
使用したGitHubレポジトリ github.com
2020年を振り返る
毎年、年末に1年を振り返りたいなと思いながら振り返れていなかったので今年こそは振り返る。
1年間の休学の終盤+復学した大学4年という1年間。
月ごとに振り返っていきたいと思います。
1月
前年の12月に、働いていたスタートアップを退職して(学生インターンだけど休学して週5くらいで働いていた)時間ができたので次は何をしようか考えていた。
プロダクト作るの手伝って欲しいと以前から声を掛けてくれていた友人を思い出し、プロダクトの内容を聞いたり、壁打ちを手伝ったりもした。次はここにフルコミットしようかなとも考えたが、自分的にピンと来なかったので違うかなと思ったりもしていた。
とりあえずどこかの組織でコードを書きたいと思い、内定先や以前のインターン先に聞いてみたがタイミングの問題もあり、厳しそうだったので新しく探すことにした。
技術的には、Golang書けるようになりたいと思っていたのでGolang書いて勉強をしていた。
2月
Golangの学習を続けており、TechTrainにてGolangを題材にした教材があったためそれに取り組んだ。 techbowl.co.jp
そのツイートをTechTrain代表のおざまささんに拾って頂き、TechTrainで面談することになった。
その面談でインターン先探してる等の会話もし、TechTrainの開発に声を掛けて頂いてTechTrainの開発に加わることになった。この頃はまだコロナの影響も大きくなく、僕が茨城に住んでいるため、出社しやすいようにGAOGAOさんのシェアハウスを用意して頂いた。(金曜日に面談して開発に加わることになり、月曜日には出社していた。圧倒的スピード感)よければぜひ私とも面談しましょう!使いにくいところとかアドバイスください😊!
— おざまさ/masao ozawa/TechBowl代表取締役 (@zawamasa) 2020年2月4日
エンジニア系以外の話だと祖父が亡くなり、初めて身近な人の死を経験した。(葬儀の準備とか諸々見て、非本質的な手間が多すぎてしんどいな。とも思った) 葬儀と好きなバンドのフェスが重なり、行けなかったのは2020年トップレベルの心残り。coldrain、またフェスを開催してくれ。欲を言えばPTPも見たい。
3月
コロナの影響も大きくなってきて、TechTrainでの開発がリモートになり、茨城に帰ってきた。(なんだかんだ物理出社したのは1週間未満になってしまった。) 今振り返ると、中々早い時期からリモートで働いていたなと思う。 TechTrainのバックエンドでは、PHP,Laravelが使用されており、それをゴリゴリ書いていた。PHPさんは中々手が掛かる。 他にはハッカソンのメンター等色々やらせて貰った。
友人と海外に行く話もしていたが、コロナの影響で取りやめにした。コロナの影響が収まってきたら海外に行きたい。 KNOTFESTのチケットも持っていたが延期になった。チケットはまだ持っている。
4月
大学に復学した。1年間休学して大学の事は何もしていなかったが、意外と専門分野の事や数学も覚えていてなんとかなるなと思った。
がっつり研究しようかなとも思ったけど、コロナで研究室に中々行けないこともあり、引き続きがっつり働いてコードを書いていた。
後は、GAOGAOさんのハッカソンに参加し、Next.js x Firebaseで開発した。
入賞できなくて悔しかった。フロントエンドもっと書けるようになりたい。
note.com
5月
やぱったとreudとハッカソンに参加し、入賞した。 qiita.com
オンラインハッカソンにて、気軽にオンライン飲み会できるサービスを作って3位を頂きました!!!🎉🎉@reudasas @yapatta_prog が優秀過ぎてチーム開発めっちゃ楽しかった! pic.twitter.com/WLuoQq1IlM
— たかひろ (@taka0110_) 2020年5月24日
2人の開発力が凄くて、あーだこーだ言っていると、どんどんプロダクトが形になっていて楽しかった。3人でまた何か開発したい。
後は、仕事&研究室のゼミ&暇な時にDockerとかの勉強してた気がする。
大学時代最後のロッキンや色々なフェスが中止になって、しんどいな〜と思っていた気がする。
ROCK IN JAPAN FESTIVAL 2020 開催中止のお知らせ
— rockin'onフェスOFFICIAL (@rockinon_fes) 2020年5月15日
2020年8月8日(土)・9日(日)・10日(月・祝)に国営ひたち海浜公園で開催を予定していた「ROCK IN JAPAN FESTIVAL 2020」は、中止とさせていただきます。
開催を楽しみにお待ちいただいていた参加者の皆様には深くお詫びを申し上げます。
→
6月
引き続き、TechTrainで働きつつ研究室のゼミをこなしていた。 プレゼン力を上げたいということもあり、LTで登壇したりもした。 note.com
7月
6月と同じ感じの生活をしていて、またLTで登壇した。
devrel.connpass.com
知り合いが全然いないということもあり、かなり緊張した。もっと上手くなれるよう精進したい。
大学では、研究テーマが決まり、やりたいと言っていた機械学習使う系のテーマに決まって良かった。
8月
研究室のゼミが夏休みで時間ができたということもあり、筋トレしてた。そしたら、アナフィラキシーショックになった。死ぬかと思った。運動は適度にやることis大切。 後は、初キャンプに行った。楽しかった。また行きたい。
技術の事だと、友人とClean Architectureの輪読を始めた。良い本なのかもしれないけど、筆者の主張的なとこが多かったり、今でも使えんのか?って思うとこは結構あった。
今でもプログラム書くときはアーキテクチャどうしようかな〜って結構悩む時間が多い。
9月
ISUCON10予選にやぱったとreudと参加した。
棚からぼたもちにより学生枠で本戦出場してしまった。めっちゃ嬉しかった。
taka-rock.hatenablog.com
サポーターズ社の技育展にも登壇した。入賞はできなかったけど、周りのプロダクトが凄くてそれはそうって感じがした。オンラインでの登壇には慣れてきたのでオフラインでも同様のパフォーマンスを発揮したい()
【作品紹介 / チーム開発】
— 【公式】技育プロジェクトbyサポーターズ (@geek_pjt) 2020年9月16日
インターン仲間と開発したオンライン飲み会プラットフォーム「Cheers!」。コロナ禍ならではの作品です。https://t.co/woXxAWSZll
学生エンジニア制作の85作品が展示されるカンファレンス「#技育展」オンライン観覧者を募集中!https://t.co/l4ZTYds75T pic.twitter.com/fUr9OhMCrG
仕事では、インターン生のメンターをやったり、インフラのDocker化、AWS ECSへの移行+TerraformでのIACを設計から1人でやったりしてた。
【武蔵野大学×TechBowl インターン】
— 三宮 洋太 (@yota3s) 2020年9月9日
5日間終了しました・・・!
まずはインターン生のみなさんお疲れ様でした!
各チームしっかりと特徴が出ており、5日間でよくここまで整理できたなと思います!この経験を今後に生かしてくださいね〜!#TechTrain #TechBowl #武蔵野大学 #インターン pic.twitter.com/fjPYZiim4d
今までその場しのぎでインフラを触っていた感があったが、ISUCONと仕事でのインフラ移行タスクにより、この辺から自分のインフラスキルの圧倒的向上を感じた。
10月
ISUCON10本戦に出場した。正直、全然歯が立たなかった。来年は、社会人枠で本戦出場し、優勝して100万円欲しい。 taka-rock.hatenablog.com
応用情報にも申し込んだが、全然勉強する気が出なく、落ちた。来年はちゃんと勉強して取りたい。
友人とのClean Architectureの輪読が終わったので実践Rustプログラミング入門の輪読を始めた。友人と輪読することによって、CSや他の言語での実装知識も深まってなかなか良い。来年はRustでプロダクト作りたい。
11月
研究の中間発表があったため、がっつり研究してた。
所属している研究室が無線通信の分野で、数式で理論を立てる→Pythonでシミュレーションという流れの研究室だが、とりあえずシミュレーションを実装する→もっと理論まとめなって教授や先輩から言われるのをひたすら繰り返してた気がする。(とりあえず手を動かす&足りないところは後からキャッチアップする派の人間なので自分にはこのやり方が合ってた。)
シミュレーション回してると、もっと効率化したり実行時間を短縮したいという気持ちが強くなり、シミュレーション終了時にslackに結果を通知するようにしたり、Pythonでの並列処理を勉強したりしてた。プログラミングスキルで研究ゴリ押ししてます。ニューラルネットもやっているので、機械学習のスキルもちょっとついて嬉しい。
研究が忙しくなってきたこともあり、この月でTechTrainでの開発は辞めさせてもらった。コロナが落ち着いてきたらまた遊びに行きたい。
12月
引き続き研究を進めつつ、研究室で機械学習用のPCを買って貰ったので研究しやすいようにそれのサーバー化など、研究してると見せかけて違うこともしていた。
折角研究するなら、学会発表したいと言い続け、年内最後の進捗報告で教授から年明けすぐ締め切りの学会発表申し込もっかとクリスマスプレゼントを頂いた。(年末年始潰れるかと思いましたが、無事提出してこの記事を書いています。)
まとめ
2020年はコロナの影響で、中々遊びに行けない&仕事と研究と趣味のプログラミングが全部同じ場所でしんどいって思う時がかなりありました。
ITは家で仕事できるから良いよねって言われることが結構あるけど、リモートではプロダクトを作っているってより業務を請け負っている感を強く感じてしまいました。
周りでも転職した話を今年は結構聞いたので、家で仕事できていて全てうまく行っている訳では無いかなと思います。
そんな中でも振り返ってみると、2020年はTechTrainの方々や、今までのインターン等で仲良くなった方々のおかげで結構色々挑戦し、スキルの幅が広がったのではないかと思います。
地方国立通っており、大学には自分と同じようなことをやっている人間は少ないので彼らの存在は大きかったです。 (大学や研究室等の周りの方々にもお世話になりました!)
(今日ちょうどこんなツイートを見つけ、同じことが博士以外の自分のような場合でも言えるな〜と思いました。)
博士が孤独感を感じやすい理由は
— みのん (@min0nmin0n) 2020年12月29日
①単純に母数が少ない
②共通の悩みを共有しづらい
②修士との研究に対する熱量の差が大きい
の3つが主要な要因かと思う。自分は他人と違うことをやっていると感じると人は孤独感を持ちやすい気がする。やる気のある仲間を見つけられると楽しい3年間を送りやすい
2021年の抱負
4月から社会人として働くので、頑張っていきたいです。今まで複数社でインターンしてきましたが、同規模の開発には余り関わってこなかったので新たなスキルを身に着けたい。
また、2020年にAWSを結構勉強したので資格を取りたい。 (他の資格も取りたい。)
そしてその前に、ある程度の成果を研究で出したい。(多分もう大学は卒業できるけど、プロダクト思考寄りの人間でも、やれば理論もいけるってことを証明したいです。)
おわりに
一緒に開発や、近況報告など何でもいつでもお待ちしてるので、声掛けてください!
4月からは東京に行きます!多分!
ISUCON10本戦に参加してきた話
ISUCON10 本戦に参加してきた話
ISUCON10予選に参加してきた話の続き。 予選敗退、、、悔しい。って気持ちだったのですが、ISUCON本戦2週間前になんだかんだあって本戦出場できることになりました。歓喜。
メンバー
予選と一緒
本戦まで
事前練習
reudがVultrでインスタンスを4台用意してくれました。
予選ではhtopとnew relicでほぼ雰囲気で計測していたため、計測ツールの導入等練習しました。
具体的には
の導入の練習をしました。
また、予選の環境をインスタンス上に構築する際に運営側がAnsibleで用意してくれていた為、Ansibleを初めて触りました。触った所感として、「Ansibleめっちゃ便利だな」とチームメンバー全員でAnsibleに感動し、計測ツールの導入もAnsibleで行おうという話になり、reudがAnsible周りの用意をしてくれました。神。
後、この記事を読んで
主にはベンチ実行後、alp + pt-query-digestの結果をNginxのドキュメントルート配下にhtml出力し、そのURLをSlack通知するという方法で確認できるようにしていました。 この形式だと結果を中途半端にSlack通知するより視認性も高く、後から見返す時もわかりやすいのでオススメです
とあり、便利そう!ということでalp + pt-query-digestの出力結果をNginxで配信できるように準備しておきました。
その内容としては
定期実行処理としてcrontabに
crontab -e
こんな感じでnginxのアクセスログをalpに通し、sedでmdのいー感じの形式にし、md形式にて出力しました。
*/1 * * * * cat /var/log/nginx/access.log | alp ltsv -r | sed -e 's/\+/|/g' -e '1d' | head -n -1 > /home/isucon/logs/nginx.md
また、pt-query-digestの方も
*/1 * * * * sudo pt-query-digest /var/log/mysql/mysql-slow.log > /home/isucon/logs/slow.txt
のように出力しました。
本戦始まってから気づいたのですが、cronでこんなに解析かけるとサーバーになかなか負荷がかかります。その為本戦ではcron外しました、、、良い子はめんどくさがらずにちゃんとベンチマークの実行に合わせて解析をかけるようにしましょう。
景品
当日前に本戦出場記念の景品が届きました! 物が届くと嬉しいですね!
当日
やぱったの家にメンバー3人で集まり、オフラインで本戦に挑みました!
本戦開始
10:00の開始と共に問題とレギュレーションを読み始めました。文章量かなりあった、、、
概ね読み終えた所でインスタンスに接続し、まずはミドルウェア周りの確認を行いました。
mysqlは8系が入っていることはすぐに分かったのですが、Webサーバーにnginxが用いられていると思いきや入っていない、、、
sudo lsof -i:443
で確認してみるとenvoy
が動いている、、、存在は知ってたけど触ったことは無かったのでconfigのymlを雰囲気で読みました。
確認後はmysqlの設定でslow-queryのログをオンにし、pt-query-digestで解析できるようにしたり、別インスタンスからのアクセスを許可したり、軽くindexを貼ったりしました。
メンバーの方もUbuntuのバージョンによる違いによりansibleが上手く動かない等、かなり手こずっていました。
また、alpでアクセスログの解析を行おうとしたものの、envoyのログをalpで欲しい形式で出力するのに時間がかかってしまい、結局やめました。(ltsvで食わせて上げる必要があると思っていたのですがalpのREADMEをちゃんと読んでみると解析に必要な情報が書いてありますね、、、本番中にも読んだのですが、焦っていて読み飛ばしていました)
サーバー構成
reudがneofetchを用いてサーバー3台のスペックを確認してくれて、3台ともスペックが違う事が判明しました。(3台のスペックが違うなんて思ってもいなかった)
- サーバー1
- CPU 2コア
- メモリ 1GB
- サーバー2
- CPU 2コア
- メモリ 2GB
- サーバー3
- CPU 4コア
- メモリ 1GB
その為、初期状態でベンチを回した場合もスコアはバラけてました
- サーバー1
- 4973
- サーバー2
- 5617
- サーバー3
- 6887
netdataで負荷を確認しながらベンチを何度か回して、サーバーの構成を入れ替え、DBのCPU負荷が高いため最終的に
- サーバー1
- envoy
- サーバー2
- web, api
- サーバー3
- DB
の構成にしました。 多分この辺でスコアが多分7152点。
TeamCapacity
サーバーを分散し、CPU負荷に余裕があることが確認できた為、何かしらのパラメータでアクセス数のlimitにあたっているのでは無いかと考えました。メンバーに話すとTeamCapacityなるパラメータがある事が判明。 10→50へ上げてもらう。これでスコアが多分10010点。 良さげなTeamCapacityを探すために一旦120にあげて、ベンチを回したりしてみるも通らず60にてfix(負荷がかかりすぎるとenvoyが死んでしまっていたっぽい。open files limitに引っかかってしまっていた??)
WebPush
やぱったとreudが実装してくれました。 途中で実装手伝おうと思いドキュメント読んだものの、しっかり読まないと何も分からなかった為何も手伝えず。実装感謝です。これで多分14314点
Goのコードをい〜かんじに
Goのコードをい〜かんじにしようとしたものの時間が足らずにほぼできませんでした。
ラスト1時間
諸々あって、ベンチが通らなくなってしまいかなり焦りました。
終了時間ぎりぎりでベンチが通ったためそこでfinish
結果
最後のベンチが通ったのでいけると思ったのですが、ランダムでベンチが落ちる状態だったようです。 出ていたエラーとしては下記
- あるべき通知を受信していないことが検知されました
- 最終検証にて Clarification の検証に失敗しました
悔しいです。
振り返り
競技中はやらなきゃいけないことが多すぎて終了時間まであっという間でした。その振り返りとして、やることとやらないことをしっかりと判断するべきだったと感じています。
具体的には、計測ツールの導入で手間取る箇所やenvoyの理解など競技のスコアアップには本質的には繋がらない箇所はある程度割り切って自分たちのできる所で更にスコアアップを狙うべきだったと思います。
できることをやるの大切。
後、マニュアルにキャッシュ可と書いてあったものの最小工数での良い方法が思いつかなかったです。他チームではVarnishというものをenvoyとwebの間でキャッシュしていたそう。知らなかった為、勉強する。
来年は社会人枠で本戦出場目指して頑張ります。
P.S.
メンバーでオフラインで集まって参加するのはとても楽しかったです。 来年はオフラインで開催できてると良いな。
ISUCON10予選に参加してきた話
ISUCON10予選に参加してきた話
ISUCON10予選に参加してきました。 ブログを書くまでがISUCON予選?と言われているので今回はISUCON10予選について振り返っていきたいと思います。
ISUCONとは?
いい感じにスピードアップコンテスト、略してISUCONです。 その名の通り、サーバーの高速化をしたりしていい感じにスピードアップして戦います。
本選へ参加するための予選があり、下記の条件で30チーム選出されます。
- 一般枠 (25チーム):予選終了時スコアにおける上位25チーム
- 学生枠 (5チーム):学生チームの中で、予選終了時スコアにおける上位5チーム
結果は?
結果は、1122点でした。
Twitterで見た所、学生枠の5位だった方が1240くらいだったそうなので学生枠での本戦出場あと一歩でした、、、
下記が作業レポジトリです。(機密情報が含まれていた為、commitを1つに固めてます)
https://github.com/reud/01NOCUSI
誰と出たの?
ワンランク上のジロリアンというチームで3人で出場しました。
メンバーの役割分担としてはそこまで明確には決めてなく、インフラとアプリケーション(Go)のコードを3人でい〜感じに分担して作業する感じでした。
Discordを使って常に通話しながらのフルオンラインで競技に参加しましたが、特に問題なく競技を行えました。
参加までの準備
事前にISUCON9の予選を用いて少し練習していました。
reudがConoHaでVPSを借りてくれたので、そこに諸々立てました。
3人ともNginx, MySQLのチューニング等したことが無かった為、皆で介抱し合いながら色々勉強していました。
VPS借りてくれたのめっちゃありがたかった、、、
- 皆で勉強したこと
基本的な所の勉強&参加者のブログを読んで自分たちも実際にやってみたって感じですね。参加ブログ感謝。
当日
開始まで
10:00開始だったものの、朝起きたら12:00開始に変更になっていました。時間の余裕ができた為、10:00からDiscordに集まりながらSlackのワークスペースを作ったり、便利コマンドや困ったときの記事をSlackに貼りまくっていました。開始延期ありがたい。 そんなこんなで12:20開始に変更になり、ゆるゆると開始待機していました。
12:20 競技開始
開始と同時に、チームメンバー全員で予選マニュアルをしっかり読みました。
踏み台経由のSSHをしたこと無かったため、 ssh_configの例がマニュアルに書いてあったのめちゃくちゃ助かりました。
Host isucon-bastion HostName <踏み台用IPアドレス> Port 20340 User isucon Host isucon-server ProxyJump isucon-bastion User isucon HostName <自チームサーバのIPアドレス>
インスタンスに接続して最初に、ディレクトリ構成を確認しました。
Webアプリケーションは/home/isucon/isuumo/webapp
以下を触れば良さそうだったのでチームメイトにGit管理の追加&Githubのプライベートレポジトリの連携等頼みました。Git管理は必須ですね。
次はNginx, MySQLのデフォルトの設定を確認したりしていました。
とりあえずベンチを走らせようと思ったもののまだ走らせる事ができなかったので、ソースコードの確認やreudがnew relicを導入してくれたりしていました。仕事が早い。
13:30
ベンチを動かせるようになったので動かしました。 493点。なるほど。
13:40
やぱったがインスタンスのメモリを確認してくれました。
2GB、、、?めっちゃ低スペじゃね??って話したりしていた記憶がある。
new relicの計測結果や、ソースコード見た感じでデータベースチューニングゲーか、、、?と強く感じてきた頃。SQLクエリとschemaとにらめっこし始めました。
インデックス貼ろうにもwhere句とORDER BYが多すぎて辛い。
14:00
マニュアルに下記の記述が書いてあったことを思い出しました。
bot からのアクセスはコンバージョンに繋がらないため、弾くことが仕様として決定しましたが、まだ実装されていません。 bot は User-Agent が以下の正規表現にマッチする形式であり、このリクエストに対して 503 Service Unavailable を返すことが許可されています。 これに対するベンチマーカーからの減点は発生しません。
やぱたが正規表現の神(正規表現エンジンとか作ってた)なので該当UAからのアクセスは503を返すようにNginxの設定をお願いしました。/etc/nginx/sites-enabled/isuumo.conf
に下記を追加していく感じ
if ($http_user_agent ~* "^ISUCONbot(-Mobile)?$") { return 503; }
点数が上がった気がする。(確かこの頃ベンチマークのキューを積めなくてすぐに計測できなかった)
クエリ見てみるページング処理がとLIMIT OFFSETを用いて記述されている。これを良い感じにしようと思ったが、条件が複雑でどうやってページングすればよいのか分からず断念、、、(これどうやってページングするのがベストだったんですかね、、、?)
この辺で、スキーマをデータ構造に最適っぽやつに変更して、インデックスを追加してみた。(これもベンチで計測できなかった為お気持ち早くなった気がする)
Webからアプリを操作していたら、画像ファイルの配信が遅かったのでNginxで静的ファイルのキャッシュをすれば早くなるような気がしたのでキャッシュの設定を追加する。(マニュアルしっかり読んだら、「ベンチマークからのリクエストは、 API に対してのみ行われます。」と書かれているので静的ファイル周りの設定は今回は何も意味ないですね。最後にキャッシュ周りの設定を剥がしました。マニュアルしっかり読みましょう。)
16:00
reudがhtop
コマンドを用いて負荷計測する事を提案してくれる。
CPU使用率が100%貼り付き、、、
処理の流れ的にもMySQLの負荷が凄い事は確実なのでMySQLのインスタンスを増やそうって話になるものの17:00まではインスタンス1台でスコア上げていく方針で作業を進める。
17:00
MySQLのインスタンス増やそうにも、構成どうするって話になる。
ここが今回1スコアが上がったターニングポイント
3人とも別の手法提案していてどれを採用するか結構話し合った
- takahiro
- MySQLのレプリケーションを用いてDBを増やす
- レプリケーションの設定はやったこと無かったものの結構簡単だと記憶していたのでいけそうな気がした
- reudがISUCONの過去の参加者の「MySQL のリードレプリカは、ベンチマークのアクセス速度に対してレプリケーションが追いつかずに fail 連発してボツ。」という記事を見つけてくれたりして、同期の整合性が怪しいので労力の割にリスクが、、、て話になる
- MySQLのレプリケーションを用いてDBを増やす
- やぱった
- reud
こんな感じでどれを採用するかなかなかまとまらない為、とりあえずGoで1台, MySQLで1台の2台構成にして計測してから考えようって話になる。
ここで、Goから別のインスタンスのMySQLに接続切り替えるのに結構手間取ってしまった、、、
やったこととしては/etc/mysql/mysql.conf.d/mysqld.cnf
へ他インスタンスのIPをbind-address=xx.xxx.xx.xxx
で追加して、Goのインスタンスからmysql -u isucon --host=xx.xx.xx.xxx --port=3306 -p
で接続できることを確認。
接続できたので問題ないだろうとWebからページの表示を確認した所500が返ってくる、、、みたいな状況になり、色々話しているうちにユーザー+ホストでMySQLユーザー権限の管理がされている事を思い出し、各インスタンスに紐づくユーザーを追加した所、問題なく接続できました。(複数インスタンスでの接続練習しておくべきだった、、、)
やっとGoで1台, MySQLで1台で動作成功し、ベンチを走らせる。
スコアは覚えていないが、あれこの構成でも結構スコア上がってね??ってなった
19:00
インスタンス構成は、色々話しているうちにreudの案を元にNginx+Goで1台,テーブル毎に分割したMySQL2台を用意して、Go側でDB2台の接続情報を持つのが一番楽&安全でスコア上がりそうじゃね??って話になった為その構成に変更。
reudが爆速でGoでの2台DB接続処理を書いてくれました。早すぎて震えた。
ベンチを走らせるとスコア881で学生順位15位くらいに食い込んで、「あれ、本戦出場目指せるんじゃね??」という雰囲気が出て、インスタンス構成はこれで確定。
MySQLのinnodb_buffer_pool_size
を2GBにしたりGo側の処理をいーかんじにしてもらい再度ベンチを走らせる。
スコア1075で学生順位7位で本戦出場が本当に見えてくる。
20:00
各ミドルウェアのログ出力をオフにしたり、new relicをGoのコードから剥がしたりと撤退作業を始めました。
諸々作業完了後に計測すると1122点。他のチームがそこまでスコア上がっていないことを願い、これ以上のスコアUPは諦めました。
21:00
終わり。
結果
学生枠でも本戦出場に食い込めませんでした、、、
振り返り
結果発表待ちの時に気づいたのですが、MySQLの設定のinnodb_buffer_pool_size
ってメモリ上に展開するデータサイズで、それ+query_cache_size
でクエリの実行結果もどんどんメモリ上にキャッシュしていくことができるんですね、この事を知らずにメモリの空きがある事は確認できていたものの、query_cache
のチューニングを行いませんでした、、、、メモリ限界までキャッシュさせてればもっとスコアあがったと思うとかなり悔しいです。勉強不足です。(再起動テストを行っていなかったため、結局Failで落ちていたっちゃ落ちていたかも)
またスコアに繋がる打ち手を全然自分で出せておらず、あまりチームに貢献できなかったなと感じています。
細かく振り返ってみると次の点が改善できたと思っています。
- 計測を更に行えるように
- 計測の練習ほぼしておらず、当日ぶっつけで計測したので、更に良い計測方法があったはず、、、来年は推測せずに、計測します。
- Goの練習を直前にもっとしておくべきだった
- Goは書いたことがあるものの、複雑な処理を記述すのには時間がかかってしまう位のレベル感なのでGoの最適化をチームメイトに全て任せる形になってしまいました。問題点見つけて自分で爆速で修正できるレベルまで練習しておくべし。
- ミドルウェアの設定内容を深く理解しておく
query_cache
の件など理解しきれていない点が存在した。細かいパラメータを完全に理解しなくとも頭の中の引き出しは増やしておくべし。
まとめ
ギリギリ、本戦出場できなかったのでかなり悔しいです。精進します。
来年の意気込み
社会人枠で本戦出場目指します。
P.S.
DISCORDに書いてあった他の人の打ち手
- MySQL5.7 → 8に変更する
- 8に変更すると早くなるんですね、、、普段5系ばかり使っているので知らなかった
- generated columns
- Special index
はじめてのKubernetes
About
Kubernetesをはじめて触ってみた。
その際の記録。
- 教材
- 技術評論社 「みんなのDocker/Kubernetes」
環境構築
Docker for MacではKubernetesがサポートされている。
Docker for MacのPreferenceを開き、KubernetesタブからEnable Kubernetesにチェックを入れればKubernetesのインストールが始まり、少し待つとKubernetesが使用できるようになる。とても簡単!
kubectl cluster-info
コマンドを実行し、次のような結果が返って来れば導入成功!
Kubernetes master is running at https://kubernetes.docker.internal:6443 KubeDNS is running at https://kubernetes.docker.internal:6443/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy To further debug and diagnose cluster problems, use 'kubectl cluster-info dump'.
やってみた
Kubernetsでは「マニフェスト」と呼ばれる定義ファイルに「リソース」を記述して登録することで、コンテナを実行する。
- Word List
- Pod
- コンテナの管理に使用するリソース。1つ以上のコンテナのグループを表し、Kubenetsにおけるコンテナ管理の基本単位である。
- Deployment
- 実行するPodの数とそのPodの定義テンプレートを含むリソース。複数のPodを管理する為に使用する。
- Pod
Podの定義例
nginxコンテナを実行するPodの定義
apiVersion: v1 kind: Pod metadata: name: nginx spec: cantainers: - name: nginx image: nginx:1.15.7 ports: - containerPort: 80
Deploymentの定義例
ngixコンテナを実行するPodを3個起動する定義
apiVersion: apps/v1 kind: Deployment metadata: name: nginx-deployment spec: selector: matchLabels: app: nginx replicas: 3 template: metadata: labels: app: nginx spec: containers: - name: nginx image: nginx:1.7.9 ports: - containerPort: 80
コンテナを起動してみる
上記Deploymentの定義を記述したマニュフェストファイルをdeployment.yaml
として保存する。
kubectl apply
コマンドを実行してKubenetesにマニフェストファイルの内容を実行する。
kubectl apply -f deployment.yaml
kubectl get pods
コマンドを実行してKubenetesのクラスタ上にPodが起動しているかを確認する。
下記のような結果が返って来れば実行成功!
NAME READY STATUS RESTARTS AGE nginx-deployment-6dd86d77d-5sgtm 1/1 Running 0 100m nginx-deployment-6dd86d77d-cphxc 1/1 Running 0 100m nginx-deployment-6dd86d77d-k4kkd 1/1 Running 0 100m
Podと通信してみる
Deploymentを用いてPodを起動し、コンテナを建てられたので実際に通信してみる。
- Word List
- Service
- 複数のPodへの負荷分散を実現する為のリソース
- Serviceの定義を元にロードバランシングできる
- 複数のPodへの負荷分散を実現する為のリソース
- Service
Serviceの定義例
先程のngixコンテナを実行するPodへポート8080への通信をPodのポート80へ転送する定義
apiVersion: v1 kind: Service metadata: name: nginx-service spec: type: LoadBalancer selector: app: nginx ports: - protocol: TCP port: 8080 targetPort: 80
Deploymentの登録と同様に、上記Serviceの定義を記述したマニュフェストファイルをservice.yaml
として保存し、kubectl apply
コマンドを実行してKubenetesにマニフェストファイルの内容を実行する。
kubectl apply -f service.yaml
kubectl get service
コマンドを実行してServiceの状態を確認する。
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 20h nginx-service LoadBalancer 10.105.91.94 localhost 8080:31494 TCP 102m
nginx-serviceがlocalhost:8080で待ち構えている事が分かる。
実際にhttp://localhost:8080へアクセスし、nginxの標準ページが表示されればService経由でのPodとの通信が成功!
感想
Docker for MacでKubernetesがサポートされている為、とても簡単にHello worldができ、コンテナの複数起動&ロードバランシングもとても簡単に行えた。
更に触れ、実用的なメリットも学んでいきたい。