こんにちは!開発現場で日々コードとインフラに向き合っていると、「Dockerイメージの容量が肥大化してデプロイに時間がかかる」「`composer install` の待ち時間が長すぎて、CI/CDのパイプラインがボトルネックになっている」といったストレスに直面しませんか?
今回は、PHPのパッケージ管理ツールである Composer と Dockerのマルチステージビルド を組み合わせ、「爆速のビルド」と「極小のイメージ容量」を同時に手に入れるベストプラクティスを解説します。
これをマスターすれば、毎日のデプロイ待ち時間が劇的に短縮され、セキュアで軽量な本番環境を構築できるようになりますよ。さあ、一緒にその仕組みを紐解いていきましょう!
—
そもそも、なぜComposerのDocker最適化が必要なのか?
初心者の方が最初にやりがちなのが、ひとつの `Dockerfile` の中で `apt` でPHPを入れて、Composerを入れて、`composer install` を実行し、そのままアプリを動かすという方法です。
これには、以下の2つの大きな課題があります。
1. イメージ容量の肥大化: Composer自体や、その実行に必要な開発用パッケージ(Gitやunzipなど)、さらには `composer.json` の `require-dev`(PHPUnitやPHPStanなど)までが本番イメージに含まれてしまい、セキュリティリスクとストレージを無駄に圧迫します。
2. ビルドキャッシュの無効化: ソースコードを1文字書き換えただけで、Dockerが「変更があった」と判断し、重たい `composer install` を毎回ゼロからやり直してしまいます。
これらを綺麗に解決するのが、「マルチステージビルド」と「Composerキャッシュのレイヤー分離」です。
—
1. 究極のDockerfile設計(全体像)
まずは、理論をコードで証明しましょう。以下に示すのが、現場の第一線で使われている最適化済みの `Dockerfile` です。
このファイルをプロジェクトのルートディレクトリに配置してください。
=================================ソステージ 1: ビルド環境=================================
重たいビルドツールやComposer本体を含むステージです。本番には持ち込みません。
FROM composer:2.6 AS builder
コンテナ内の作業ディレクトリを指定
WORKDIR /app
キャッシュ効率を最大化するため、まずは依存関係定義ファイルだけをコピーします
(ソースコードの変更でキャッシュが破棄されるのを防ぐため)
COPY composer.json composer.lock ./
【重要】開発用パッケージを除外し、スクリプトの自動実行を抑制してクリーンにインストール
–no-dev: 本番に不要なテストツールなどを入れない
–no-scripts: インストール時点でのスクリプト暴走を防ぐ
–no-autoloader: オートローダーの生成を後回しにする(最適化のため)
RUN composer install \
–no-dev \
–no-scripts \
–no-autoloader \
–prefer-dist \
–no-interaction
ここで初めてアプリケーションのソースコード全体をコピーします
COPY . .
最後に、オートローダーを最適化しつつ再構築します
RUN composer dump-autoload –optimize –no-dev
=================================ステージ 2: 本番環境=================================
実行に必要な最小限のPHP-FPM(またはApache)イメージを採用します
FROM php:8.2-fpm-alpine AS production
本番コンテナ側の作業ディレクトリ
WORKDIR /var/www/html
ステージ1(builder)で生成された、ベンダーフォルダとソースコードだけをピンポイントでコピー
これにより、ComposerやGitなどの重たい開発ツールは一切本番イメージに入りません!
COPY –from=builder /app /var/www/html
セキュリティ確保のため、必要に応じてパーミッションを調整
RUN chown -R www-data:www-data /var/www/html
本番コンテナの起動コマンド
CMD [“php-fpm”]
—
2. コードに隠された「プロの技」を徹底解説
なぜこの書き方で爆速かつ安全になるのか、3つの重要なポイントを噛み砕いて説明します。
① マルチステージビルドによる「いいとこ取り」
Dockerの `FROM … AS …` を複数回使うことで、「ビルドする場所(Builder)」と「実行する場所(Production)」を完全に分離しています。
ピザを焼くときに「窯(重たい設備)」は店に置いておき、お客さんには「美味しいピザ(成果物)だけ」を届けるイメージです。これにより、本番イメージの容量を数百MB単位で削減できます。
② ファイルコピーの順序による「ビルドキャッシュの魔術」
Dockerは、上の行から順にキャッシュを効かせます。もし `COPY . .`(全ファイルのコピー)を `composer install` の前に書いていると、「コントローラーの変数名を1つ変えただけ」で、重いcomposer installが最初からやり直しになります。
今回のDockerfileでは、次のように順序を工夫しています。
1. `composer.json` と `composer.lock` だけを先にコピー
2. `composer install` を実行(ここでDocker層としてキャッシュされる)
3. 残りのソースコードを `COPY . .` する
これにより、依存関係(ライブラリ)に変更がない限り、2回目以降のビルドは一瞬で完了します。これが開発効率を劇的に上げる最大の秘訣です。
③ `–no-dev` と `–no-scripts` の哲学
- `–no-dev`: 本番環境で `phpunit` などのテストフレームワークが動く必要はありません。脆弱性のリスクを減らし、容量を削るために必ず指定します。
- `–no-scripts`: パッケージインストール時に自動実行されるスクリプト(`post-install-cmd`など)は、まだソースコード(環境変数や設定ファイルなど)が揃っていないビルド初期段階ではエラーの原因になります。安全にビルドを完結させるための必須テクニックです。
—
3. 動作確認:どれほど速く、軽くなったか?
実際にこの構成でビルドを行い、その効果を体感してみましょう。
準備:最小限の `composer.json`
動作確認用として、プロジェクトルートに簡単な `composer.json` を用意します。
{
“name”: “developer/optimized-app”,
“description”: “Composer Docker Optimization Sample”,
“require”: {
“monolog/monolog”: “^3.5”
}
}
ビルドの実行とログ
以下のコマンドをターミナルで実行してください。
docker build -t my-php-app:latest .
【初回ビルド時の様子】
[+] Building 12.5s (11/11) FINISHED
=> [internal] load build definition from Dockerfile
=> => transferring dockerfile: 856B
=> [builder 1/5] FROM docker.io/library/composer:2.6@sha256:…
=> [builder 2/5] COPY composer.json composer.lock ./
=> [builder 3/5] RUN composer install –no-dev –no-scripts …
# ここでMonologなどのライブラリがダウンロードされます
=> [builder 4/5] COPY . .
=> [builder 5/5] RUN composer dump-autoload –optimize –no-dev
=> [production 2/2] COPY –from=builder /app /var/www/html
=> exporting to image
【2回目(ソースコードのみ変更後)のビルド】
もう一度ビルドを実行してみてください。今度は `composer.json` に変更がないため、ステップ2〜3が一瞬でスキップされ、1秒未満でビルドが完了します。このサクサク感、たまりませんよね。
容量の確認
イメージのサイズを確認してみましょう。
docker images my-php-app
開発ツールやComposer本体が含まれていないため、Alpineベースの非常に軽量(数十MB程度)なイメージが完成しているはずです。
—
先輩エンジニアからのエール
今回ご紹介したマルチステージビルドとComposerの最適化は、モダンなPHP開発において「知っているか・いないか」でインフラの品質が大きく変わる分水嶺です。
最初は少し難しく感じるかもしれませんが、一度このテンプレートを手に入れたら、どんな巨大なLaravelやSymfonyのプロジェクトであっても、クリーンで高速なCI/CDパイプラインを組むことができるようになります。
毎日のコーディングだけでなく、デプロイの待ち時間からも解放されて、よりクリエイティブな開発に集中していきましょう!あなたの開発ライフがより快適になることを応援しています。