Rails: Dockerマルチステージビルドでgemをキャッシュする(翻訳)
Railsのビルドでは、Dockerfileのbundle install
にかかる時間が大半を占めます。
以下は、あるRailsアプリケーションの標準的なDockerfileを簡略化したものです。
FROM ruby:3.1.2
RUN gem install bundler:2.3.7
WORKDIR /app
COPY Gemfile Gemfile.lock ./
RUN bundle install
COPY . .
このDockerfileでは、ソースコードをコピーする前にビルドキャッシュを使ってGemfileとGemfile.lockを分離しています。
gemがインストールされるのは最初のビルドだけなので、この方法は非常に有効です。
非Docker環境と異なるのは、Gemfileやgemを変更したりgemを追加・削除したりすると、すべてのgemが最初から再インストールされることです。
キャッシュイメージを使う
RUN gem install bundler:2.3.7
WORKDIR /app
# Copy the gems from a dedicated cache image
COPY --from saeloun:rails7:gem-cache /usr/local/bundle /usr/local/bundle
COPY Gemfile Gemfile.lock ./
RUN bundle install
COPY . .
上は、saeloun:rails7:gem-cache
イメージの/usr/local/bundle
ディレクトリを自分たちのビルドにコピーします。
COPY --from
を指定すると、Dockerはファイルを既存のレジストリから現在のビルドにコピーできます。これにより、Bundlerの実行をやり直さなくなり、前回使ったすべてのgemがキャッシュされるようになります。
唯一の問題は、イメージがそのレジストリ内に存在していないと動作しないことです。この方法は、初期のビルドイメージが存在しない場合は失敗します。
マルチステージでgemをキャッシュする
マルチステージビルドを使うと、Dockerfileをさらに改善してレジストリをdocker build
の引数として渡せるようになります。
ARG BASE_IMAGE=ruby:3.1.2
ARG CACHE_IMAGE=${BASE_IMAGE}
# gemキャッシュ用のビルドステージ
FROM ${CACHE_IMAGE} AS gem-cache
RUN mkdir -p /usr/local/bundle
# Bundlerインストール済みのイメージ
FROM $BASE_IMAGE AS base
RUN gem install bundler:2.3.7
WORKDIR /usr/src/app
# gemキャッシュ用のビルドステージからgemをコピーする
FROM base AS gems
COPY --from=gem-cache /usr/local/bundle /usr/local/bundle
COPY Gemfile Gemfile.lock ./
RUN bundle install
# ソースコードを配置する
FROM base AS deploy
COPY --from=gems /usr/local/bundle /usr/local/bundle
COPY . .
このビルドステージには以下の4つのステップがあります。
gem-cache
: gem-cache用のディレクトリを作成するbase
: Bundlerをインストールするgem
: gemを既存のイメージからコピーしてBundlerを実行するdeploy
: ソースコードを追加する
CACHE_IMAGE
が設定されていないと/usr/local/bundle
の内容が空になり、gemをコピーできなくなります。CACHE_IMAGE
にgem入りのイメージが設定されていれば、gemはコピーされます。
これで、以下を実行してイメージをビルドできるようになります。
docker build .
タグが設定された後は、CACHE_IMAGE
を以下のように設定できます。
docker build . --build-arg CACHE_IMAGE=saeloun:rails7:gem-cache app
概要
元サイトの許諾を得て翻訳・公開いたします。