スタディサプリ Product Team Blog

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

スタディサプリのWebアプリケーションはこうやって開発されている

こんにちは。Webアプリケーションエンジニアの @ttokutake です。

今回はスタディサプリのWebアプリケーション開発がどのように行われているかをざっと紹介したいと思います。

スタディサプリ 大学受験講座 の開発の話がメインです。 スタディサプリ 中学講座 は今回の話とはやや異なる部分もあるため、それはまた別のブログ記事で紹介されるかもしれません!

スタディサプリ ENGLISH は全く別物のシステムです。 そちらの開発については こちらのサイト のブログ記事を読むといろいろと雰囲気が掴めそうです。

Webサービスの構成

まずはWebサービスの構成を紹介します。

右半分にはDBを共有している複数のWebアプリケーションが描かれています。 これらは歴史の長いWebアプリケーションがそれなりにあり、いわゆる「分断されたモノリス」状態となっています。

左半分にはマイクロサービス群が描かれています。 モノリスから一部の機能が徐々に切り離されてWebアプリケーションやWeb APIサービスが作られることで、昨今ではその数をどんどん増やしています。

画像には描かれていないWebアプリケーションやWeb APIサービスもあり、その総数は80くらいです。

インフラストラクチャー

今回はさらっとした紹介にとどめておきますが、WebサービスAmazon EKS 上で動いております。 Kubernetes を使うようになっていった歴史は他のブログ記事でも紹介されています。

いろいろな環境

スタディサプリのWebサービスはいろいろな環境で動いています。 その環境についてざっくりと定義を説明します。

  • 本番環境
    • エンドユーザーに使っていただくWebサービスが動いている環境です。
    • production環境とも呼ばれます。
  • edge環境
    • 本番環境にかなり近い状態でWebサービスが動いている環境です。
    • CSチームの方々が問い合わせの状況確認などで利用します。
  • ステージング環境
    • develop環境
      • 開発された機能が続々と反映される環境です。
      • 開発者が動作確認をするときにメインで利用されます。
    • PR環境
      • あとで説明しますのでお待ちください。
      • DBはdevelop環境と共有しています。
    • release環境
      • 本番環境へ変更を反映する前にQAをするための環境です。

図にすると以下のような感じです。

特に赤枠で囲った部分については、今後の説明で関わってきますので「ふーん、そうなんだ」くらいに認識しておいていただけたらと思います。 他にも用意されている環境はありますが、これ以上は混乱の元になるので紹介は差し控えます。

こういった環境の中、どのようにWebサービスの開発が行われているかをご紹介します。

ソースコード管理

ソースコード管理には Gitホスティングサービスには GitHub を使っています。

そして、ほとんどのWebサービスが一つのリポジトリーで管理されています。 つまりMonorepoです。

以下のように各Webサービスディレクトリーがトップレベルにあります。 その中にアプリケーションのコードやDockerfile、Kubernetesマニフェストなどが置かれています。

monorepo
├── service_a
│   ├── Dockerfile
│   ├── Gemfile
│   ├── Gemfile.lock
│   ├── app/
│   ├── config/
│   ├── kubernetes/
│   ├── package.json
│   ├── spec/
│   └── yarn.lock
├── service_b
│   ├── Dockerfile
│   ├── Gemfile
│   ├── Gemfile.lock
│   ├── app/
│   ├── config/
│   ├── kubernetes/
│   └── spec/
└── service_c
    ├── Dockerfile
    ├── Makefile
    ├── cmd/
    ├── go.mod
    ├── go.sum
    └── kubernetes/

Webサービスをそれぞれ1つずつのリポジトリーで管理するのと比較して、Monorepoには以下のような利点があります。

  • Gitの1つのbranchをKubernetesのnamespaceとして扱うことでデプロイの管理がしやすい。
    • これによる恩恵はあとで具体的に紹介します。
  • CIなどのコードを共有しやすい。
  • 変更の影響範囲やバグの調査をしたいときにはMonorepoを git grep するだけで済む。
  • 個人の開発環境を構築するときにMonorepoを git clone するだけで済む。

逆に欠点としては以下のようなものがあります。

  • git clone に時間がかかる。
    • shallow cloneでもそれなりに時間がかかります。
    • (一部のジョブなどを除いて)頻繁にcloneをする場面があるわけではないので今のところ大きな問題ではありません。
  • CIがMonorepoに対応していない場合に実行制御が大変な可能性 🈶
    • CIサービスによっては、例えば「 service_b を変更したら service_aservice_b のCIを実行する」みたいな制御の設定に苦労する可能性があります。
    • スタディサプリでは GitHub Actions を使っているので、現在は問題になっていません。

こんな感じで自分としては普段さまざまな恩恵を享受しつつMonorepoで開発をしております。

開発環境のためのDB

スタディサプリでは本番環境のDBからedge環境やステージング環境のDBにデータをコピーしています。 もちろんセンシティブなデータはマスクされています。

先ほど赤枠で囲われていたdevelop環境で使っているDBは開発者それぞれの個人の開発環境からも利用されています。 「そんなにたくさん共有していると作業のコンフリクトが起こらないか?🤔」と疑問が思うかもしれませんが、スタディサプリでは意外と起こりません。 サービスの規模にもよると思いますが、それなりに大きなサービスになってくると思ったほどコンフリクトは起こらないという印象です。

また、本番DBからのデータのコピーは毎日行われています。 これにより常に本番相当の状況が再現されるため、バグの調査においては結構簡単に状況再現できることが多いです。

より詳しい話は スムーズな開発体験を支えるデータベースリストアの仕組み というブログ記事でも紹介されていますので、よろしかったらご覧ください。

開発環境

スタディサプリの個人の開発環境は2パターン存在します。

1つ目は Docker Compose を使った開発環境です。 社内では qall と呼ばれております。 私も入社した当初はこの qall で開発をしておりました。

まずはMonorepoを git clone します。 設定ドキュメントに従って環境変数の設定などをしたあとに docker-compose up service_a みたいな感じでコマンドを打ちます。 すると、すぐさまローカル環境でアプリケーションが立ち上がり大変便利でした。

ただ、以下のような弱点はありました。

  • Macでは Docker for Macのosxfs の読み書きが遅いため、テストの実行が遅かったりする。
    • WindowsWSL2 を使っている開発者にはこの問題はありませんでした。
  • AWS上にあるステージング環境のDBにアクセスするためにポートフォワーディングが必要で通信も遅い。

とはいえ、ローカルマシン上に直接開発環境を構築するより簡単に環境構築できるのは新メンバーのオンボーディングなどで大変威力を発揮しました。 そんな感じで、自分は特に大きな不満もなく快適にデベロッパーライフを送っていました。

より詳しい内容を知りたい方は qall に関するスライド が存在しますのでぜひご覧ください。

しかしそんな qall にも転機は訪れます。 M1チップ の登場です。 これにより qall も動かないところが出てきたりしました。

そこで登場したのが2つ目の開発環境 qall-k8s です。 k8sなので、Kubernetes上で動いているということです。 開発者ごとにKubernetes上にnamespaceを作って、AWS上で開発環境を動かしているという代物です。

qall の弱点も解決されるし、M1 Macでもなんら問題なく開発できるというソリューションです。 現在は私はこの qall-k8s で開発をしています。

コストについては...スタディサプリでは許容できる範囲だったようです。 各チームで必要なWebサービスだけを起動できるようにチームごとにKubernetesマニフェストを分けているような工夫はされています。

エディタでの開発はどうするのかという疑問が出てくるかと思いますが、私は Vim を使っていますのでなんら問題はありませんでした。 sshして vim と打つ程度の感覚で開発できます。 簡単ですね。

というの冗談で、そんなにダイバーシティインクルージョンのない世界ではありません。 例えば VS Code を使っているのであれば、以下の拡張をインストールすることで開発環境が整えられるらしいです。

こんな感じでなかなかにカッコ良い開発環境でスタディサプリの開発は行われています。 qall-k8s についてすごく雑な説明でしたが、もし詳しい内容を知りたい方は入社していただくと話が早いかもしれません。

ちなみに qall を使っている開発者もまだまだいますので、「俺はDocker Composeを愛してるんだよ」という方もご安心してご入社いただけたらと思います。

Pull Requestを出したあとは

コーディングしてテストを書いたら、Pull Requestを出します。 レビュー以降の確認作業におけるスタディサプリの味方をご紹介します。

1つ目はPR環境です。 これはPull RequestごとにKubernetesのnamespaceが作成されて、専用の動作確認環境が用意されているという代物です。 Gitの1つのbranchをKubernetesのnamespaceとして扱うことでデプロイの管理がしやすい。 という利点がここにも活きています。 ここではすべてのWebサービスが起動しているので、どのアプリケーションの動作確認もさっと始められます。

レビューをする開発者やデザインのチェックをするデザイナーの方も動作確認が可能です。 また開発環境と違って本番と同じ設定でビルドをしてアプリケーションを立ち上げるので、より本番に近い状況で動作確認が可能です。 例えば Rails アプリケーションの場合、開発環境では RAILS_ENV=development ですが、PR環境では RAILS_ENV=production です。

Pull Requestを作成すると以下のように「PR環境作ってるぜ」とコメントをしてくれます。

staging-navigator というリンクをクリックすると、以下のようにPR環境の全WebアプリケーションのURLを表示してくれます。 (画像はお見せしても問題ないWebアプリケーションを一部表示しているだけです。)

Webアプリケーションのリンクをクリックすることで、PR環境でのアプリケーションの動作確認ができます。 便利ですね。

コストはどうなの?とお思いの方もいらっしゃると思いますが、 Dependabot などが出したPull RequestではPR環境を立ち上げないような工夫もされています。 また、夜10時にはすべてのPR環境をシャットダウンするようなこともしています。

2つ目はE2Eテストの自動化ツールの AutifyMagicPod です。 ノーコードでE2Eテストのシナリオを作成できるので、開発者以外の人でも触りやすいというのが最大の利点と思われます。

私個人の観点で一番活躍するのはRails Upgradeのような影響範囲の広い修正をしたときの網羅的な動作確認です。 この手の変更はそんなに頻繁ではないですが、それでも定期的に発生するのでやはりE2Eテストが用意されていると安心感が違います。

より詳しい話は以下のブログ記事をご参照ください。

デプロイ

さてPull Requestがマージされたらどのように本番環境までデプロイされていくかをご紹介します。 スタディサプリの主なデプロイ方法は2種類あります。 Weekly ReleaseとSingle Releaseです。

デプロイと言いながらReleaseと名前がついていますが、社内の通称なのでお許しください。 またデプロイとリリースはほとんどのケースでは同時に実施されるので、そんなに名前が間違っているわけでもありません。 のちほどリリースについてもしっかりご紹介します。

Weekly Releaseは主にモノリスアプリケーションやモノリスに依存するWebサービス群をデプロイします。 名前の通り、週に1度デプロイを実施します。

1週間分の変更が入りますので、デプロイ前にQAが実施されます。 水曜日に開始されて、木曜日の13:00くらいにQAが完了すると本番環境にデプロイされるという流れです。

ちなみに、Weekly Releaseを待たずに修正をデプロイしたい場合はHotfixという手段がちゃんと用意されています。 これは好きなタイミングで修正をデプロイして本番環境に反映させる手法です。

Single Releaseはモノリスに依存がないようなWebサービスを個別にデプロイします。 各Webサービスのオーナー(チーム)は好きなタイミングでリリースできますので、リリース頻度についてよりコントロールしやすいです。

Single ReleaseのQAはオーナー次第ですが、先ほど登場したAutifyなどを利用している場合もあります。

リリース

最後にリリースについてご紹介します。 先ほども言及しましたが、ほとんどの場合はデプロイとリリースは同時に行われます。 ただし、それ以外にもリリースの手段は2つあります。

1つ目はカナリアリリースです。 これは先ほど登場したHotfixの一種なのですが、例えば「まずは全体の1%、次に10%、...」のように段階的にリリースをします。 これは Argo RolloutsのCanary Deployment Strategy で実現しています。

カナリアリリース中にエラーレートが上がったらそのリリースを破棄して、Webサービスカナリアリリース前の状態に戻します。

自分のチームがカナリアリリースをよく利用するのはRails Upgradeです。 あるWebアプリケーションのRailsのバージョンをアップグレードしたときにはE2Eテストなども実施していますが、それでも結局本番環境でしか起こらない問題もあります。 そういった問題に被害が大きくなる前に気づけるカナリアリリースは大変重宝されております。

2つ目はDarklaunchです。 これは自前で実装している Feature Toggles の機能です。 雑に説明してしまうと、ある機能を実装してデプロイまではしますが、管理画面などでトグルをONにしないとその機能が見えないようにしておくというものです。

これによりリリースのタイミングをデプロイとは独立して自分たちで自由に決められます。 加えて、リリースしたけどやっぱり戻すみたいなことも簡単なので大変便利です。 ただし、データベースのデータなどは簡単には戻せないので、どんなリリース手段を持ってしてもその辺の注意は必要です。

Darklaunchについてのより詳しい内容は Darklaunchという便利なものと、その未来 というブログ記事をぜひご覧ください。

このようにしてスタディサプリの機能がリリースされていきます。

さいごに

今回はスタディサプリのWebアプリケーションがどのように開発されてリリースされていくかをご紹介しました。 皆さまのご参考になれば幸いです。 今後も開発環境は進化を続けていくと思うので、また何かあればブログ記事にしようと思います。

よくわからないことやご指摘などがありましたら、 Twitter などでお気軽にご連絡ください。

またスタディサプリでの開発に興味が湧いた方や「私が働くイメージが完全に整った」という方は こちらの採用ページ からご連絡ください!