こんいす~。昨年に引き続き、今年もISUCONに参加した記録を残していきます。
去年の参加記録はこちら
「今年も参加するぞ!!」と意気込んでなんとか申し込み成功。権利をGETしたので、知人を誘って参戦してもらいました。
チーム名 : 梅割り駆動開発
メンバー :
- @HirokiYoshida837: クエリやアプリの解析、インフラ周りを主に色々。
- @sagawa4425: Index貼ったりN+1潰したり
- @sagawao: やれることをやる
なんだこのチーム画像。
「今年もISUCONやるぞ~!」ということでちまちまと準備を進めていました。
alp
や pt-query-digest
も便利だけど折角なのでいい感じにやりたいよね、ということで前回のISUCON12では、Datadog APM を利用してアプリケーションいの解析を行いました。
ただ如何せん個人でDatadog APM 利用するにはかなりコストがかかることが去年の参加の時点でわかっていたため、今年は別の手段を検討しました。
Note
APMはホスト単位での課金になるため、無邪気にチーム練習しているとすごい金額になる可能性がある。
去年の予選後の振り返りで「他の手段はないかな~」とチームメイトと相談し、Google Cloudの Cloud Profiler もしくは、 Elastic APM 、またISUCON時に協賛があり無料で利用できる可能性がある New Relic 、もしくは OpenTelemetry あたりが良さそう、ということで検討をしており、これらを試してみた結果から OpenTelemetry + Elastic APM を採用することにしました。
理由は大きく以下のとおりです。
- Cloud Profilerの場合、エンドポイントごとの解析がし辛い
- pprof的な解析や時間がかかっているエンドポイントは見れるが、DatadogのAPMダッシュボードのような快適さがなかった
- New Relic はAgentの導入が少し手間。Goの場合スパンの設定などが大変だった。
Elastic APM
では ISUCONでよく用いられるsqlx
のクエリ解析がし辛いOpenTelemetry
は任意のバックエンドが選べるため、設定が柔軟にできそう。ISUCONでよく用いられるEchoやsqlxの計測も問題なく利用可能。Kibana
でのAPMの操作感と、ダッシュボード作成が便利。- 単純に自分で
ElasticSearch
をホスティングして使ってみたい。
Note
NewRelicでの計測に関しては、echoのMiddleWareを実装することでよしなにできた可能性はありますが、SQLの解析部分が大変であったため採用しませんでした。
ということで OpenTelemetry
+ Elastic APM
の構成でISUCONアプリケーションの計測をすることに決めました。
最終的な計測環境の構成は以下のようになりました。
計測環境の構築としては、以下のようになっています。
-
otelのバックエンドはES APMにする。もしかしたらnew Relicなどに切り替えるかも (試した結果、Elastic APMでよいな、となりましたが)
-
nginxからの分散トレーシングはしない
- nginxへのotelの導入ハードルが高い。練習段階では、nginxのバージョンが違うと動かない、ということが多々あり断念。
- Pathの正規化も少し手間がかかる、負荷分散するのも終盤になるのでnginxからトレーシングシなくてもいいはず
-
ホストのメトリクスはhtopで観察する
- metricbeatを入れてES側で観察するのは少しラグがある。ベンチ中にリアルタイムで負荷状況を見たい。
- netdataを入れることも考えたが、見る項目も限られているのでhtopで十分そう
-
logをAPM情報と結びつけてKibanaからぽちぽちして見たいが...
- データ量があまりにも多いので諦め。
- ユーザの周遊情報の解析まではやらないだろう...。初期化処理時のエラーさえ分かればいいのでjournalctlでログを適宜観察。
ちなみに、ES+Kibanaを動かしているサーバーでAPMの状況や絞り込みのクエリを実行するときに不便に感じないようにするため、解析用サーバーはそれなりのスペックのものを利用しています。
Note
これくらいのインスタンスを使うのであればDatadog APMを使ったほうが楽だったのでは...!?
個人的にAnsibleを利用したかったので採用しました。
- Ansibleでgithub用のデプロイキーの設定やsshの設定をしてくれるシンプルなPlaybookを作成
git init
あたりは、ignoreの設定も必要だったり、サーバーのディレクトリ構成が変わる可能性もあるのを考慮して手でやることに
当日・練習のために、Makefileを /home/isucon
下に配置し、 make deploy
や make restart
などのコマンドで必要なアプリケーションを一括で操作できるように準備しました。
デプロイは複雑なことをせずに、競技サーバー内で make deploy
を実行すると GitHubのmain最新状態に更新して、go buildを実行 + 各種再起動 をするような処理にしています。
また、チームで各自が開発しやすいように make deploy.xxx
とすると任意のブランチをデプロイできるようにもしています
チームで使うPrivate Repositoryを作成して、その中でノウハウを整理しました。
-
いろんな技術調査、チーム練習時は、Discordで連絡共有 + issueにまとめ作成
- Issue上で乱雑にメモを残しながら作業する。
- ある程度内容がまとまったら 後述のようにDiscussionに転機。
-
決まった手順(初期構築手順や、otel導入、各種秘伝のタレ的な設定ファイル) は Discussion に整備
- Private Repositoryの場合はWikiが使えないので。
- Wikiにすると階層構造のメンテが面倒なので、小規模なノウハウ共有であれば Discussion に入れてもいいと思ってる。
- そろそろ準備するか~!ということで OpenTelemetryなどを調べ始める。
- ISUCON本を改めてパラパラと読む
- ISUCON参加登録の素振り (参加登録の素振り!?)をする
-
無事に参加登録できたので、友人「ISUCON興味ない??」と誘う
-
過去のISUCONの資料をメンバーと一緒に見ながらモチベを高める。
- この辺の資料
- ISUCON 夏期講習 2020
-
一緒にISUCON11の環境を構築して各自で練習したりする
- ISUCONの環境構築、便利なツールはたくさんあるけどなんやかんやでインフラ経験がない人がやるとつまりがち。
-
チーム練習で ISUCON11予選をやる
- 本番を想定してクラウド上に環境を構築(Bench + 競技サーバーx3)して、模擬練習
- SSHって!?ポートフォワーディングって!?? DBに接続するのってどうやるん?みたいなのをチームメイトとワイワイしながらやってみる。楽しい。
- OpenTelemetryとかKibanaもこのタイミングでチームメイトに使ってもらう。
-
チーム練習で ISUCON12予選をやる
- 再度チーム練習。sqliteまじ!?とか言いながらワイワイする。
- 初動 + ドキュメント・コードを読んでドメイン知識把握 + N+1とか実際に潰していく などを練習
- 当日用の便利Makefileなども整備
- チームでのブランチの運用方法も練習で整備
といった流れで個人練習 + チーム練習 をやりながら準備してきました。チーム全体でワイワイできるタイミングがあまり取れなかったので個人練習 + discord でPRベースでやったことを各自共有、などをしながら当日まで練習を進めています。
「せっかくなので家に集まってオフでISUCONに参加しよう!」ということで、チームメイトの家にお邪魔して環境構築 + 最後の練習。ISUCONのために家からPC3台(開発用+予備+予備2)、サブディスプレイ1枚、キーボード類を持ち込んだので実家に帰るよりも大荷物になりました。
家にお邪魔してPCや各種機材の配置をしながら、「明日は楽しみやね~」とスクリプト類の整備と当日のスケジュール整備をします。前日の準備のうちにチームで決めたことは、以下です。
- 朝は頑張って起きよう(一番大事)
- コミュニケーション取りながら褒めあってモチベをアゲ↑↑ていこう
- 楽しんでいこ~~
また、Otel、ESサーバーのアクセス制限の設定や ESサーバーのディスク容量の確認などを行って準備ヨシ!ということで解散しました。
ということでワクワクしながら当日です。
- 無事に全員起床できたので集合
- 運営のDiscordと、ISUCONのYoutubeチャンネルをみながらソワソワする
-
YouTube配信をみながら、『動画配信サービスの高速化とかだったりして~』みたいな話をする
- UDPとか動画プロトコルを扱うってこと!?やばくね?みたいな話をした
-
ISUCON13 問題紹介のムービーが流れる
- こんいす~~。かわいい~~~!
- え!??動画配信サイトじゃん!??マ???
- マジ!??と全員で驚く。
-
問題内容に驚きましたが、おちついてサーバーにアクセス + 初期構築。
- sshのconfファイルを書いてチームメイトに共有。
- DBの接続情報などをソースコードやenvファイルから改修してメモ。
- マニュアルのリンクとかもすぐに見れるようにまとめる。
-
CloudFormatinでサーバー構築して全サーバーにBench実行で挙動を確認
-
htopで負荷をみてDBの負荷が支配的なことを確認
-
リポジトリ初期化時にDNS用のファイルがある!?と驚き。ひとまず頭の片隅においておく
-
去年は 『git init 時に初期データを吹き飛ばして CloudFormation実行し直し』という大惨事をやらかしたので緊張しながら設定。
-
事前にAnsible化 + 手順書を準備しておいたのでスムーズに完了
-
自分が初期化しているうちに、
sagawao
,sagawa4425
にマニュアル把握を依頼。- ssh port forwardでローカルで画面開いて確認、などもやってもらった。
- 画面を開いて確認してもらうことで、ドメイン知識を把握してもらいやすくなる。
- 競技時は急いで改善したくなるが、初手で画面確認は絶対やった方がいい。
- EchoとSQLのOtelトレーサーを導入する。スコアが下がるがまあそんなもん。
- 自分がotel入れるまでの間に、
select *
のようになっていて不要なデータをDBから取っている箇所を少しずつ減らしてもらう
- KibanaでAPM情報をみながら、発行数が多い・実行時間が長いSQLに効くindexを貼っていく
dsn = dsn + "&interpolateParams=true"
を入れる
今回の/initialize
処理ではテーブルがDropされないのでIndexをどう貼るか悩む
- sqlで冪等に実行したいよ~~(IF NOT EXIST的な)。と思い、ChatGPTに聞きながら記法を調べて30分くらい悩んでしまう。
- 結局つくれなかったので、手で実行 + sqlファイルを作成して貼ったIndexをメモ。
- こんなところで悩んで時間を使うんじゃなかったとすこし後悔。
ブランチデプロイしてBench -> Bench OKだったらPRをマージ
というチーム運用にしよう!としていたのですが、mainブランチをデプロイすると整合性チェックでコケて Score 0 になってしまう、という状況に。- 直近で入ったPRをRevertしてももとに戻らず、「なんで!??」と言いながら原因調査をする。
- failする対象のコミットが特定できずかなり焦る。コミットを巻き戻したり、DBを初期化したりして調査。
- コミット履歴をみると、mainに直Pushされたコミットが!!!ひえ~!!!
- この辺の調査で1時間くらい使って、気づいたら13:30くらいに。
チームメイトと相談して、初期状態のブランチを自分のローカルからforce push し歴史改変を行うことでなんとか解決しました。
- チームメイトがKibana動かなくない?と気づく
- まさか!??と思って調べると、モニタリング用に使っていたElasticSearchのディスクがフルに。
- まじかよ~~!!と言いながらVolumeを初期化、ES初期化用のスクリプトを流し込んで20分ほどで復旧。
前日にディスク容量をチェックした際に、(あと10%も
あるし大丈夫だろ)、と思っていたら悲惨な目にあいました。
事前に手順を準備していたので問題なく分離。 分離するとスコアが上がったので嬉しい。
分離したつもりなのに、アプリケーションサーバーのMySQL負荷が高くてなぜ??となる
- PowerDNSのバックエンドがMySQLなので、負荷が高い、ということにここで気づく。
このあたりで ISUPipe
-> イス + 管
-> ISU + Tube
-> YouTube
ってこと!?と気づく。
自分がgitを直したりElasticSearch直したりしている間に sagawa4425
がN+1を改善してくれてスコアが管理上がる。チームみんなで小躍りする。
- みんなですこしずつ改善するが、なかなかスコアが上がらない。
- そろそろDNSと向き合うか...となる
sagawao
がタグ検索の処理を改善してくれる。スコアが大きめに伸びて1万に到達。- 名前解決数が大きく増えて
6500
に
- powerDNSはアプリケーションサーバーと同居させたまま、DBだけを分離してみるのを試す
- 試した見たら大きくスコアが下がったので切り戻してしまう。
- あとになって思うと、名前解決数が増えたせいでアプリが重たくなったとか?コンテスト時は焦ってたのでそこまで判断できずに切り戻してしまう。
-
そういえば、ベンチの向き先と環境変数のpowerDNSの向き先の関係がおかしくない?と思いマニュアルや設定を読み込む
- ここで、ベンチの向き先はDNSサーバーである、ということをに気づく。
- 早く読んでおけばよかった。。。
-
PowerDNSの設定を変えたりしながら分離できないかを試してみる
- 設定がうまく行かず難航。あきらめる。
- AppとDBを分離した都合上、チーム3人がそれぞれ別サーバーで開発するのがしづらくなっていた。ほかメンバーのベンチの隙間を縫いながら設定検証するのが難しい。
早いうちに再起動試験を実施。無事に再起動後にベンチが通ったので一安心。
- 負荷になるので、パッと止められるものを止める。
- が、スコアあがらず。PowerDNSがリソースを食ってそう。
- 再起動試験は大丈夫そうなので、時間ギリギリまでコミットとベンチを回します。
- が、やはりスコアは上がらず。むむむ...。
というわけで競技終了。対戦ありがとうございました。
終わった直後にみたKibanaの画面はこんな感じ。
最終スコアはこうなりました。1万を超えなかったのが悔しい!
最終スコア : 9938
順位 : 255
なお、チーム内の最高スコアは 11223 です。
名前解決数、ぜんぜん違うじゃん。
今年で4回目、ということで前回よりもやれることは増えたし、Goでの実装もある程度は支障なくできたかなと思いますが、実際のところスコアがあまり伸ばせなかったな、という悔しい思いが強いです。練習不足だと思う点もありますし、普段からの実装力・インフラ設定の経験、知識不足を痛感しました。
OpenTelemetryによる計測 -> ボトルネック発見 -> 改善 のイテレーションはチームメンバーと協力しながらいい感じに回せたかなとは思います。改善力が足りないのはやはり練習+普段の実装力がなかったですね。
gitのmain直Pushやブランチ運用、ESのディスクフルなど、業務でも起こりかね無いなぁ...という大きなトラブルをISUCONで経験できたのはある意味ラッキーだったかなとは思います。(それでも、それぞれ事前に十分準備できていれば防げたはずですが...。)
こういったハプニングがありながらも、チームメンバー間で「大丈夫だよ~!」「N+1の改善、天才!」「もう8000点もいったよ!!すごいじゃん!」などと冷静にさせてくれたり、モチベを保てるようなやり取りをしてくれたのも感謝です。ワイワイとしながら本当に楽しい8時間でした。
(途中でハプニングはありましたが) 練習時点からOpenTelemetryによるモニタリング環境の構築もして、運用もできたので得られるものは多かったです。今回はKibanaでのダッシュボード構築やパフォーマンス解析をしてみましたが、このあたりの経験は、現時点でも実業務に非常に役に立っています。
OpenTelemetry+ElasticSearchの運用はなかなかハードだったので、次回参加する場合はDatadogを利用してもいいかなぁとは思っています。こうやって人はマネージドサービスのありがたみを知ることができるんですね。
さて、今回計測に使用したElasticSearch、Otel関係やMakefile、Ansibleスクリプトなどはこちらに整理しています。KibanaのDashboard用のndjsonも入っているのでこれですぐにISUCONで戦える!
※ただ、まだ十分に整理できていないので直していきます...
それでは、来年もまた参加するぞ、ということで。おついす~~。