Compilaciones multietapa y optimización
Reduzca las imágenes y separe la compilación de la ejecución
Compilaciones multietapa y optimización es una lección gratuita de PHP Academy en CoddyKit. Esta es la lección 2 de 4. Puedes leer la lección completa abajo gratuitamente — luego la practicas en el navegador con un editor de código integrado y un tutor de IA 24/7. Forma parte de la ruta de aprendizaje de PHP Academy, y tu progreso se sincroniza en la web y la app de CoddyKit. El curso de PHP Academy incluye 4 lecciones en total.
Por qué usar varias etapas
Una imagen de una sola etapa incluye Composer, las dependencias de compilación, las cabeceras de desarrollo y la carpeta tests/ en producción. Las compilaciones de varias etapas permiten compilar e instalar en una etapa builder completa y copiar únicamente los artefactos terminados a una etapa runtime ligera.
El resultado son imágenes más pequeñas, una superficie de ataque menor, descargas más rápidas y ningún compilador enviado a producción.
Etapas con nombre
Cada FROM ... AS name inicia una etapa nueva. Las etapas posteriores pueden extraer archivos de las anteriores mediante COPY --from=name. Solo la etapa final se convierte en su imagen; las etapas intermedias se descartan, aunque se almacenan en caché.
# 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 . .Separación de las dependencias de compilación
La compilación de extensiones necesita autoconf, gcc y cabeceras de desarrollo, elementos que no pertenecen al entorno de ejecución. Use el instalador en una etapa builder y, después, copie los archivos .so compilados y el ini correspondiente de conf.d a una etapa runtime limpia.
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/Orden de caché de las capas
Docker almacena las capas en caché de arriba abajo e invalida todo lo que se encuentra debajo de una capa modificada. Ordénelas desde las que cambian menos hasta las que cambian con mayor frecuencia:
- Base y extensiones (rara vez)
composer.locke instalación (ocasionalmente)- Código fuente de la aplicación (en cada commit)
- Volcado de autoload (en cada commit)
Esto significa que un cambio exclusivo en el código reutiliza por completo la capa de vendor almacenada en caché.
# 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 --optimizeMontajes de caché de BuildKit
Con BuildKit (DOCKER_BUILDKIT=1) puede montar una caché persistente que se conserva entre compilaciones sin terminar dentro de la imagen. Es ideal para la caché global de Composer, ya que las compilaciones posteriores pueden evitar volver a descargar los paquetes.
# 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-distMedición del tamaño de la imagen
Inspeccione el desglose de las capas para encontrar elementos innecesariamente voluminosos. docker history muestra el tamaño que añadió cada instrucción; herramientas como dive muestran el espacio desaprovechado. El objetivo es que la etapa runtime no contenga compiladores, Composer ni dependencias de desarrollo.
# 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:latestAligerar la etapa final
La etapa runtime NO debe contener Composer, el script extension-installer ni su conjunto de pruebas. Copie vendor y el código fuente desde las etapas builder; nunca ejecute composer en la etapa final. Si debe ejecutar el instalador allí, elimínelo después de usarlo.
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"]Limitaciones de Distroless / Scratch
PHP no puede ejecutarse sobre un scratch completamente vacío: necesita libc y bibliotecas compartidas. El límite práctico es alpine (musl) o una imagen Debian mínima de estilo distroless. Alpine ocupa menos espacio, pero tenga cuidado con las bibliotecas nativas que esperan glibc; si aparecen errores de segmentación con NSS/ICU, vuelva a 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-bookwormSelección de etapas
Un Dockerfile puede servir tanto para desarrollo como para producción mediante --target. Añada una etapa dev sobre runtime que vuelva a incluir las dependencias de desarrollo de Composer y Xdebug; compile con --target=runtime para producción y con --target=dev localmente.
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 .Razonamiento sobre el tamaño de las capas
Un modelo mental sencillo le ayuda a predecir el comportamiento de la caché. Este fragmento de CLI simula el error clásico del crecimiento ilimitado de una capa frente a uno limitado, e ilustra por qué es importante combinar la limpieza en el mismo 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";
?>Combinar y limpiar en un solo RUN
Cada RUN es una capa; eliminar archivos en una capa posterior no reduce el tamaño de la imagen porque las capas anteriores todavía contienen esos bytes. Instale, utilice y limpie dentro de un mismo RUN para que los archivos temporales nunca se incorporen.
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/*Comprobación rápida
¿Por qué deben eliminarse las dependencias de compilación en el mismo RUN en el que se instalaron?
Resumen
Las compilaciones de varias etapas mantienen los compiladores y las dependencias de desarrollo fuera de producción. Aprendió a nombrar etapas y copiar artefactos con COPY --from, ordenar las capas de las menos a las más volátiles para aprovechar la caché, usar montajes de caché de BuildKit para Composer, medir el tamaño con docker history/dive, elegir deliberadamente entre alpine y glibc, seleccionar etapas de dev/prod y combinar la instalación y la limpieza en un solo RUN.
Preguntas frecuentes
¿La lección «Compilaciones multietapa y optimización» es gratis?
Sí — el texto completo de «Compilaciones multietapa y optimización» es gratis para leer aquí en la web. Para practicarla de forma interactiva (editor de código integrado y tutor de IA 24/7) y desbloquear el resto del curso de PHP Academy, actualiza a CoddyKit PRO. El curso de PHP Academy incluye 4 lecciones en total.
¿Qué aprenderé en «Compilaciones multietapa y optimización»?
Reduzca las imágenes y separe la compilación de la ejecución Practicas PHP Academy con código real que ejecutas directamente en el navegador, y un tutor de IA 24/7 responde tus preguntas mientras trabajas en la lección.
¿Necesito experiencia previa para empezar PHP Academy?
No se requiere experiencia previa. PHP Academy en CoddyKit está estructurado para principiantes hasta estudiantes avanzados, así que puedes empezar aquí o desde el inicio y avanzar a tu ritmo. Esta es la lección 2 de 4.
¿Cuánto tiempo toma la lección «Compilaciones multietapa y optimización»?
La mayoría de las lecciones de CoddyKit toman alrededor de 5–10 minutos. Cada una es compacta e interactiva, así que avanzas constantemente y retomas exactamente por donde dejaste en la web y la app.
¿Puedo escribir y ejecutar código en esta lección de PHP Academy?
Sí. Cada lección de PHP Academy incluye un editor de código integrado, así que escribes y ejecutas código real directamente en tu navegador y obtienes retroalimentación instantánea de IA — sin configuración local necesaria.
Todas las lecciones de este curso
- Contenerización de una aplicación PHP
- Compilaciones multietapa y optimización
- Docker Compose para entornos locales
- CI/CD con GitHub Actions