こう見えて本業はインフラエンジニア(最近はエンジニアしていない)ウスター商事でございます。
昨年に引き続き、LINE株式会社主催のISUCON(Iikanjini Speed Up Contest)に出場したので、その報告ブログです。
社内システム名を少し変更して、「戦ELGANA(たたかえるがな)」のチーム名で参加しました。
目次 本ページは10分くらいで読めます
ISUCONとは
ISUCON(Iikanjini Speed Up Contest)とはLINE株式会社が運営するWEBページを
イイカンジに高速化することを目的にプログラミングやWEB関連のインフラ構築力を総合的に競うコンテストです。
毎年開催されており、今年で13回目の開催となります。
1~3人のチームで、お題として与えられた激重なWEBページを、限られた計算機資源(CPUやメモリやディスク容量などなど)を使って、限られた時間(8時間)の間で、いかに早く処理させるかがポイントです。
事前にやったこと
- 1.メンバ集め
今回は同級生でクラウドエンジニア屋さん(セインさん)とオンプレミスインフラ+DB屋+NW屋さん(ウスター商事)の2人チームでチームを組みました。
遠隔で画面共有とWEB会議で連携をとる体制で挑みました。
※もう1人フロントエンドエンジニアにお声がけしましたが、日程の都合がつかずに断念。 - 2.事前学習
昨年のISUCON#12を反省に、最低限DB周りは改善できるようにしようということで
達人が教えるWebパフォーマンスチューニング 〜ISUCONから学ぶ高速化の実践
を再度履修しました。
また、過去問を解くためにさくらインターネットさんのレンタルサーバを利用して過去の優良回答を写経して、意味を理解したりしていました。 - 3.事前準備
予選当日に初めての技術や初めてのコマンドは実行できないと考えていたので、
事前学習で打ったコマンドや設定したファイルなどはGithubの事前練習リポジトリに保存していました。
当日のおおよそのタイムスケジュールなどを作成しておきました(午前中まではうまく言ったんですが午後は全然想定外でした) - 4.レギュレーションの確認と生成AIの教育
ISUCON#13のレギュレーションを確認する中で、あえて「開発において、AIを活用したコードの分析、生成等を利用しても構わない。」と記載があったため、事前に過去問題を用いて生成AIがどこまで使えるものか確認していた。
→正しい計測結果を渡せば、ある程度ミドルウェアの設定など改善手順を提案してくれるが
コードベースで具体的な改善を提案することまでは流石に難しいと確認した。
実力的に割り切っていたこと
- 1.ミドルウェアの載せ替えはしない
Nginx↔Apacheとかの載せ替えできるだろうけど、位置から設定することになるので間に合わない可能性がある。
前回の反省(DB載せ替えに失敗して時間を浪費した)からMysql↔PostgreSQL↔SQLiteなどのDB載せ替えもしない - 2.コミットは自由にベンチ回すときだけ声がけ
誰かの作業を待って手が止まることは避けたいので、ベンチマーク実行の前後と結果だけ声をかけて、あとは個々人で喋りながら手を動かす。 - 3.環境を壊さないこと最優先
トップを狙えるほどの実力でないのはわかりきっている、再起動試験や環境試験、レギュレーション違反防止をしっかりと行う。
当日やったこと
- 先に結果だけ報告。我々のチームは
初回ベンチマーク(3,536点)→昼まで(7,214点)→最終(12,715点)で663チーム中209位でした - 9:20 Discordで集合
0.5次予選の全員起床and全員集合に成功。
これはもう勝ちましたね。
約束された勝利でしょう! - 9:40 ISUCONライブ配信視聴
お題となるWEBサービスの説明や環境の説明など。
今回は映像配信サービスということで、ISUCON運営の動画も配信NGになるという、大変凝った演出でした。出題問題の動画は↓
https://www.youtube.com/embed/OOyInZbM85k?si=sNe1NJ6JZ_0-zdXD - 10:00 環境構築スタート
AWS上に3つのサーバを構築し、環境を壊したときのバックアップのため
スナップショットを取得、サーバ内のファイルをローカルとGIthubにバックアップ。
練習の成果もあり10分程度でバックアップ完了して初回ベンチマークを実行完了。
サーバにアクセスするためのSSH鍵の連携で少し手間取るも特に問題なし。 - 10:25 初回ベンチマーク
ログ計測系の基本設定だけ行った上で、初回ベンチマーク(3,536点) - 11:00~13:00 ボトルネックのDBを高速化(3,536点→7,214点)
htopコマンド・slow-query-logの解析からSELECT * FROM livestream_tags WHERE livestream_id = N
SELECT image FROM icons WHERE user_id = N
SELECT IFNULL(SUM(l2.tip), N) FROM users u INNER JOIN livestreams l ON l.user_id = u.id INNER JOIN livecomments l2 ON l2.livestream_id = l.id WHERE u.id = N
というDBへの要求(クエリ)が多いのに効率的でないと判断。
2,3個のINDEXを貼ってはベンチマークを回して、再度計測結果上位のDBにINDEXを貼るを繰り返す作業を実施。
結果的に貼ったINDEXをは下記use isupipe; #isupipeというDBに対してのINDEX
ALTER TABLE livestream_tags ADD INDEX livestream_id_idx (livestream_id);
ALTER TABLE icons ADD INDEX user_id_idx (user_id);
ALTER TABLE themes ADD INDEX user_id_idx (user_id);
ALTER TABLE livestreams ADD INDEX start_at_idx (start_at);
ALTER TABLE reservation_slots ADD INDEX start_end_idx (start_at, end_at);
ALTER TABLE livestreams ADD INDEX user_id_idx (user_id);
ALTER TABLE livecomments ADD INDEX livestream_id_idx (livestream_id);
ALTER TABLE reactions ADD INDEX livestream_id_idx (livestream_id);
ALTER TABLE ng_words ADD INDEX user_livestream_idx (user_id, livestream_id); #事後だが、ng_wordsはDBにINDEXではなくアプリ側でチェック実装することができたかもしれない。use isudns; #isupipeというDBに対してのINDEX
ALTER TABLE records ADD INDEX records_idx1 (disabled, name, domain_id);
【朗報】ISUCON#12の反省を活かして、DBのINDEXを適切に設定できたことは成長の証 - 13:30 昼ごはん食べつつ言語入れ替え
一旦昼ごはんを食べつつ、片手でできる実装言語の切り替えをいくつか試しました。
ウスターはRustとPythonが、セインさんはPHPが得意分野のため、スコアに大きな差がなければ言語を切り替えてアプリ実装を変更する狙いがありました。
【悲報】PHPに切り替えたあと、Nginxの設定ミスで環境壊しました(マジでごめんなさい)
「環境を壊さないこと最優先」とか言いながら1/3台のサーバ環境を破壊する暴挙をやらかしましたので、以降の作業は慎重になりました。 - 14:30 WEB+APLとDNS+DBの2台にサーバを分割(7,214点→10,623点)
・2名の作業を分担しやすくなるため、早々に機能ごとにサーバを分割。
・サーバ1号機(WEB+AP)は
/home/isucon/の配下にenv.shにDBやDNSの接続先などの環境設定ファイルがあったので、宛先のIPアドレスをDBの2号機に変更。
・サーバ2号機(DNS+DB)は
/etc/mysql/my.conf
の設定で、
bind-address=0.0.0.0
にしてどのサーバからでもアクセスできるように変更。
・2号機のDBサーバの設定としてisuconとisudnsユーザがアクセスできるように特権権限の範囲を192.168.XXX.XXXに広げる設定をSQLで追加。CREATE USER
isucon
@192.168.%
IDENTIFIED BY 'isucon';CREATE USER isudns@192.168.% IDENTIFIED BY 'isudns';
GRANT ALL PRIVILEGES ON *.* TO isucon@192.168.%;
GRANT ALL PRIVILEGES ON *.* TO isudns@192.168.%;
→ココでベンチマークの結果から
「DNS水責め負荷が上昇します」の文字が見えたため
初めてDNS水責め攻撃(DDoS攻撃)を受けていることを認識した。
当日マニュアルとアプリケーションマニュアルを再度確認する。 - 14:30~15:30 DNS水責め攻撃への対策検討
・DBの改善(INDEX設定のみ)が少し進んでいることもあったので、
DNS水責め攻撃への対処へ方向転換(今思うとDB検索回数が無駄に多いN+1問題など残っていたので方向転換は悪手であった…)
DDoS攻撃ならば、特定IPアドレスからの通信を遮断することで対処できるはずということで、/var/log/syslog
の結果から、攻撃IPアドレスを特定(セインさんナイスプレイ)
Firewallにてsudo ufw deny from "攻撃元IPアドレス/32"
sudo ufw permit "sshやhttp,httpsなど通したい通信"
sudo ufw reloadsudo ufw enable
で設定と反映を実施。
再度、/var/log/syslog
の結果から攻撃IPアドレスを確認すると攻撃してくるIPアドレスが別のIPアドレスに変わっているではありませんか。(さすがISUCON運営、そんな単純ではない)
・処理能力自体は落ちるが、ブルートフォース攻撃を緩和する意味で/etc/ufw/applications.d/
配下にDNSのフィルタ定義をポート53/udpで作成。
sudo ufw limit DNS
を実行するが効果なし。※効果があったとしてもlimitでかなり性能が落ちたと思われる。 - 15:30~16:30 WEB+APLとDNSとDBの3台にサーバを分割(10,623点→12,864点(ベスト))
・DNS水責め攻撃の負荷とDBアクセスの負荷を分散するために
DNSとDBのサーバを分割実施。
この日のチームベスト点を記録。 - 16:30~17:30 ココからは環境を壊さないようにチューニングの世界へ(11,000~12,000点)
・DNS水責め攻撃の緩和のために/etc/powerdns/pdns.conf
に、DNSクエリのキャッシュ設定実施
後で思うとsyslogに残っていた問い合わせドメインはランダムのため
キャッシュは意味をなさなかった。
・DB(mysql)とWEB(Nginex)のメモリ設定とキャッシュ設定を変更
後で思うと逼迫しているのはCPUであるため
メモリを増やしても効果が薄いことはわかっていた。
あとNginxはボトルネックではないので意味がない。 - 17:30~18:00 コード凍結+ログ出力を止めて再起動試験(最終の12,715点)
・MysqlとPowerDNSとNginxとその他ログ収集ツールをすべてOFFにして
アプリとミドルウェアをすべて再起動実施してベンチマーク実施。
・ベストスコア近くの点数が出たため、ココで終了。 - 18:00~18:30 反省会
- 18:40 賞がもらえるようなスコアではないため、これにて解散。
反省
- 圧倒的な勉強不足だが去年よりはマシ
去年苦戦したDBのINDEXを設定する部分は、2時間程度で達成できたので一定の勉強成果を感じた。
一方でAPLからのクエリを変更できることや、DNSの改善などインフラエンジニアの範疇なのに対応できない部分についてまだまだ実力不足を感じた(悔しがれるのでまだ成長できる)。 - 1変更1コミット1ベンチマークは良い
去年は数カ所変更1コミット1ベンチマークで、数値が変わっても何がよかったか分からず暗中模索していたが、今回は1変更1コミット1ベンチマークを徹底できていた点は非常に解析も改善もわかりやすかった。 - 生成AIはすごい
事前にある程度わかっていたが、当日の解析用ログをまるごと渡してもDBのINDEXやNginexの改善点、DNS水責めの際のFirewall設定(結果的に効果なしだったが…)などかなり適切なアドバイスを出してくれた。2人チームと言いつつ実質は3人チームのような感触であった。 - 最大のボトルネックはやはり私という未熟者。
今回の環境でもDB周り+DNS水責めの処理が支配項だったと思っています。
ウスターがDB改善やAPLからのSQLの改善ができていれば、更にスコアが伸びただろうと思うと、ウスター自身がボトルネックであったと反省するばかりです。
引き続き精進いたします。
最近のコメント