Dockerfileのビルドステージから重複する`libvips`インストールを除去
Railsが生成するDockerfileで、libvipsがビルドステージとランタイムステージの両方にインストールされていた問題を修正しました。FROM base AS buildという継承関係があるにもかかわらず重複インストールが発生していた冗長なエントリを削除しています。
背景
Railsアプリケーション新規作成時に生成されるDockerfileで、libvipsが二重にインストールされるという問題が#57237として報告されていました。
生成されるDockerfileはbaseステージでランタイム用パッケージをインストールし、その後FROM base AS buildでビルドステージがbaseを継承する構造になっています。baseステージでlibvipsをインストール済みであるにもかかわらず、ビルドステージでも再度libvipsをインストールする命令が追加されていました。Dockerのマルチステージビルドでは、親ステージのイメージレイヤーをそのまま引き継ぐため、既にインストール済みのパッケージを再度インストールする命令は完全に冗長です。
技術的な変更
railties/lib/rails/generators/app_base.rb の dockerfile_build_packages メソッドから、libvipsをビルドパッケージに追加していた3行が削除されました。
変更前:
def dockerfile_build_packages
# ...
packages << "python-is-python3"
# ActiveStorage preview support
packages << "libvips" unless skip_active_storage?
packages.compact.sort
end
変更後:
def dockerfile_build_packages
# ...
packages << "python-is-python3"
packages.compact.sort
end
この変更により、Active Storageが有効な場合に生成されるDockerfileのビルドステージからlibvipsのインストール命令が除去されます。libvipsは引き続きbaseステージでランタイム用パッケージとしてインストールされるため、Active Storageの画像処理機能は変わらず利用できます。
設計判断
ビルドステージへの追加を削除するという最小限の修正が採用されました。
libvipsは画像処理のランタイム依存であり、アプリケーション実行時に必要なパッケージです。ビルド時にのみ必要なパッケージ(build-essentialやlibpq-devなど)とは性質が異なります。dockerfile_build_packagesメソッドはビルド時の依存関係を管理する責務を持つため、ランタイム依存であるlibvipsはここに含めるべきではなく、baseステージで管理されるべきという判断が明確に示されています。
まとめ
本PRは3行の削除のみという最小限の変更で、Dockerのマルチステージビルドにおけるステージ継承の原則に沿ったDockerfileを生成できるようにした修正です。ビルドパッケージとランタイムパッケージの責務分離を正しく維持することで、生成されるDockerfileの品質と一貫性が高まります。