Multi-Stage-Builds und Optimierung
Images verkleinern und Build von der Laufzeit trennen
Multi-Stage-Builds und Optimierung ist eine kostenlose PHP Academy-Lektion auf CoddyKit. Dies ist Lektion 2 von 4. Du kannst die komplette Lektion unten kostenlos lesen – dann übst du sie direkt im Browser mit einem integrierten Code-Editor und einem KI-Tutor rund um die Uhr. Sie ist Teil des PHP Academy-Lernpfads, und dein Fortschritt wird über Web und CoddyKit-App synchronisiert. Der PHP Academy-Kurs umfasst insgesamt 4 Lektionen.
Warum Multi-Stage-Builds
Ein einstufiges Image enthält Composer, Build-Abhängigkeiten, Entwicklungs-Header und Ihren tests/-Ordner in der Produktion. Mit Multi-Stage-Builds können Sie in einer umfangreichen Builder-Stage kompilieren und installieren und nur die fertigen Artefakte in eine schlanke Runtime-Stage kopieren.
Das Ergebnis: kleinere Images, eine kleinere Angriffsfläche, schnellere Downloads und keine Compiler in der Produktion.
Benannte Stages
Jedes FROM ... AS name startet eine neue Stage. Spätere Stages können mit COPY --from=name Dateien aus früheren Stages kopieren. Nur die finale Stage wird zu Ihrem Image; Zwischenstufen werden verworfen, aber weiterhin gecacht.
# 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 . .Build-Abhängigkeiten trennen
Zum Kompilieren von Erweiterungen werden autoconf, gcc und Entwicklungs-Header benötigt – nichts davon gehört in die Runtime. Verwenden Sie das Installationsprogramm in einer Builder-Stage und kopieren Sie anschließend die kompilierten .so-Dateien sowie die passende conf.d-INI-Datei in eine saubere Runtime-Stage.
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/Reihenfolge für Layer-Caching
Docker cached Layer von oben nach unten und macht alles unterhalb einer geänderten Layer ungültig. Ordnen Sie sie von am seltensten bis am häufigsten geändert an:
- Basis und Erweiterungen (selten)
composer.lockund Installation (gelegentlich)- Anwendungsquellcode (bei jedem Commit)
- Autoload-Dump (bei jedem Commit)
Dadurch wird bei einer reinen Codeänderung die gesamte gecachte vendor-Layer wiederverwendet.
# 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-Cache-Mounts
Mit BuildKit (DOCKER_BUILDKIT=1) können Sie einen persistenten Cache mounten, der über mehrere Builds hinweg erhalten bleibt, ohne im Image zu landen. Das ist ideal für den globalen Composer-Cache, weil wiederholte Builds das erneute Herunterladen von Paketen überspringen können.
# 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-distImage-Größe messen
Untersuchen Sie die Aufschlüsselung der Layer, um überflüssige Inhalte zu finden. docker history zeigt, wie viel Größe jede Anweisung hinzugefügt hat; Tools wie dive zeigen ungenutzten Speicher. Ziel ist eine Runtime-Stage ohne Compiler, Composer und Entwicklungsabhängigkeiten.
# 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:latestDie finale Stage entschlacken
Die Runtime-Stage sollte Composer, das Extension-Installer-Skript und Ihre Test-Suite NICHT enthalten. Kopieren Sie vendor und den Quellcode aus den Builder-Stages; führen Sie niemals composer in der finalen Stage aus. Entfernen Sie das Installationsprogramm nach der Verwendung, falls Sie es dort ausführen müssen.
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"]Grenzen von Distroless / Scratch
PHP kann nicht auf dem wirklich leeren scratch ausgeführt werden – es benötigt libc und gemeinsam genutzte Bibliotheken. Die praktische Untergrenze ist alpine (musl) oder ein minimales Debian im Distroless-Stil. Alpine ist am kleinsten, aber achten Sie auf native Bibliotheken, die glibc erwarten. Wenn bei NSS/ICU Speicherzugriffsfehler auftreten, wechseln Sie zu 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-bookwormStages gezielt bauen
Ein Dockerfile kann über --target sowohl für die Entwicklung als auch für die Produktion verwendet werden. Fügen Sie oberhalb von runtime eine dev-Stage hinzu, die die Composer-Entwicklungsabhängigkeiten und Xdebug wieder ergänzt. Bauen Sie lokal mit --target=runtime für die Produktion und mit --target=dev für die Entwicklung.
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 .Layer-Größen verstehen
Ein einfaches mentales Modell hilft Ihnen, das Cache-Verhalten vorherzusagen. Dieser CLI-Ausschnitt simuliert den klassischen Fehler eines unbegrenzten Layer-Wachstums im Vergleich zu einer begrenzten Variante und zeigt, warum das Bereinigen in derselben RUN-Anweisung wichtig ist.
<?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";
?>In einem RUN kombinieren und bereinigen
Jedes RUN ist eine Layer. Das Löschen von Dateien in einer späteren Layer verkleinert das Image nicht, weil die früheren Layer die Bytes weiterhin enthalten. Installieren, verwenden und bereinigen Sie innerhalb eines einzigen RUN, damit die temporären Dateien niemals festgeschrieben werden.
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/*Kurzprüfung
Warum müssen Build-Abhängigkeiten in derselben RUN-Anweisung entfernt werden, in der sie installiert wurden?
Zusammenfassung
Multi-Stage-Builds halten Compiler und Entwicklungsabhängigkeiten aus der Produktion heraus. Sie haben gelernt, Stages zu benennen und Artefakte mit COPY --from zu kopieren, Layer für das Caching von der am wenigsten bis zur am stärksten veränderlichen anzuordnen, BuildKit-Cache-Mounts für Composer zu verwenden, die Größe mit docker history/dive zu messen, bewusst zwischen Alpine und glibc zu wählen, Entwicklungs- und Produktions-Stages gezielt zu bauen und Installation und Bereinigung in einem RUN zu kombinieren.
Häufig gestellte Fragen
Ist die Lektion „Multi-Stage-Builds und Optimierung“ kostenlos?
Ja — der vollständige Text von „Multi-Stage-Builds und Optimierung“ ist hier im Web kostenlos zu lesen. Um sie interaktiv zu üben (integrierter Code-Editor und 24/7 KI-Tutor) und den Rest des PHP Academy-Kurses freizuschalten, upgrade auf CoddyKit PRO. Der PHP Academy-Kurs umfasst insgesamt 4 Lektionen.
Was lerne ich in „Multi-Stage-Builds und Optimierung“?
Images verkleinern und Build von der Laufzeit trennen Du übst PHP Academy mit praktischem Code, den du direkt im Browser ausführst, und ein 24/7 KI-Tutor beantwortet deine Fragen während du die Lektion bearbeitest.
Brauche ich Erfahrung, um PHP Academy zu starten?
Keine Vorkenntnisse erforderlich. PHP Academy auf CoddyKit ist für Anfänger bis fortgeschrittene Lernende strukturiert, sodass du hier starten oder von Anfang an beginnen und in deinem eigenen Tempo voranschreiten kannst. Dies ist Lektion 2 von 4.
Wie lange dauert die Lektion „Multi-Stage-Builds und Optimierung“?
Die meisten CoddyKit-Lektionen dauern etwa 5–10 Minuten. Jede ist kompakt und interaktiv, sodass du stetig Fortschritte machst und genau dort weitermachst, wo du aufgehört hast – im Web und in der App.
Kann ich in dieser PHP Academy-Lektion Code schreiben und ausführen?
Ja. Jede PHP Academy-Lektion enthält einen integrierten Code-Editor, sodass du echten Code direkt in deinem Browser schreibst und ausführst und sofort KI-Feedback erhältst — ohne lokale Einrichtung erforderlich.
Alle Lektionen in diesem Kurs
- Eine PHP-Anwendung containerisieren
- Multi-Stage-Builds und Optimierung
- Docker Compose für lokale Stacks
- CI/CD mit GitHub Actions