巨大Railsアプリケーションの改善

小室直氏(以下、小室):始めさせていただきます。

まずこれ、たいした意味もなく出してるんですが、この会場に来たときにこれを見て「あ~すごいちゃんとイベントバナー作ってる。クックパッド、クラッシー。あ、クックパッドもなんか絡んでるんだな~」って思ったんですけど。よく考えたら僕がしゃべるからクックパッドって社名も出てるんですね(笑)。

(会場笑)

単にそれを言いたかっただけです(笑)。

さっそく中身のほうをやってきたいと思います。クックパッドから来ました、小室(@hogelog)です。

今日は『クックパッドの巨大Railsアプリケーションの改善』というタイトルで話をさせていただきます。

ちょうど前にしゃべった方々と違う内容になっているのでよかったかなという気もしています。Railsアプリケーションの改善と言っていますが、Railsの中身についてはろくに触れずに進めていきます。

自己紹介はたいしたこと書いてないんですけど、クックパッドに2013年に入っていろいろやって、今は技術部開発基盤グループというところでcookpad.comの裏側にあるcookpad_allという巨大Railsアプリケーションの面倒を見るというのが主な役割です。

みなさん大きなRailsアプリケーション開発してますか? 今日のテーマはRailsで開発をしている人々、技術的負債に興味がある人たちが参加しているということなので、おそらく大きなRailsに気持ちがある人たちだと思います。

大きなRailsアプリケーションがなぜすごく辛いかとか、そういう話は、みなさん辛いことはわかっていると思うのでサクッと省きます。

(会場笑)

クックパッドではその巨大Railsアプリケーションをどう改善していったか。今までやってきたことをざっくり紹介していきたいなと思います。

cookpad_allの歴史

まずは、巨大アプリケーションがいくつも詰まっているcookpad_allというリポジトリですね。それについての今昔を紹介したいと思います。

前提知識なんですが、cookpad_allとはcookpad.comの裏側を支える巨大なRailsアプリケーションが複数ぶち込まれていて、その共有ロジックをまとめたsharedというgemが含まれているリポジトリです。

cookpad_all内の各アプリケーションはほとんどモデルとDBの共有によりロジックの共有をしていて、まあDB共有いいですよね。

(会場笑)

ね、早いし。あんまり落ちたりしないし。あるとき限界が来るまでは。

ひと昔前のクックパッドの開発風景は、だいたい社内のエンジニアみんながcookpad_allのアプリケーションに対してやたらめったらコミットしていて、みんなそこで開発していっている状況でした。

開発基盤やインフラのリソースもcookpad_allに集中しています。とにかくみんなで巨大なモノリシックRailsアプリをどんどん整備していって。これはRubyやRailsのコミッターをやっている松田明さんが2015年にオレゴンで開催されたRuby on Alesというイベントで発表してきた内容です。

世界最大のRailsモノリスということで、クックパッドという会社が超巨大なRailsアプリケーションをどうやってメンテナンスしているかみたいなタイトルで発表しました。これがクックパッドのモノリシックRailsアプリ最盛期みたいな時期でした。

2016年頃の開発風景

ちょっと時代が進みまして、クックパッドにもマイクロサービスの波が来ました。

DockerやECSなど、いろんな便利な道具が揃って増えてくる中で、マイクロサービスが広がっていく。ただしcookpad_allはもう巨大なモノリシックRailsアプリとして依然残り続けていて、どんどんレガシーで辛いものになっていきました。

(参考:クックパッドにおける最近のMicroservices事例

ここで紹介している記事はcookpad_allでマイクロサービスがどうやって広がっていったかという記事です。

実はそのときの内情を話すと、そのころcookpad_allっていう最後の砦みたいなところだけはほぼ手付かずで巨大なモノリシックRailsアプリとして残っていました。

そして2017年にお台場プロジェクトという、『ふつうのHaskellプログラミング』とか10年SQL本とか『ふつうのLinuxプログラミング』とかそういう本をいろいろ書いていたりする青木峰郎さんという、この人もRubyコミッターですね、が立ち上げたcookpad_all改善プロジェクト。お台場プロジェクトというのが発足しました。

cookpad_all改善と言いつつ裏の目標としては開発基盤チームの解散とかそういうのがあるんですが……時間はどのくらいかな? 時間が厳しそうなのでこのへんの裏の目標とかはサラっと飛ばしていきます。

お台場プロジェクトというcookpad_allの巨大Railsアプリをいくつも抱えているリポジトリの改善プロジェクトが2017年7月ごろに発足して今までずっとやってきました。

お台場プロジェクトが誕生

というわけで、お台場プロジェクトを実際にどういう感じに実践していったかというを紹介したいなと思います。

実際にやったこと。

こんな感じで、開発メトリクスを定点観測して、いらないシステムを削除したり、システムをcookpad_allから分割していきました。あとコードを削除していろいろやってきました。

詳細に触れていきます。まず開発メトリクスの定点観測。これに関してはtechlifeという技術ブログですでに紹介しているところではあるんですけれども。

コード削除などをいい感じにしていくには、まず今がどれくらいダメで、日々どれくらいいい感じにできているのかを可視化しなければいけないと思いました。ですので、まずダッシュボードを作って可視化できるようにしました。

実際にcookpad_allの改善、お台場プロジェクトでどのようにやってきたのか、最近までの数字を出しています。こういったことをやってきました。

昔はCIで10分くらいかかっていたのが、最近では5分くらいまで短縮できたとか。一番短いやつだと7分くらいかかっていたのが3分くらいになったとか。そのくらいになっりました。

これも可視化していたおかげで、日々こうして「このへんをがっつりやっていったことで、これ時間短縮できたんだな」みたいな。日々自分たちの心を慰めながら改善を進めることができました。

これはアプリのロード時間ですね。

rails sをしてからちゃんと動き始めるくらいまで、という感じで考えてください。10秒くらいかかっていたのが4秒くらいまで短縮できています。

あとはCode Statistics、rails statsみたいなやつですね。

cookpad_allに含まれている一番巨大なRailsアプリであるcookpadは、コードラインで40万行くらいあったのが30万以下くらいに減っています。

こう示すと、一番ガクッと出てるところやこういうところに注目したくなる気持ちかもしれませんが、お台場プロジェクトに参画してやっていた身としては、日々ちょっとずつ下がっていってるところに注目してほしいです(笑)。

日常的に細かく削除していって、ちょっとずつでもちゃんとやっているという気持ちがこの図に出ていて、どちらかと言うとなだらかな曲線のほうが見ていて心が温かくなるという、そんな話ですね。

(会場笑)

日々モニタリングを行う

rails statsの数字を出してみたんですけど小さくてあんまり見えないので、トータルのところだけ触れます。

cookpad_all内にはRailsアプリが今で4つ5つくらい入っているんですけれども、その中で一番巨大なcookpadというRailsアプリケーションの数字です。

ほかにも同等サイズのものが3つくらいあったりしますが、そのcookpadのやつで2017年7月1日時点でトータルで54万行ありました。それが今朝測ってきた数字だと34万行ということで、20万行くらい減っているというですね。いやー、がんばりました。

書いたコード量で給料が決まるような会社だと、きっと僕はマイナス給料で会社にお金を数千万円払うとかそんな感じになってたのかなと。そうじゃなくてよかったです(笑)。しょうもないことを言いました。

(会場笑)

これは依存gem数みたいなのを出していて、Gemfile.lockを見るだけで出せるので2011年頃まで遡って出してみました。まあおもしろ半分なんですが。

2017年7月ごろ、このころにちょっとだけそういうことを始めたので上限がたまに下がるところがあるんですが、そういう努力をしない限り、基本的にgem数はどんどん膨れ上がっていきます。Rails自体が依存するgem数なんかもすごく増えていってますし。

とあるgemを導入したいときに「あ~こっちのgemと依存関係だ」とか、みなさん感覚としてわかると思います。それは時間が経つと増えていく。昔はRailsももうちょっと素朴だったみたいでそういうのはあると思いますが、それが図に出ていて、減らすと減るという。減らすと減る? 何言ってんだろうな。

(会場笑)

これはGemCollector、依存gemの最新度みたいなのを取っていて、これは社内にあるRubyアプリケーションの数値から相対的に出している数値です。

cookpad_allのRailsアプリケーションになにも触らなかったとしても、cookpad_allがRailsをアップグレードしない間にほかがRailsアップグレードしていくとどんどん数値が下がっていくという。そんなものになっています。

2017年7月ごろ、お台場プロジェクト発足と前後していますが、気持ちをもってエイっとbundleアップデートしたのが左側にガツンと上がっているところで。それまですごく少なかったのがちょっとずつ上がりました。

でもがんばらないとどんどん下がっていって、たまにがんばって上げて、みたいなことをやっています。毎月1日に上げるとか、そういうことをやるべきかもしれません。

2017年ごろのcookpad_allの構成

システム削除の話に入ります。cookpad_all内にある不要なシステムを削除していきます。これはとくに工夫はありません。がんばって削除です。

(会場笑)

2017年当初のcookpad_allの構成はこういう感じです。cookpadというアプリケーションは恐ろしいことにデプロイ先が4ヶ所くらいあったと。

嫌ですね。「これいらねぇよな」みたいな。ちょっといらなそうというのはわかっていたので、がんばって削除しました。

がんばって削除したら1個減りました(笑)。

(会場笑)

次にbackground worker、これは新しい仕組みがすでにあったのに残っていた非同期処理システムみたいなもので、それもがんばって削除しました。

もう1つ減った。まだ1個残ってるのが気になる。気になるけど、これはまた次のところで。

システムを正しく分割する

次にシステム分割をしていきます。システムを正しく分割する。DB共有はやっちゃいけないので、アプリ間はAPIとかそういったもので通信する。

pantryを分割します。

これはcookpad_all内で分割します。さっきの図は明らかにここに誘導していたんですが、こうなってたら普通こうしたほうがいいですよね、という感じに分割しました。1アプリ1デプロイ先という自然な状態になりました。

さまざまなマイクロサービスアプリに切り出していきました。これはもういろんな、いい感じに切り出していったというそれだけです。

そして、いろんなアプリを切り出していきました。特にどういうサイズで切り出すという明確な境界があるわけでもなく、機能群とかいい感じの……本当にいい感じのサイズで切り出していったとしか言いようがないですね(笑)。

(会場笑)

ECS Clusterみたいな、そういうのはすでにあったのでそっちに載っけていきました。載せる先はマイクロサービス化の流れで整備されていたのでそこは困ることはありませんでした。

その中にmobileというのがありました。mobileといったら普通にスマホかな、みたいな感じですが、これはクックパッドでは携帯電話版向けサイト『モバれぴ』というものがございまして。

うちではまだアクティブに稼働していますので、それをやるmobileというアプリケーションなんですが、誰も開発していないんですね。メンテナンスくらいはしているんですが、アクティブな開発はしていない。作り直すのにブロッカーがなにもないからモダンな技術で作り直そうという意思決定をして、モダンな技術で作り直していきました。

というわけでcookpad_all内のアプリは4つになりました。

不要なコードやgemを探す

次に、不要なコードをがんばって削除していってます。

いろんなやり方を書いてます。具体的なやり方をなにも書いてないのがいくつかありますが、そうですね、がんばって削除しました。

(会場笑)

あとはCode Cleaning Contestみたいなものを社内で開催して、期間1ヶ月で「みなさんコード削除してください、豪華賞品もらえます」みたいなこともやりました。別にそのために時間を取るわけではなく、みんながスキマ時間に高い意識でやるみたいな。そういうのも開催したこともあります。まあ、いろいろ消しました(笑)。

棚卸しIssueの自動作成みたいなこともやりました。半自動化、手元でスクリプトを回したらガーっといろんなものを見て、「このコードもう動いてないんじゃないですか?」みたいなIssueが立てられて。

Gitのコミットログからそのコードを触った人に「お前触っただろ、これ?」みたいな感じで。「お前がこれをどうにかするんだ」みたいな感じで自動でアサインされていく感じで動かしていっています。

(会場笑)

これもちょっと回しすぎたからか反応が鈍くなってきたので、削除可否だけ聞いてお台場プロジェクトメンバーが削除するみたいなのが最近増えてきました。

あとはGemfileをじっと眺めて不要gemを探す。

これはじっと眺めて「不要そうだな」みたいな。あとはがんばって削除。jquery-rjsとか、最近ようやく消せました。あ、消せてないか。Gemfileから消さなきゃいけねぇや。aws-sdk-v1とか、みなさんの中にも思い当たるフシがある人がいるかもしれません。

Rubyの lazy loadingを利用し不要gemを検出したり、このあたりのことはtechlifeにもありますね(参考:Ruby の lazy loading の仕組みを利用して未使用の gem を探す)。

あと、iSeq実行処理を記録し不要コードを探したりとかもしています。(参考:Feature #15022 Oneshot coverage by @mame

Ruby2.6では素のRubyでこういうことが可能になるかもしれません。@mameさんがそういう感じの提案をしているので興味があったら見てみてください。

その他にもまあいろいろがんばってやっています。

bootsnap入れたり、Rubyアップグレードやったり、Rails含め色々なgemのアップグレードをやったり、Machinist 1からFactoryBotに移行してみたり、EC2インスタンスを入れ替えたり、いろいろやりました。

うまくいった要因

けっこうがんばってやっていて、いろいろうまくいったなと思います。うまく進んでいる要因、名前が付いたというのが1ついいかなと。名前があると自分も他人もこういうことをやっているということを認識しやすいです。

あとは単純に優秀なエンジニアをアサインして人々がやっていってる。みなさんの会社でも、Rubyコミッターがいるかなと思うんですけども。

(会場笑)

Rubyコミッターがちょうど僕の席のすぐそばに2人くらいいて、iSeq実行ログの記録とかLazy Loadingの活用とかがっつりそのへんの人たちにサポートしてもらったりしています。

そこから逆に、@mameさんの提案はそこから知見を得て提案してきてくれたのかなみたいなのもあるので、お互い与えているものもあるかなと思いたいところです。そんな感じです。

まあ、まとめると、やっていきました! っていう感じですね。

はい、以上です!

(会場拍手)