概要
マイクロサービスアーキテクチャで開発をしており、各サービスをDockerでコンテナにしています。ローカル環境の各コンテナを最新化する際に、docker-compose up --build -dがすべて終わるまでに20~30分毎回かかってしまっていたので、頑張って高速化してみました。体感、3~4分ぐらいまで高速化できたと思います。Windows環境での開発を前提としています。また、1つ1つの効果を細かく測定はしていないので、それぞれの対応がどれだけ効果があったかは体感になります。(測定したら記事を更新したいと思います。)
やってみた事
- BuildKitのインラインキャッシュの利用
- BuildKitのマウントキャッシュの利用
- 並列ビルドの利用
- Buildステージに軽量なイメージを利用する
- DBのMigration時にビルド済みのリソースをちゃんと使う
BuildKitのインラインキャッシュの利用
BuildKitのインラインキャッシュは、Dockerイメージのレイヤーにビルドキャッシュを埋め込む機能です。通常のDockerビルドキャッシュと異なり、イメージ自体にキャッシュ情報が含まれるため、異なるマシンやCI環境でもキャッシュを再利用できます。ローカル環境でも、同じDockerfileから何度もビルドを行う場合に効果がありそうなので、試してみました。
以下のようにBUILDKIT_INLINE_CACHE: 1を記述しておきます。
# docker-compose.yml
services:
backend-service:
build:
context: ./backend
dockerfile: Dockerfile
args:
BUILDKIT_INLINE_CACHE: 1
BuildKitのマウントキャッシュの利用
BuildKitのマウントキャッシュを使用することで、NuGetパッケージのリストア処理を効率化しました。このキャッシュはホストマシン上に保持され、ビルド時のみ一時的にマウントされます。Dockerfileでは以下のように設定します。
RUN --mount=type=cache,target=/root/.nuget/packages dotnet restore "Hoge.csproj"
RUN --mount=type=cache,target=/root/.nuget/packages dotnet build "Hoge.csproj" -c Release
ローカルのNuGetパッケージのキャッシュをコピーする事も考えましたが、各端末の開発環境の影響が大きかったため、中止しました。環境がかなり統一されている状況なら使えるかもしれません。
並列ビルドの利用
PCのスペックが高くない場合は効果が薄かったり、負荷がかかりすぎるかもしれませんが、幸いな事にそれなりのスペックだったこともあり、かなり効果があったと思います。
docker compose build --parallel
Buildステージに軽量なイメージを利用する
Buildステージのみ、alpine版にして少しでも処理が軽くならないか期待してみました。高速化の観点ではそこまで効果は無かった印象です。
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
↓
FROM mcr.microsoft.com/dotnet/sdk:8.0-alpine AS build
DBのMigration時にビルド済みのリソースをちゃんと使う
docker compose build --parallelの後にdocker compose up -dでコンテナを起動し、DBのMigrationを実行してローカルのDB環境を構築していました。Entity Framework Core CLIを利用して、Migrationを実行していましたが、実行時にBuildから実行されており、なぜかBuildに5分以上の時間がかかっていました。イメージを作成する際にビルド済みなので、それを使う事によりこの5分以上の時間を短縮する事ができました。
ENTRYPOINT [ "dotnet", "ef", "database", "update", "--no-build", "--configuration", "Release" ]
まとめ
いろいろな最適化手法があり、組み合わせる事でメリットが得られます。
今回は、特にBuildKitのマウントキャッシュと並列ビルドの組み合わせが効果的でした
実際の環境に合わせて各設定を調整し、最適なパフォーマンスを探してみてください。