0Pricing
PHP Academy · レッスン

マルチステージビルドと最適化

イメージを軽量化し、ビルドと実行環境を分離します。

「マルチステージビルドと最適化」は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 --optimize

BuildKitのキャッシュマウント

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フィードバックを取得できます。ローカル設定は不要です。

このコースのすべてのレッスン

  1. PHPアプリケーションのコンテナ化
  2. マルチステージビルドと最適化
  3. ローカル環境向けDocker Compose
  4. GitHub ActionsによるCI/CD
← PHP Academyに戻る