スタディサプリ Product Team Blog

株式会社リクルートが開発するスタディサプリのプロダクトチームのブログです

スタディサプリの開発運用を支える GitHub Actions

こんにちは。SRE の @int128 です。

スタディサプリのプロダクト開発では GitHub Actions を積極的に導入しています。本稿では、日常の風景に溶け込んでいる GitHub Actions を紹介します。

プロダクトの開発運用を支える GitHub Actions

リポジトリとオーナーシップ

我々は monorepo を採用しており、すべてのマイクロサービスを同じリポジトリで管理しています。リポジトリディレクトリ構成は下図のようになっています。

.
├── .github/workflows/
│   ├── マイクロサービス--test.yaml
│   ├── マイクロサービス--deploy.yaml
│   ├── ...
│   └── reusable--横断的関心事.yaml
├── マイクロサービス/
│   ├── README.md
│   ├── プロダクションコード...
│   ├── テストコード...
│   ├── Dockerfile
│   └── kubernetes/
│       └── マニフェスト...
...

各マイクロサービスにはオーナーシップがあります。サービスオーナーは、自分が所有するマイクロサービスに対して技術選択の決定権を持ち、保守運用の責務を負っています。一方で、開発組織全体で技術選択や技術的負債などの横断的な問題(横断的関心事)を改善していくために、技術戦略グループを設けています。詳しくは下記を参照ください。

blog.studysapuri.jp

GitHub Actions についても、マイクロサービスに関するワークフローはサービスオーナーが管理しています。Kubernetes に関するテストやデプロイなどの横断的関心事のワークフローは SRE がオーナーシップを持っています。なお、 actions/*aws-actions/* などの横断的に利用されている Action は SRE がまとめてバージョンを上げることもあります。

コードの変更をテストする

好きなエディタでコードを書きましょう。

Pull Request を作成すると GitHub Actions でテストが実行されます。スタディサプリで導入されているテスティングフレームワークや静的解析ツールなどには以下があります。

  • Ruby (RSpec, Rubocop, Standard Ruby)
  • TypeScript (Jest, ESLint, ...)
  • Go test
  • Rust test
  • Pytest

一部のフロントエンドでは VRT (Visual Regression Testing) が導入されており、ブラウザの画面に差分がある場合はコメントで通知されます。

ソースコードと自動生成コードが乖離している場合は自動的に修正がコミットされるようになっています。例えば、以下のような修正があります。

  • フォーマッターの修正
  • 静的解析ツールの修正
  • データベースに関するコードや定義
  • GraphQL に関するコードや定義
  • OpenAPI に関するコードや定義

我々は、アプリケーションコードと Kubernetes マニフェストを同じリポジトリで管理しています。Kubernetes マニフェストについては Pull Request で以下がテストされます。

  • マニフェストが信頼性やコストなどに関するポリシーを満たしているか検査する (Conftest)
  • マニフェストの型が正しいか OpenAPI スキーマを利用して検査する (Kubeconform)
  • kustomize build の結果の差分をコメントする

アプリケーションコードが成長するにつれて、テストの所要時間や安定性が大きな課題になってきます。そこで、我々はテストケースの所要時間や失敗数を GitHub Actions から Datadog に送信して、継続的にモニタリングできるようにしています。サービスオーナーは Datadog Dashbodard で長時間のテストケースや Flaky なテストケースを確認できるようになっています。

Pull Request を動作確認する

Pull Request に deploy ラベルを付けると、動作確認用の独立した環境(PR 環境)がデプロイされます。

PR 環境にデプロイされる内容は以下のようになっています。

  • Pull Request で変更しているマイクロサービスは、変更内容がビルドされてデプロイされます
  • Pull Request で変更していないマイクロサービスは、メインブランチの内容がデプロイされます
  • データベースは開発環境と PR 環境で共用しています

PR 環境がデプロイされるとコメントで通知されます。

もし PR 環境のデプロイで問題が起きた場合は、コメントの内容を参考にして Argo CD や kubectl コマンドなどで原因を調べましょう。

本番環境にデプロイする

我々のリポジトリでは、ブランチと環境が対応するブランチ戦略を採用しています。

  • Pull Request は PR 環境にデプロイされる
  • メインブランチは開発環境にデプロイされる
  • release ブランチはリリーステスト環境にデプロイされる
  • production ブランチは本番環境にデプロイされる

マイクロサービスによってデプロイサイクルが異なるため、release ブランチや production ブランチについてはマイクロサービスごとにブランチを用意しています。

  1. GitHub Actions のワークフローを実行すると、メインブランチからリリーステスト環境にデプロイされる。また、本番環境にデプロイするための Pull Request が作成される
  2. リリーステスト環境でテストを実施する
  3. Pull Request をマージする

リリーステスト環境では、自動テストを流す場合とテストベンダーによる手動テストを実施する場合があります。マイクロサービスによっては、リリーステスト環境のデプロイが完了した契機で GitHub Actions から MagicPod や Autify などを自動実行している場合もあります。

マイクロサービスによっては、GitHub Actions で E2E テストを実行している場合もあります。詳しくは下記を参照ください。

blog.studysapuri.jp

本番環境へのデプロイ状況は Slack でサービスオーナーに通知されます。

Terraform でクラウドを管理する

AWSGoogle Cloud などのクラウドの構成は Terraform で管理しています。GitHub Actions で Terraform を管理するために tfaction を採用しています。

クラウドの構成についても、基本的にはサービスオーナーがオーナーシップを持っています。ただし、セキュリティやコストなどの横断的関心事については SRE が統制や助言を行うこともあります。

大量の CI/CD を支える Self-hosted runners

スタディサプリでは2021年に Self-hosted runners を導入してから約3年半が経ちました。現在では、我々の開発運用を支える基盤となっています。

blog.studysapuri.jp

数字でみる GitHub Actions

2024年11月の利用状況を振り返ってみましょう。

  • 2024年11月に実行されたジョブは 36.5 万件
  • 2024年11月に実行されたジョブの累積時間は 15,915 時間
  • ジョブの起動待ち時間は 59.92 秒 (90%tile)
  • Spot Interruption で失敗したジョブは 0.82%
  • OOM killer で失敗したジョブは 0%

Self-hosted runners で実行されたジョブのメトリクスは Datadog dashboard で誰でも確認できるようになっています。

開発生産性とコストへの影響

SRE は定常的にプロダクト開発に携わる機会がないため、開発生産性への影響になかなか気づくことができないという課題があります。そのため、開発生産性への影響を定量的に捉えるために、以下の SLO (サービスレベル目標)を毎週確認しています。

  • GitHub Actions が快適に利用できた時間の割合(ジョブキューに10分以上残ったジョブが5件以内であれば快適と考える)
  • 45秒以内に開始できたジョブの割合
  • インフラの問題で失敗したジョブの割合
  • AWS のコスト

テストやデプロイなどの待ち時間は開発生産性に直結します。理想的には、Pull Request を作成してからすぐにテストやデプロイが完了すると、ソフトウェア開発のフィードバックサイクルを早く回すことができるでしょう。

快適な GitHub Actions を提供するために、以下のような取り組みを進めています。詳細は別の機会に紹介できればと思います。

  • ワークフロー層の改善
    • ジョブの実行契機をなるべく減らすことで、ジョブの起動待ち時間やコストを改善する
    • キャッシュを効率的に利用することで、ジョブの所要時間を改善する
    • Dependabot, Renovate の実行時間を夜間に設定することで、日中のジョブの起動待ち時間を改善する
  • actions-runner-controller 層の改善
    • EphemeralRunner リソースの更新回数を減らすことで、短時間に大量のジョブが到着した場合の待ち時間を改善する(本件は SRE の技術顧問である @mumoshu さんと一緒に検証やフィードバックを進めています)
    • AWS ローカルの APT リポジトリを参照することで、 apt-get コマンドの所要時間を改善する
  • インフラ層の改善
    • 時間単価の安い Spot Instance を採用することで、コストを抑える。トレードオフとして、強制終了したジョブの割合を継続的に監視する
    • コンテナに割り当てる CPU やメモリをオーバーコミットすることで、コストを抑える。トレードオフとして、異常終了したジョブの割合を継続的に監視する

まとめ

本稿では、スタディサプリの開発運用を支える GitHub Actions の日常風景と、それを支える Self-hosted runners の運用をお伝えしました。