100万行オーバーのモノリシックRailsアプリをマイクロサービス化したクックパッドの手順

マイクロサービスの導入事例を、中の人が徹底的に語ります。クックパッドでは、100万行オーバーの超巨大なRuby on Railsアプリのマイクロサービス化に挑みました。アプリをいかに分離し、連携できるようにするか、など、同社が採ったマイクロサービス化の戦略を聞きました。

100万行オーバーのモノリシックRailsアプリをマイクロサービス化したクックパッドの手順

サービスを構成するシステムを分散・細分化するマイクロサービスアーキテクチャ。この手法を導入する企業は、徐々に増えています。しかし、マイクロサービス化のためのベストプラクティスを見出すのは難しいでしょう。どの企業も手探りの状態で、アーキテクチャ改善の取り組みをしているのが現状です。

では、巨大なサービスを持つ有名IT企業は、どうやってマイクロサービス化を推進してきたのか。その手法を学ぶことで、「マイクロサービス化を行うための道しるべ」が見えてくるのではないでしょうか。今回は、料理レシピ投稿・検索サービスクックパッドの「お台場プロジェクト」と呼ばれる事例を掘り下げます。

聞き手を務めるのは、株式会社ウルフチーフの代表取締役であり、アーキテクチャ設計のスペシャリストでもある川島義隆さん。話し手は、「お台場プロジェクト」の発起人である青木峰郎さんと、プロジェクトリーダーである外村和仁さんです。

「世界最大のモノリシックRuby on Railsサービス」とも言われているクックパッドを、細分化するための手法に迫ります。

1
外村 和仁(ほかむら・かずひと) 2 hokaccha 3 hokaccha(写真右)
ソーシャルゲームのバックエンド開発やWebフロントエンドの開発業務を経験後、2015年にクックパッドに入社。レシピサービスのサービス開発に数年携わった後、現在は技術部クックパッドサービス基盤グループにてモノリシックで巨大なRailsアプリケーションのアーキテクチャ改善業務に従事している。著書に『ノンプログラマのためのJavaScriptはじめの一歩』など多数。
青木 峰郎(あおき・みねろう)(写真中央)
ふつうの体育会系プログラマー。大学在学中にRubyをはじめとしたOSS開発にどっぷりつかる。2007年より並列分散RDBMSベンダーに勤務、並列処理に目覚める。2013年にクックパッド入社(現職)、データ分析サービス「たべみる」をエンジニアほぼ1人で実装。現在は技術部データ基盤グループに所属し、全社共通データ分析基盤の構築リード、お台場プロジェクトPMなどを幅広く手掛ける。『ふつうのLinuxプログラミング』『10年戦えるデータ分析入門』はじめ著書多数。
川島 義隆(かわしま・よしたか) 4 kawashima (写真左)
株式会社ウルフチーフ 代表取締役。TIS株式会社にて19年半、様々な業種のシステムアーキテクチャ設計を担当し、2018年に退職、株式会社ウルフチーフを創業する。以降流しのアーキテクトとして、前職時代から書き溜めていたOSSプロダクトや技術記事を元に、様々な現場でアーキテクチャの設計や研修を実施している。

Ruby on Railsのバージョンアップに1年かかっていた

川島:今回のインタビューでは、「お台場プロジェクト」についてのお話を伺いたいです。このプロジェクトは何を目指したものだったのでしょうか?

外村 「お台場プロジェクト」は、cookpad_allというリポジトリのアーキテクチャ刷新・改善を目指したものです。これは、クックパッドが cookpad.com のドメインで提供しているレシピサービスです。みなさんが、いわゆる「レシピサービスのクックパッド」と聞いてイメージするものになります。

cookpad_allのリポジトリ内には、WebアプリケーションやAPIサーバ、管理画面、バッチなどのシステムが含まれています。これらのコード一式が「世界一のモノシリックRuby on Rails(以下、Rails)サービス」と呼ばれているものです。

コード行数としては、Rubyのコードが約27万行(テストを除く)、テストコードが約51万行、HTMLテンプレートが約14万行。つまりトータルで100万行ほどありました。

5

川島:アプリケーションが巨大であるがゆえに、「お台場プロジェクト」の推進前は開発・運用において大きな課題を抱えていたそうですね。どのような課題があったのですか?

青木 アプリが巨大すぎるため、cookpad_allに大規模な機能追加・変更ができないことが本質的な課題でした。あるコードを変更すると、意図しない箇所の機能が動かなくなってしまうんです。ライブラリのバージョンも迂闊には上げられません。依存が多すぎるため、更新がトリガーになって機能がおかしくなるケースがあるためです。

それから、Railsのバージョンアップが難しいことも大きな課題でした。バージョン2からバージョン3へのメジャーアップデートが一番きつくて、確か3~4人で1年くらいかかりました。3から4はもう少しマシでしたが、それでも1人が専任で半年はかかっていましたね。

川島:それは大変だ……。

外村 Railsが後方互換性のない機能更新を行ったことが原因で動かなくなるケースもありますし、アップデートに起因するエッジケースのバグを踏んでしまうケースもあります。それに、Railsのアップデートに追従していない古いgemが壊れたり、gemにモンキーパッチを当てて運用していたものが使えなくなったり。

これほど大きなRailsアプリケーションだと、バージョンアップに伴って数え切れないほどの問題が発生します。作業は一筋縄ではいきません。ようやく上げきった頃には、また次のバージョンが出ている、というくり返しでした。

6

川島:その状態では、永遠にバージョンアップの作業が続きそうですね。

青木 そうした課題を解決するために「お台場プロジェクト」はスタートしました。まずやったのは「改善したいことをリストアップする場所」としてGitHubにリポジトリを作ることでした。その活動が少しずつ実を結んでいき、マイクロサービス化に結びついていきます。

外村 つまり、もともとこのプロジェクトはマイクロサービス化を主眼にしたプロジェクトではなかったんです。あくまで巨大なモノリシックサービスを開発・運用する辛さを軽減するためのプロジェクトで、その手法のひとつとしてマイクロサービス化が有効だとわかった、という流れでした。

【マイクロサービス化戦略】まずはコードを減らすことから

川島:どのような部分から「お台場プロジェクト」を進めましたか?

青木 まず、辛さの根本原因を明確にするために、社内のさまざまなエンジニアに話を聞きました。全員が言っていたのが、コード量が多すぎるということです。

外村 そこで、まずは「いかにしてコード量を少なくするか」を主眼に置いて動きました。いらない機能や使われていない機能を探して、消していったんです。

青木 マイクロサービス化を行う際には、「機能をどう分割するか。何から着手すべきか」の議論が必ず起こりますし、社内調整にも苦労します。一方で、使われていない機能の廃止はメリットしかないので、反対意見も挙がりません。つまり、“プロジェクトの第一歩”に向いた作業なんです。

川島:とはいえ、使われていないコードを探すのも大変な作業ですよね。使われているコードを間違って消してもいけないですし。どのようにして、作業を進めていきましたか?

青木 本番環境で使われていないコードを、ツールで自動判定できれば作業が楽になります。そこで、「お台場プロジェクト」のメンバーとクックパッドのフルタイムRubyコミッターである笹田耕一さん、遠藤侑介さんで話し合い、どういう機能があるとコード削除に便利かを一緒に考えていきました。いくつかの機能を試し、最終的にできたのが「oneshot coverage{$annotation_1}」というRubyの新機能です。

この機能をオンにして一定期間測定することで、コードが実行された箇所のカバレッジを取ることができます。また、一度特定の箇所を通ったら二回目は計測されなくなるため、処理性能もほとんど劣化しません。

川島:Rubyを用いてサービス開発している企業にとって、このツールは汎用的に導入できそうですね。

【マイクロサービス化戦略】アプリ固有のバッドノウハウを減らす

川島:他にはどのようなことを実施しましたか?

青木 コード削除とほぼ同じタイミングで実施したのが、アプリケーション構造の整理です。cookpad_allはかつて、「動作モード」と呼ばれるトリッキーな実装がされていたんです。この「動作モード」をなくすことに取り組みました。

川島:「動作モード」とは何ですか?

青木 ひとつのRailsアプリケーションが、動作しているホスト名に応じてWebアプリケーションになったり、APIサーバーになったりするという変わった仕組みです。ひとつのアプリの中にWebアプリとAPIサーバーの設定が混在しているので、どの設定がどちらのものかを調べるのにも一苦労でした。

7

青木 ローカル環境でアプリを動かすのも大変でした。ホスト名を見てモードが切り替わるんですが、ローカル環境のホスト名は基本的にlocalhostじゃないですか。だから、localhostに別の名前を付けなければ、モードを切り替えられないという独自のノウハウがありまして。

川島:独自仕様を詳しく知らないと、開発がそもそもできないですね。

青木 はい。その状態はなかなか辛いので、構造をすっきりさせることにしました。

川島:どのようにして分割していったのですか?

青木 「動作モード」の削除にあたっては、アプリケーション本体のコードが機能単位でディレクトリ分割されていたことが幸いしました。命名を元に分離を行うことができたからです。APIサーバーは独立したアプリケーションにして、さらに古いAPIサーバーのコード削除も合わせて実施したことで、アプリケーション構造をすっきりさせることに成功しました。

8

外村 cookpad_allは10年以上も運用されていたため、歴史があるからこそ独自の運用ノウハウが蓄積されていたんです。その辛さを改善することが、このフェーズでは重要視されていました。

青木 「できるだけ普通のRailsアプリケーションにしよう」というキャッチフレーズで、プロジェクトを進めていましたね。

【マイクロサービス化戦略】まずは分離しやすい部分からお試しで

青木 次フェーズとして、cookpad_allからスマホアプリのA/Bテストなどに使われている機能を分離しました。この時点では、マイクロサービス化を行うかどうかは決めかねていたため、まずは試験的に、私が当時所属していたチームの持っていたシステムを分離する方針をとったんです。

川島:最初に分離する機能の候補はいろいろあったと思いますが、その機能を選んだ理由は何ですか?

青木 いくつか理由がありました。ひとつは、その機能はトータルのコード行数が1000行もない、ごく小さなAPIであったことです。依存ライブラリの種類も少なく、APIの数も2~3個くらいでした。しかも利用しているデータベースはRedis1個だけで、そのRedisは他サービスからは使われません。APIの独立性も高くcookpad_allとの通信もありませんでした。

川島:さまざな面で、分離するには都合のいいものだったわけですね。

9

青木 その機能の分離がうまくいったことで、サービス分割(=マイクロサービス化)が実現できそうなことや、分離に成功すればアプリケーションの大幅な機能変更も容易になることの道筋が立ちました。

青木 余談ですが、実はクックパッドは3~4年ほど前、マイクロサービス化に失敗しているんです。正確には、課金や広告配信などのシステムは綺麗に分離できたんですが、それ以外のシステムにおいて良くないサービス分割をしてしまいました。

分離したけれどうまくいかず元に戻したり、分離したことでかえって開発・運用の手間が増える事態が多発しました。「お台場プロジェクト」の初期フェーズでマイクロサービス化を主眼として打ち出さなかったのは、そのときの経験から「マイクロサービスは良くないのでは」という雰囲気が社内にあったためです。

川島:なぜ、当時はマイクロサービス化に失敗してしまったのだと思いますか?

青木 もっとも連携の多い部分を切ろうとしたからでしょうね。分割を行う前に「どの部分を分割すべきか」をまず分析すべきだったんですが「これからはマイクロサービスが主流だ。どんどん分割しよう」と進めてしまったのがよくなかった。

マイクロサービス化に着手する場合、最初はまず分離しやすい部分から手をつけるのが良いと思います。

【マイクロサービス化戦略】データベースが切れていればサービスも切りやすい

川島:他には何を行いましたか?

青木 分離したA/Bテストのシステムが「なぜ上手くいったのか」を考えたところ、「データベースが切れていることが大きい」という結論に落ち着きました。

そこで、「データベースの分け方に沿ってコードも切り出せば、サービス分割が上手くいくのではないか」という方針のもと、現在もマイクロサービス化を進めています。全文検索システムのSolrを中心に分離したレシピ検索や、専用Auroraをベースに分離した料理きろく機能などが、この方針で分離を行ったシステムにあたります。

10

青木 ただし、レシピ検索についてはまだ課題も残っています。SunspotというRubyライブラリがSolrによる全文検索をサポートしてくれているのですが、そのライブラリはモデルと密結合の仕組みであるため、完全には機能を分離しきれなかったんです。

川島:Railsを使っていて、かつSunspotを取り入れている企業は、マイクロサービス化を行う際には注意したいですね。

青木 そうなんです。そして、これからいよいよマイクロサービス化のもっとも難しいフェーズに入ってきます。cookpad_allのコアの部分は分離が大変だとわかっているので、まずは周辺部分を分離して、少しでも取り組みやすい状況にしてから挑みたいです。

【マイクロサービス化戦略】インフラ構成を標準化する

川島:マイクロサービス化にあたり、新しく取り入れた技術スタックはありますか?

青木 コンテナ技術ですね。クックパッドではコンテナオーケストレーションのために、AWSのECSベースで構築されたHako{$annotation_2}というツールを使っています。

外村 現在、全社的にcookpad_all以外の全てのアプリはHakoを利用する形でDocker環境に乗っています。アプリケーションに関連したシステムやメトリクス等の情報を閲覧できるhako-consoleやHakoを用いたオフラインジョブ実行の仕組みなど、Docker環境で便利に使えるツール類を社内の共通基盤チームが開発しているので、それらを利用可能にするためにDocker移行が行われてきたからです。そして、Hakoを利用することは、マイクロサービス化においてもたくさんの利点がありました。

11

川島:例えば、どのような?

青木 Hakoを用いてインフラ構成を標準化・自動化することで、各チームの開発者が環境構築も含めて自分たちで制御できる状態になります。これにより、インフラ基盤を見ているチームから各チームの開発者へと、インフラの決定権そのものが渡りやすくなります。

外村 それに、共通基盤に乗ることで、インフラ構築やデプロイ、監視などの方法が全社的に標準化されます。各チーム内で、開発から運用までの作業をより完結しやすくなるわけです。現在、cookpad_allも同様にHako化していき、Docker上で動かすための取り組みを続けています。

【マイクロサービス化戦略】サービスメッシュを入れて通信の課題をクリアせよ

青木 他に導入した技術スタックとしては、サービスメッシュがあります。クックパッドでは、サービスメッシュのdata-planeとしてEnvoyを使用しており、control-planeは自作しています。

これも、cookpad_allをマイクロサービス化するために導入したというより、全社的に共通で使えるサービスメッシュ基盤を別のチームが作っていたので、それにcookpad_allも乗っかったところ、マイクロサービスにも適していたという感じでした。

サービスメッシュの導入によって全通信がプロキシを経由するため、自動リトライやエラーの検知、通信状況の可視化などが容易になりました。マイクロサービスではサービス間の通信が大量に発生するので、サーキットブレイカーやリトライなどの仕組みを入れておくことが必要です。Envoyの導入によって、これらの前提条件がクリアできました。

外村 マイクロサービス間の通信において必要な機能が一通り揃っているのは、Envoyの優れているところですね。

青木 また、その他にもAPIオーケストレーション層としてOrchaというシステムを開発しました。このツールがリバースプロキシとAPIサーバーの間に入りこんでくれて、既存APIのコードをいじらずにAPIのレスポンスを変えることが可能になりました。これによって、マイクロサービス間の通信の柔軟性が大きくなっています。

12

マイクロサービス化は、システム改善であり組織改善でもある

川島:今回のお話から、クックパッドさんは非常にオーソドックスな手法で、無理のない範囲からマイクロサービス化をスタートしている印象を受けました。かつて、「3~4年前に失敗した」とおっしゃっていましたが、その経験を踏まえて、適切な手法にたどり着いているのだと思います。

青木 いきなり難しいところに取り組んでも、うまくいかないですからね。

川島:この事例では、まず周辺部分から切り離し、モノリスの本当の姿が見えるようにしたのが素晴らしいと思いました。多くの企業は、この事前準備ができていないです。複数のシステムが混在している状態ですと、本当の意味でのビジネス規模が見えにくいですし、適切な分割の形も見えにくい。

マイクロサービス化しようと考える企業って、初っ端から本体のシステムを、いわばラスボスを倒しにいきがちなんです。ラスボスに負けてしまって、頓挫するケースが多い。

外村 スライムを倒してレベルを上げるところから、知見を積まなければいけないですね(笑)。

13

川島:その通りです(笑)。では最後に「大規模なサービスを持つ企業が、マイクロサービス化に取り組む意義」について語っていただければ。

青木 大きく分けて3つの意義があると思います。1つ目は高速で大胆な開発スタイルを取り戻すこと。冒頭で述べたように、大きな機能の追加・変更ができない状態では、サービス開発にも制約がかかります。ビジネスで本来達成すべき目標が、実現しにくくなるわけです。その状況を改善することで、ドラスティックな機能の追加・変更が可能になります。

2つ目は組織をスケールアウト可能にすること。巨大なモノリシックサービスの場合、コード修正のコンフリクトが山ほど発生するため、組織のサイズそのものが制限されてしまいます。マイクロサービス化によって、分散開発を可能にすることには大きな意義があります。

これは私見ですが、モノリシックなアプリケーションで問題なく開発できるのは、エンジニアの人数100人くらいが上限だと思います。それ以上に組織が大きくなるなら、マイクロサービスの方が確実にスケールしやすくなります。マイクロサービス化は、システム改善であると同時に組織改善でもあるわけです。

3つ目はデータと機能の再利用です。クックパッドには約300万名の登録ユーザーさまがおり、そのデータは大きな資産です。しかし、コアの部分がモノリシックな状態になっているとデータの再活用は容易ではありません。新規サービスを立ち上げて、データにアクセスすることは難しいわけです。結局、本体にコードを追加する以外には方法がなくなってしまいます。

サービスを役割ごとに切り分けて再利用可能にすることで、データと機能の再利用性が高まります。サービスの共通基盤を構築でき、会社全体として強いシステムを作れるわけです。それが、マイクロサービス化の利点だと考えています。

取材・執筆:中薗昴/写真:岩崎力也

*1:oneshot coverageの詳細については、遠藤さんが「クックパッド開発者ブログ」にて「Ruby 2.6 新機能:本番環境での利用を目指したコードカバレッジ計測機能」という記事で解説している。

*2:ECSのデプロイには、ELBとコンテナの紐付け設定や環境変数の注入などさまざまな作業が必要となる。そうした諸作業を事前に設定することで、デプロイを効率化できるツールがHakoだ。デプロイ時のELB作成やRoute53によるドメイン設定など、通常であればインフラエンジニアが設定するような作業まで自動化できる。

若手ハイキャリアのスカウト転職