🚤

[ISUCON14体験記] GitHub Actionsを用いたデプロイ自動化

2024/12/17に公開

はじめに

こんにちは! 横浜国立大学大学院 環境情報学府 修士1年の @Shion1305 です。

今年11月からLayerXのDevOpsチームに実務インターンとしてJoinさせて頂いており、LayerXのクラウドインフラやCI/CDの改善に取り組んでいます。

この記事は、LayerX Tech Advent Calendar 2024 の 15日目の記事です。

先週末(12/8)開催されたISUCON14に初参加しました。普段ハッカソンで一緒に参加している友達と3人で a-company として参加しました。

結果は、全体で282位/831チーム、学生で30位/100チームでした。今回は体験談も兼ねて、ISUCON自体初参加なりに工夫したことなどについて紹介したいと思います。

競技前に行ったこと

ISUCONのキャッチアップ + private-isu (〜6日前)

初参加ということもあり、そもそも「ISUCONって何?」という状態からスタートしました。
そのため、ISUCONのキャッチアップとしてISUCON本を読み進めました。この本で紹介されている模擬ISUCON環境「private-isu」を構築し、以下のようなツールやインフラの設定方法を学習しました。
CLIツールは、競技開始時に一度に導入できるようにスクリプトをまとめたりもしました。

  • 各種CLIツール
    • alp: アクセスログの解析ツール
    • mysqldumpslow pt-query-digest: MySQLのクエリ解析ツール
  • インフラの設定
    • MySQLのクエリログの出力方法
    • Nginx のログ設定 / 各種設定値
  • New Relic
    • 結果的に採用しなかったが、New Relicで監視を全てまとめることも検討した。

達人が教えるWebパフォーマンスチューニング 〜ISUCONから学ぶ高速化の実践
ISUCON本

AWSのEC2インスタンスのコストを大きく見積もっていたため、練習環境はAWSではなくOracle Cloudの無料枠を用いてセットアップしました。Oracle Cloudでは4vCPU/24GBメモリが無料枠で使え、そこから3インスタンス構築を行いました。ISUCON向けのcloud-initが公開されており、cloud-initをTerraform経由で用いて環境構築を行いました。

Oracleの無料枠で提供されるインスタンスはARM環境であるため、ARMでセットアップする必要があり、環境構築でかなり手間取りました。(ISUCONの標準環境はx86) cloud-init の実行に何度か失敗し、かなり時間を浪費してしまいました。

結果として、アプリケーション側に本格的に取り組む時間を十分確保できず、本番まで一回もキャッシング等アプリケーションの改善にしっかり取り組む時間を割くことができませんでした。

「練習の時だけ動かす+使っていない時は止める」という運用にしておけば大して費用はかからないため、多少コストがかかっても練習環境はAWS上でAMIを利用して立てた方が無難だと思いました。

ISUCON13でのキャッチアップ (本番6日前〜)

気づけばすぐISUCON14直前になりました。ISUCON13を本番前に一度体験したいと思い、ISUCON13はAWS上でAMIを使って構築しました。

private-isuを通して行った、計測ツールやインフラの設定や一部コード改変は試せたものの、どのようにキャッシュを行うか、どのように複数台運用するかなどについては触れることができず、ISUCONでの改善フローを一通り経験できないまま本番を迎える形となってしまいました。

また、 private-isu ではGoコードが1ファイルにまとまっていますが、直近のISUCONではしっかり分かれていたりと、実際の本番環境の構成も直前で知ることになり、準備不足を実感しました。とはいえ、直前でISUCON13に触れたことで少しでも本番の状況をイメージできたのはプラスでした。

LayerXではISUCONに参加していた社員は多く、インターンの合間にISUCONに関して歓談する中で新しい情報やノウハウを得ることが多くありました。

本番当日

当日の流れ

当日は以下の体制で臨みました。

  • インフラ担当:1名
  • アプリケーション担当:2名

Discordで常にボイスチャンネルに入って画面共有などしながらコミュニケーションを取り、PRベースでアプリケーション改善を進めました。PRをMergeした後にベンチマークを走らせ、スコアが向上しない時はRevertするという運用で改善を進めていきました。

今回のサーバー構成はAPIサーバー1台+DBサーバー1台の2台構成にしました。競技時間は10:00〜18:00でしたが、ざっくりとした自分の作業タイムラインは以下の通りです。

時間 作業内容
10:00〜11:00 環境立ち上げ、Cloudformationでの問題への対処、GitHub運用体制の準備
11:00〜12:30 Nginx・MySQLのログ設定、インデックスの付与、複数台運用化
12:30〜13:30 スロークエリ・アクセスログ解析
13:30〜終了まで アプリコード修正とデプロイ管理・ログ収集

序盤のインデックス追加や複数台運用への移行、通知機能のJSONからServer-Sent Eventsへの移行では一気にスコアが改善し、一時は学生チーム3位まで浮上しました。しかし、その後のアプリケーション改善が思うように進まず、スコアは伸び悩みました。それでも、改善フローを一通り経験したことのない状態を考えるとまずまず健闘できたかなとは思います。

スコアの推移
スコアの推移状況

遭遇したハプニングたち

その1. CloudFormationで環境立ち上げができない…!

開始直後、提供されるCloudFormationで環境を立ち上げようとしたところ、何度試しても1インスタンスしかPublic IPが割り当てられない問題が発生しました。デフォルト設定のままではリソース作成失敗時にロールバックが行われ、成功分も巻き戻されてしまうため、「プロビジョニング失敗時の動作」を変更してリソースを維持される状態にし、チームが作業できる状態にしました。

結果的には、EIP(Elastic IP)のクォータ不足が原因だったようで、練習で立ち上げたISUCON13のリソース削除が不完全でEIPのクォータが枯渇していたことが原因のようです。開始数十分はこの対応に割かれました。

その2. DB周りのエラーで2時間以上ストップ

14時から16時半まで、原因不明のDB周りのエラーに足止めを食らいました。

既存実装ではSQLで移動距離計算を行うエンドポイントがボトルネックでした。そこで、DBに新しくカラムを追加して計算結果を格納することで負荷軽減を図ろうとしたのですが、ORM側でエラーが発生しました。2人がかりで対応しても解決できず、多くの時間を使ってしまいました。

大きな性能改善が見込める箇所だったため突っ込んだのですが、結果的に2時間以上の開発中断は痛手でした。もっと素早く方針転換する判断や、ローカル環境でスムーズにデバッグできる体制が整っていれば、短時間で見切りをつけて別の改善に取り組めたかもしれません。

GitHub self-hosted runnerを用いた自動デプロイ

今回私たちは、GitHub Actionsのself-hosted runnerを用いてアプリケーションのデプロイ作業を自動化しました。その結果、チーム内でのデプロイ作業が属人化することなく、開発効率の向上を実感しました。

実際にはそこまで大層なことはしていないのですが、過去の体験談や他のチームのレポジトリなどでは自動デプロイに関する記載が見当たらなかったので、今回取り上げたいと思います。

self-hosted runnerについて

GitHub Actionsは、デフォルトではGitHubが管理する実行環境上でジョブを実行します。しかし、自分で用意したサーバーをGitHubに登録して、その上でジョブを実行するように設定することも可能です。この仕組みが「self-hosted runner」です。今回はISUCONのサーバーをself-hosted runnerとして登録し、GitHubをトリガーとしてデプロイ作業を自動実行させました。

self-hosted runnerのセットアップは非常に簡単で、GitHubのレポジトリ上で Settings > Actions > Runner > New self-hosted runner を押すとセットアップ手順が表示されます。
Linux x86 を選択して Download Configure で表示されているコードを実行すればセットアップ完了です。

self-hosted runnerの設定画面

Configure の最初のコマンドでインスタンス名を聞かれますが、今回はアプリケーションを実行するインスタンスを isu14-1 として登録しました。

./run.sh の部分だけ nohup ./run.sh & で起動しました。
※最終のベンチマークでは一度インスタンスが再起動されるため、self-hosted runnerによる負荷は考える必要はありません。

デプロイ用のWorkflow

今回のActionsは即興で作成したこともあり、そこまで複雑なことは行っていません。YAMLファイルもたった20行程度です。

name: Deploy Backend Application
on:
  push:
    branches:
      - main
jobs:
  deploy:
    runs-on: isu14-1
    steps:
      - name: Deploy
        run: |
          cd /home/isucon/webapp/go
          # ローカルのファイルを同期させたかったため actions/checkout は使用しなかった
          git pull
          sudo systemctl stop isuride-go.service
          go mod download
          go build .
          sudo systemctl start isuride-go.service
      - name: Check status
        run: |
          # しっかり稼働しているかをstatus codeで確認
          systemctl is-active --quiet isuride-go

今回自分のチームでは、アプリケーション(~/webapp)のみをGitの管理下においていましたが、NginxやMySQLの設定ファイルもGitで管理しているチームもあり、それらのデプロイもGitHub Actions経由で実行できると面白そうだなと思っています。

まとめ

インデックスやキャッシュといったパフォーマンスチューニング手法にはある程度の知見があったものの、ISUCON14を通じてチューニング結果がスコアに直結する体験は非常に新鮮でした。
また、友達とチームで8時間、緊張感を保ちながらみっちり没頭できた体験はとても充実感がありました。

しかしながら、実装改善の手法・戦略についてチーム内で認識を合わせられていなかったなどの準備不足は大きな反省点です。

self-hosted runnerによる自動デプロイ施策は特に今回有用性を感じました。今回私たちのチームはアプリケーションの更新のみでしたが、サービス定義やインフラの設定の反映などに対してもより快適に開発できないか検証してみたいと思っています。

ISUCON14のスコアボードはまだまだ来年1月17日まで解放されているということなので、今回時間がなくて手をつけられなかった実装改善策や3台構成の対応、CI/CDなどについて研究しつつ、ISUCONを楽しんでいければと思います。

お知らせ

現在、LayerXではインターン・中途問わず、通年で積極的に採用を行っています!
プロダクト開発職(バクラク事業部)や、リサーチエンジニア(AI・LLM事業部)など、多様な職種で新たなメンバーを募集しています。
興味のある方は、ぜひご覧ください!
https://layerx.co.jp/jobs
さらに、1月10日(金)には「LayerX Internship Tech LT #3」 を開催します!
LayerXの実務インターンがどのように進められているのか、具体的なイメージをつかめる絶好の機会です。
エンジニアを目指す学生の皆さん、ぜひご参加ください!
https://layerx.connpass.com/event/339780/

Discussion