TurborepoでモノレポのCIビルドが劇的に速くなった話!キャッシュが効かないときの確認ポイント
🙌 結論から
Turborepo を導入すると、変更のないパッケージのビルドをキャッシュしてスキップできるようになります。
モノレポで複数パッケージを管理している場合、毎回すべてをビルドし直すのはかなり非効率です。
Turborepo に切り替えてからは変更のあったパッケージだけ再ビルドされるようになり、CI の実行時間が体感で半分以下になりました(^o^)/
特に規模が大きくなってきたモノレポで効果ありそう!✨
💡 モノレポが重くなっていた
仕事で使っているプロジェクトは、フロントエンド・API・共有ライブラリをひとつのリポジトリで管理するモノレポ構成でした。
パッケージが増えるにつれて、CI での npm run build がどんどん遅くなっていきました(´・ω・`)
しかも、UI コンポーネントを 1 行変えただけでも、API サーバーや共有 utils まで全部ビルドし直されてしまう状態…
これにちょっと悩んでてたのですが、調べてみて Turborepo を試すことにしました。
👀 Turborepo の仕組み
Turborepo はモノレポのビルドオーケストレーターで、タスクの依存関係と出力をキャッシュしてくれます。
npm install turbo
turbo.json に各タスクの設定を書くだけで動きます。
{
"$schema": "https://turbo.build/schema.json",
"tasks": {
"build": {
"dependsOn": ["^build"],
"outputs": ["dist/**"]
},
"test": {
"dependsOn": ["build"]
}
}
}
"dependsOn": ["^build"] の ^ は「このパッケージが依存するパッケージのビルドが終わってから実行する」という意味です!
ビルドが成功すると、ソースファイルのハッシュと出力がキャッシュされます。
次回以降、ソースが変わっていなければそのキャッシュが使われてビルドステップ自体がスキップされます(^^)
✨ 導入して変わったこと
実際に CI に組み込んでみたところ、変更のないパッケージはキャッシュヒットして一瞬で完了するようになりました!
以前は全パッケージのビルドで 8〜10 分かかっていた CI が、変更点が少ないプッシュなら 2〜3 分で終わるようになっています。
けっこう助かる…🥰
ローカルでも効果があって、turbo build を使うと 2 回目以降は「キャッシュ済み」と表示されてほぼ即座に完了します。
ひとつ注意点があって、キャッシュは outputs に指定したディレクトリが対象になります。
outputs の指定が漏れていると、ビルド成果物がキャッシュに含まれないため、他のパッケージからの依存が正しく解決されなくなります(・_・;)
最初に設定するときは、各パッケージの dist や build を確認しながら outputs を丁寧に設定する方が安全です。
🌐 Remote Cachingでチームのキャッシュも共有する
ローカルとCIそれぞれでキャッシュが効くようになって満足していたんですが、あるとき「自分のPCでビルド済みなのに、同僚のCIではまたフルビルドが走っている」ことに気づきました。
考えてみれば当然で、キャッシュはデフォルトだとそのマシンのローカルにしか保存されません。
これを解決するのが Remote Caching です。Turborepo を Vercel のリモートキャッシュに接続すると、自分のローカルで一度ビルドしたキャッシュを、チームメンバーやCIからも参照できるようになります。
npx turbo login
npx turbo link
この2コマンドを実行するだけで、以降のビルドは自動的にリモートキャッシュを見にいくようになります。
私が試したケースだと、**「同僚がさっきビルドしたばかりの変更を、自分が pull してビルドしたらキャッシュヒットして一瞬で終わった」**という体験がかなり衝撃的でした(・∀・)
CI側も同様で、GitHub Actionsのワークフローに TURBO_TOKEN と TURBO_TEAM の環境変数を渡すだけでリモートキャッシュに接続できます。
- name: Build
run: npx turbo build
env:
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
TURBO_TEAM: ${{ vars.TURBO_TEAM }}
Vercelのリモートキャッシュは無料で使えるので、Vercelにデプロイしていなくても「Turborepoのキャッシュ置き場」としてだけ使う価値は十分あると感じています。
チーム開発でモノレポを使っているなら、ローカルキャッシュだけで満足せず、ぜひRemote Cachingまで設定してみてください!
🤔 キャッシュが使われない・おかしいときの確認ポイント
Turborepo を入れたのに、キャッシュが使われない、または古い結果が出てくるときは、たいてい turbo.json の書き方が原因です(・_・;)
確認したいのはこの3つです。
outputsが書いてあるか: ビルド結果のフォルダ(dist/**など)を書いていないと、キャッシュから結果が戻ってこない- 環境変数を
envに書いているか: API の URL などをビルド時に使っているのに書いていないと、値を変えてもキャッシュが使われて古いまま .envファイルをinputsに入れているか: ファイルの変更をキャッシュの判定に含めたいとき
{
"tasks": {
"build": {
"dependsOn": ["^build"],
"outputs": ["dist/**", ".next/**", "!.next/cache/**"],
"env": ["API_BASE_URL"]
}
}
}
2つ目が一番こわくて、本番用の URL でビルドしたつもりが、開発用の値が入ったままになることがあります👀
おかしいと思ったら、turbo run build --summarize で、キャッシュの判定に何が使われたかを確認できます✨
パパ系Webエンジニア〜愛用ガジェット一覧 🛍️
この記事を書いている作業環境のガジェットを楽天ROOMにまとめています ✨ 在宅エンジニアのデスク作りの参考にどうぞ 🙌
👍 まとめ
Turborepo は、モノレポのビルドキャッシュをほぼ設定ファイル一枚で実現してくれます。
パッケージ数が増えてきた、CI が遅くなってきた、というタイミングで導入するといいかも!
ローカルのビルドも速くなるので、開発体験もかなり改善されますね。
まだ素の npm workspaces だけで動かしているモノレポがあれば、ぜひ一度 Turborepo を試してみてください(^_^)
※ Turborepoのバージョンによって仕様が変わることがあります。最新情報は Turborepo公式ドキュメント をご確認ください👀