0Pricing
PHP Academy · Lezione

Build multi-stage e ottimizzazione

Riduca le immagini e separi la fase di build dal runtime

Build multi-stage e ottimizzazione è una lezione PHP Academy gratuita su CoddyKit. Questa è la lezione 2 di 4. Puoi leggere la lezione completa qui gratuitamente — poi esercitati direttamente nel browser con un editor di codice integrato e un tutor IA disponibile 24/7. Fa parte del percorso di apprendimento PHP Academy, e i tuoi progressi si sincronizzano tra il web e l'app CoddyKit. Il corso PHP Academy include 4 lezioni in totale.

Perché usare più stage

Un'immagine a singolo stage porta Composer, le dipendenze di build, gli header di sviluppo e la cartella tests/ in produzione. Le build multi-stage consentono di compilare e installare tutto in uno stage builder completo e di copiare solo gli artefatti finiti in uno stage runtime snello.

Risultato: immagini più piccole, superficie d'attacco ridotta, pull più rapidi e nessun compilatore distribuito in produzione.

Stage con nome

Ogni FROM ... AS name avvia un nuovo stage. Gli stage successivi possono usare COPY --from=name per copiare file dagli stage precedenti. Solo lo stage finale diventa l'immagine; gli stage intermedi vengono scartati, ma memorizzati nella cache.

# 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 . .

Separare le dipendenze di build

La compilazione delle estensioni richiede autoconf, gcc e gli header di sviluppo, nessuno dei quali appartiene al runtime. Usi l'installer in uno stage builder, quindi copi i file .so compilati e il relativo ini di conf.d in uno stage runtime pulito.

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/

Ordine della cache dei layer

Docker memorizza nella cache i layer dall'alto verso il basso e invalida tutto ciò che si trova sotto un layer modificato. Ordini dal meno al più soggetto a modifiche frequenti:

  • Base + estensioni (raramente)
  • composer.lock + installazione (occasionalmente)
  • Sorgente dell'applicazione (a ogni commit)
  • Dump dell'autoload (a ogni commit)

Questo significa che una modifica che riguarda solo il codice riutilizza interamente il layer vendor memorizzato nella cache.

# 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

Mount della cache di BuildKit

Con BuildKit (DOCKER_BUILDKIT=1) è possibile montare una cache persistente che sopravvive tra le build senza finire nell'immagine. È perfetta per la cache globale di Composer, così le build successive evitano di scaricare nuovamente i pacchetti.

# 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

Misurare le dimensioni dell'immagine

Esamini la suddivisione in layer per individuare il peso superfluo. docker history mostra le dimensioni aggiunte da ogni istruzione; strumenti come dive mostrano lo spazio sprecato. L'obiettivo è uno stage runtime senza compilatori, Composer o dipendenze di sviluppo.

# 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

Alleggerire lo stage finale

Lo stage runtime NON deve contenere Composer, lo script extension-installer o la suite di test. Copi vendor e il sorgente dai builder; non esegua mai composer nello stage finale. Rimuova l'installer dopo l'uso se deve eseguirlo lì.

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"]

Limiti di Distroless / Scratch

PHP non può essere eseguito su uno scratch veramente vuoto: ha bisogno di libc e librerie condivise. Il limite pratico minimo è alpine (musl) o una Debian minimale in stile distroless. Alpine è più piccola, ma presti attenzione alle librerie native che si aspettano glibc; se si verificano segmentation fault con NSS/ICU, ripieghi su 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

Selezionare lo stage di destinazione

Un solo Dockerfile può servire sviluppo e produzione tramite --target. Aggiunga uno stage dev sopra runtime che aggiunge nuovamente le dipendenze dev di Composer e Xdebug; usi --target=runtime per la produzione e --target=dev in locale.

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  .

Ragionare sulle dimensioni dei layer

Un semplice modello mentale aiuta a prevedere il comportamento della cache. Questo frammento CLI simula l'errore classico della crescita illimitata di un layer rispetto a quella con un limite, mostrando perché è importante combinare la pulizia nello stesso 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";
?>

Combinare installazione e pulizia in un unico RUN

Ogni RUN è un layer; eliminare file in un layer successivo non riduce le dimensioni dell'immagine, perché i layer precedenti contengono ancora quei byte. Installi, utilizzi e pulisca tutto in un unico RUN, così i file temporanei non vengono mai inseriti nell'immagine.

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/*

Controllo rapido

Perché le dipendenze di build devono essere rimosse nello stesso RUN in cui sono state installate?

Riepilogo

Le build multi-stage tengono compilatori e dipendenze di sviluppo fuori dalla produzione. Ha imparato a: assegnare nomi agli stage e copiare gli artefatti con COPY --from, ordinare i layer dal meno al più soggetto a cambiamenti per ottimizzare la cache, usare i mount della cache di BuildKit per Composer, misurare le dimensioni con docker history/dive, scegliere consapevolmente tra alpine e glibc, selezionare gli stage dev/prod e combinare installazione e pulizia in un unico RUN.

Domande Frequenti

La lezione «Build multi-stage e ottimizzazione» è gratuita?

Sì — il testo completo di «Build multi-stage e ottimizzazione» è gratuito qui sul web. Per esercitarvi in modo interattivo (un editor di codice integrato e un tutor IA 24/7) e sbloccare il resto del corso PHP Academy, passa a CoddyKit PRO. Il corso PHP Academy include 4 lezioni in totale.

Cosa imparerò in «Build multi-stage e ottimizzazione»?

Riduca le immagini e separi la fase di build dal runtime Eserciti PHP Academy con codice pratico che esegui direttamente nel browser, e un tutor IA 24/7 risponde alle tue domande mentre lavori sulla lezione.

Ho bisogno di esperienza per iniziare PHP Academy?

Non è richiesta alcuna esperienza precedente. PHP Academy su CoddyKit è strutturato per principianti e studenti avanzati, quindi puoi iniziare da qui o dall'inizio e procedere al tuo ritmo. Questa è la lezione 2 di 4.

Quanto tempo richiede la lezione «Build multi-stage e ottimizzazione»?

La maggior parte delle lezioni CoddyKit richiede circa 5–10 minuti. Ogni lezione è breve e interattiva, quindi fai progressi costanti e riprendi esattamente da dove hai lasciato su web e app.

Posso scrivere ed eseguire codice in questa lezione PHP Academy?

Sì. Ogni lezione PHP Academy include un editor di codice integrato, quindi scrivi ed esegui codice reale direttamente nel tuo browser e ricevi feedback istantaneo dall'IA — nessuna configurazione locale necessaria.

Tutte le lezioni di questo corso

  1. Containerizzare un'applicazione PHP
  2. Build multi-stage e ottimizzazione
  3. Docker Compose per gli stack locali
  4. CI/CD con GitHub Actions
← Torna a PHP Academy