Playerこの広告は、90日以上更新していないブログに表示しています。LifeMemoryTeamの@atponsです。2021も参加いただきありがとうございました。CTFのスコアサーバーとそのインフラ構成についてまとめて反省しようと思います。2021SECCONは日本の情報セキュリティコンテストイベントです。@atponsはボランティアでSECCON実行委員/LifeMemoryTeamという有志の団体のメンバーとしてインフラ周りの整備/NOCを行っています。2019年までは本戦などはオフラインCTFとして実施していましたが2020年よりオンラインCTFとして実施しています。(Jeopardy)1400以上のユーザー/500以上のアクティブチーム(ざっくり集計なので、正確な値は公式のアナウンスでご確認ください)という内容で実施しました。2021をお楽しみいただきました。ありがとうございました。このCTFを実施したスコアサーバーについて実際にまとめていこうと思います。2021で利用した、LMTdを引き続き利用しました。ランキングは基本的にMemcachedから返しますが今回はそれ以外は特にキャッシュはしていません。千と呼ばれる可視化システムへの連携部分の作り込みも行いました。これはGatewayサービスとして別途データベースも分離して開発し、今回は競技時間中Twitchで配信するという試みを行いました。基本的なインフラ構成は前回と同様です。Bitに生ログを全部渡すのと、LokiとPromtailによる可視化のための収集をどちらも行いました。Azureとさくらのクラウドに構築されたインフラを利用させていただきました。この場を借りてお礼申し上げます。利用した技術スタックはこちらです。BitPrometheusさくらのクラウドNext.js今回導入した技術スタックは太字で記載しています。LMTdのバックエンドの開発は、今回については完全に一人でやりました。今回は上手くいくかな、と思って見ましたがやはり開催してみて、反省がいくつかありました。認証周りをほとんど独自実装して載せたこと認証系をバックエンドで処理したことフロントエンドのサポートが手厚いIDaaSを利用したいバックエンドのコードを綺麗にしたいDIするDDDするテストを書く(ほとんどテストがなかった)APIの定義をちゃんとしたいドキュメントをちゃんと書く、フロントエンドライブラリに切り出す普通のコードにする何かあっても他の人が見て分かるコードにするこれらを踏まえて,今回は以下を実施しました。Authenticationを導入してフロントエンドに処理を寄せながら、セッションの払い出しなどはサーバーから行いました。バックエンドのコードを綺麗にするまず、バックエンドのコードを綺麗にするためにDDDのようなアーキテクチャを導入しました。Serviceみたいな自分にしか分からないコードになっていましたが大分他のコードに近づいたのかなと思います。DIをするようにしてテストは簡単にできるようにしました。できるようにしました。今回は前回導入したORMであるentではなくGORMを採用しました。これはentはコードを生成できる良いORMではありましたが、entに関する知識が必要になるため自分以外が触れなくなってしまうという反省があったので今回はごくごく使われているORMであるGORMを採用しました。テストを書くHTTPルーターのテストカバレッジは6割ぐらいをキープしてました。大事なところはとりあえずテストでカバーしていたので競技中に致命的なバグが見つかることはありませんでした。gavv/httpexpectを活用してリクエスト/レスポンスの検証をしていました。今回はOpenAPIを採用し、APIのスキーマをフロントエンドと共有しながら開発を進めていきました。バックエンド側でもフロントエンド側でも同じスキーマを共有して開発できるのはかなりメリットでした。Firestoreを導入しました。Authenticationとの連携はしていませんが、柔軟に対応できたという点で良かったです。クオータに気をつける必要があり、アンケートの回答結果は変化しないことから基本的にFirestoreへはWriteのみを行うように、Readは基本的にはスコアサーバーのmemcachedを経由させています。今回は「他の人がみても分かる」コードにしたかったのでDBのスキーマも想定外のものが完成しないかに気をつける必要がありました。そのため、k1LoW/tblsを導入しました。データベースの現状のスキーマからドキュメントを生成しつつ確認できたのは良かったです。管理画面のNext.jsの利用これはまだ実験的ですが、主催者が見れる管理画面をNext.jsにしました。以前はhtml/templateだったのでそれからはちょっと進化したと思います。この管理画面のAPIについてもOpenAPIで管理しつつやっています。OpenAPIについてはaspida/aspidaでYAMLからクライアントの自動生成が出来て非常に助かりました。CD用のリポジトリでKustomizeのYAMLを新しいイメージにする作業(インフラリポジトリにPRする)のが億劫になっていました。これを踏まえてCIでイメージを生成したら、Kustomizeのイメージタグを書き換えてPRを出すボットを作成しました。上記のような通知とともに、このようなPRが作成されるのであとはマージすればデプロイされるという仕組みになりました。コンテナイメージのビルドにBazelを使ってる関係もあるので自分達でこのようなツールを作りましたが、他でも全然活用できるツールだなと思いました。IDをつけてDBへのクエリするところまでもこれを引き回すとエラーになったところが追いやすくなるのでちゃんとやって良かったです。負荷対策が甘かった競技開始直後、アクセスの急増に伴い数分間アクセスしづらい現象が発生しご迷惑をおかけしました。これは今回入れた競技状態を入れているテーブルへのアクセス増加にともない、アクセスが困難になりました。競技開始後、アクセスが急増するルーターのミドルウェアに競技開始を見に行くコードがあるのでそこでアクセスが増加MySQLのコネクションが増加してMySQLがアクセス不可になる(ネットワークI/O増)CPU、メモリ、ネットワークというあらゆる負荷の増加に伴いスコアサーバーが入っているKubernetesクラスタがやられて、監視系(Grafana/Promtail/Prometheusなど)もダウンという感じでした。数分後にアクセスが落ち着きましたがこれは十分な負荷試験ができてなかったことの裏返しでもあります。検証するべきだったと思います。アクセス急増に伴い最初はKubernetesノードの異常なCPU利用率からノードプールへのノード追加で収まると思い、追加しました。結果的に原因はデータベースへの接続部分でしたが、こうしてすぐ負荷をオフロードできたのはKubernetesの良かったところかなと思います。開始直後と終了直後のアクセスすごい...適切なアーキテクチャだったのかどうかが分からない開発は一人だったのでこれが正解だったのかは分かってません...。個人的にはいいなと思う設計もいくつかありましたので収穫にはなりました。2021のスコアサーバー(バックエンド)編を振り返りました。2021電脳会議の19日のセッション(12:30-13:30)でもお話できたらと思います。他にも何か思い出したら書こうと思います。(TCP)機能をxDSサーバを実装しながらまとめてみる最近Envoyを少しだけ触ってみている。Envoyは基本的にIstioのPr...2020-12-232020年まとめ(大学を卒業して社会人になりました編)こんにちは、atponsです。2019年書いてなかった・・・。8カスもといゴミではないがリンク。(メインブログ)参加グループうごメモはてなグループはてなブログをはじめよう!atponsさんは、はてなブログを使っています。ブログを報告する引用をストックしましたストック一覧を見る閉じる引用するにはまずログインしてくださいログイン閉じる引用をストックできませんでした。再度お試しください閉じる限定公開記事のため引用できません。