マルチステージビルドと最適化
イメージを軽量化し、ビルドと実行環境を分離します。
「マルチステージビルドと最適化」はCoddyKit上の無料PHP Academyレッスンです。 これはレッスン2/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはPHP Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 PHP Academyコースには全4レッスンが含まれています。
マルチステージにする理由
単一ステージのイメージには、Composer、ビルド用依存関係、開発用ヘッダー、tests/フォルダーまで本番環境に含まれてしまいます。マルチステージビルドを使うと、大きなビルダーステージでコンパイルやインストールを行い、完成した成果物だけをスリムなランタイムステージにコピーできます。
その結果、イメージが小さくなり、攻撃対象領域が縮小し、取得が速くなり、本番環境にコンパイラを持ち込まずに済みます。
ステージに名前を付ける
各FROM ... AS nameは新しいステージを開始します。後続のステージでは、COPY --from=nameを使って前のステージからファイルを取り出せます。イメージになるのは最終ステージだけで、中間ステージは破棄されます(ただしキャッシュされます)。
# Stage 1: dependencies
FROM composer:2 AS vendor
WORKDIR /app
COPY composer.json composer.lock ./
RUN composer install --no-dev --no-scripts --prefer-dist --ignore-platform-reqs
# Stage 2: runtime
FROM php:8.3-fpm-alpine AS runtime
WORKDIR /app
COPY --from=vendor /app/vendor ./vendor
COPY . .ビルド用依存関係を分離する
拡張機能のコンパイルにはautoconf、gcc、開発用ヘッダーが必要ですが、これらはランタイムには不要です。ビルダーステージでインストーラーを使い、コンパイル済みの.soファイルと対応するconf.dのiniファイルをクリーンなランタイムステージにコピーしてください。
FROM php:8.3-fpm-alpine AS ext-builder
ADD https://github.com/mlocati/docker-php-extension-installer/releases/latest/download/install-php-extensions /usr/local/bin/
RUN chmod +x /usr/local/bin/install-php-extensions && \
install-php-extensions redis igbinary opcache intl
FROM php:8.3-fpm-alpine AS runtime
# Copy compiled extensions + their enable configs
COPY --from=ext-builder /usr/local/lib/php/extensions/ /usr/local/lib/php/extensions/
COPY --from=ext-builder /usr/local/etc/php/conf.d/ /usr/local/etc/php/conf.d/レイヤーのキャッシュ順序
Dockerはレイヤーを上から下へキャッシュし、変更されたレイヤーより下にあるすべてのレイヤーを無効化します。変更頻度が低いものから高いものへ並べます。
- ベース + 拡張機能(まれ)
composer.lock+ インストール(ときどき)- アプリケーションのソース(コミットごと)
- オートロードのダンプ(コミットごと)
つまり、コードだけを変更した場合は、キャッシュ済みのvendorレイヤー全体を再利用できます。
# BAD: copying all source before composer install
# busts the vendor layer on every code change
COPY . .
RUN composer install
# GOOD: lock first, then source
COPY composer.json composer.lock ./
RUN composer install --no-dev --no-autoloader
COPY . .
RUN composer dump-autoload --optimizeBuildKitのキャッシュマウント
BuildKit(DOCKER_BUILDKIT=1)を使うと、ビルド間で保持される永続キャッシュをマウントできます。このキャッシュはイメージには含まれません。Composerのグローバルキャッシュに最適で、繰り返しのビルドではパッケージの再ダウンロードを省略できます。
# syntax=docker/dockerfile:1
FROM composer:2 AS vendor
WORKDIR /app
COPY composer.json composer.lock ./
RUN --mount=type=cache,target=/tmp/composer-cache \
COMPOSER_CACHE_DIR=/tmp/composer-cache \
composer install --no-dev --prefer-distイメージサイズを測定する
レイヤーの内訳を調べて、肥大化の原因を見つけます。docker historyは各命令が追加したサイズを示し、diveのようなツールは無駄な領域を示します。目標は、コンパイラもComposerも開発用依存関係も含まないランタイムステージです。
# Compare sizes
docker images myapp
# Per-layer contribution
docker history --no-trunc --format '{{.Size}}\t{{.CreatedBy}}' myapp:latest
# Deep inspection of wasted bytes
dive myapp:latest最終ステージをスリムにする
ランタイムステージにはComposer、拡張機能インストーラースクリプト、テストスイートを含めてはいけません。ビルダーステージからvendorとソースをコピーし、最終ステージでcomposerを実行することは避けてください。そこで実行する必要がある場合も、使用後にインストーラーを削除してください。
FROM php:8.3-fpm-alpine AS runtime
WORKDIR /app
# bring extensions + vendor in from builders — no Composer here
COPY --from=ext-builder /usr/local/lib/php/extensions/ /usr/local/lib/php/extensions/
COPY --from=ext-builder /usr/local/etc/php/conf.d/ /usr/local/etc/php/conf.d/
COPY --from=vendor /app/vendor ./vendor
COPY . .
USER www-data
CMD ["php-fpm"]Distroless / Scratchの制約
PHPは完全に空のscratch上では実行できません。libcと共有ライブラリが必要だからです。実用上の最小構成はalpine(musl)またはDistroless形式の最小Debianです。Alpineは最小ですが、glibcを前提とするネイティブライブラリには注意してください。NSS/ICUでセグメンテーションフォールトが発生した場合は、php:8.3-fpm-bookwormに切り替えてください。
# Smallest practical PHP runtime
FROM php:8.3-fpm-alpine
# If musl causes native-lib issues (e.g., some ICU edge cases),
# the glibc Debian slim variant is the safe fallback:
# FROM php:8.3-fpm-bookwormステージを指定する
1つのDockerfileで、--targetを使って開発環境と本番環境の両方に対応できます。ランタイムの上にdevステージを追加し、Composerの開発用依存関係とXdebugを再び追加します。本番用には--target=runtime、ローカル用には--target=devでビルドしてください。
FROM runtime AS dev
ADD https://github.com/mlocati/docker-php-extension-installer/releases/latest/download/install-php-extensions /usr/local/bin/
RUN chmod +x /usr/local/bin/install-php-extensions && \
install-php-extensions xdebug @composer
USER root
RUN composer install # includes dev deps
# Build prod: docker build --target runtime -t app:prod .
# Build dev: docker build --target dev -t app:dev .レイヤーサイズを考える
簡単なメンタルモデルを使うと、キャッシュの動作を予測しやすくなります。このCLIスニペットは、レイヤーが際限なく増加する典型的な誤りと、上限を設けた場合をシミュレートし、クリーンアップを同じRUNにまとめることが重要な理由を示します。
<?php
// Simulate layer sizes (MB) for two strategies
$installSteps = [120, 8, 8, 8];
// Separate RUN per step keeps temp files in layers
$separate = array_sum($installSteps);
// Single RUN with cleanup removes temp files before commit
$combined = max($installSteps); // peak, then cleaned
echo "Separate layers total: {$separate} MB\n";
echo "Combined+cleanup: {$combined} MB\n";
echo 'Saved: ' . ($separate - $combined) . " MB\n";
?>1つのRUNでまとめてクリーンアップする
各RUNはレイヤーです。後続のレイヤーでファイルを削除しても、前のレイヤーにバイト列が残っているため、イメージは小さくなりません。1つのRUN内でインストール、使用、クリーンアップを行い、一時ファイルがコミットされないようにしてください。
RUN apk add --no-cache --virtual .build-deps $PHPIZE_DEPS && \
pecl install redis && \
docker-php-ext-enable redis && \
apk del .build-deps && \
rm -rf /tmp/pear /var/cache/apk/*確認
ビルド用依存関係を、それらをインストールしたのと同じRUNで削除しなければならないのはなぜですか?
まとめ
マルチステージビルドを使うと、コンパイラや開発用依存関係を本番環境から除外できます。ステージに名前を付けてCOPY --fromで成果物をコピーすること、キャッシュのためにレイヤーを変更されにくい順から変更されやすい順に並べること、ComposerにはBuildKitのキャッシュマウントを使うこと、docker history/diveでサイズを測定すること、alpineとglibcを意図的に使い分けること、開発用と本番用のステージを指定すること、インストールとクリーンアップを1つのRUNにまとめることを学びました。
よくある質問
「マルチステージビルドと最適化」レッスンは無料ですか?
はい。「マルチステージビルドと最適化」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、PHP Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 PHP Academyコースには全4レッスンが含まれています。
「マルチステージビルドと最適化」で何を学びますか?
イメージを軽量化し、ビルドと実行環境を分離します。 ブラウザで直接実行するハンズオンコードでPHP Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。
PHP Academyを始めるのに経験は必要ですか?
事前経験は必要ありません。CoddyKitのPHP Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン2/4です。
「マルチステージビルドと最適化」レッスンにはどのくらい時間がかかりますか?
ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。
このPHP Academyレッスンでコードを書いて実行できますか?
はい。すべてのPHP Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。