Compilações em múltiplas etapas e otimização
Reduza as imagens e separe a compilação da execução.
Compilações em múltiplas etapas e otimização é uma aula grátis de PHP Academy no CoddyKit. Esta é a aula 2 de 4. Você pode ler a aula completa abaixo gratuitamente — depois pratica ao vivo no navegador com um editor de código integrado e um tutor de IA 24/7. Faz parte do caminho de aprendizado de PHP Academy, e seu progresso é sincronizado entre a web e o app CoddyKit. O curso de PHP Academy inclui 4 aulas no total.
Por que usar vários estágios
Uma imagem de estágio único leva o Composer, as dependências de compilação, os cabeçalhos de desenvolvimento e sua pasta tests/ para a produção. As compilações com vários estágios permitem compilar e instalar tudo em um estágio construtor completo e copiar apenas os artefatos finalizados para um estágio de execução enxuto.
Resultado: imagens menores, uma superfície de ataque menor, transferências mais rápidas e nenhum compilador enviado para a produção.
Estágios nomeados
Cada FROM ... AS name inicia um novo estágio. Os estágios posteriores podem usar COPY --from=name para copiar arquivos dos anteriores. Apenas o estágio final se torna sua imagem; os estágios intermediários são descartados, mas permanecem armazenados em 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 . .Separando as dependências de compilação
A compilação de extensões precisa de autoconf, gcc e cabeçalhos de desenvolvimento — nada disso pertence ao ambiente de execução. Use o instalador em um estágio construtor e depois copie os arquivos .so compilados e o arquivo ini correspondente de conf.d para um estágio de execução limpo.
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/Ordem do armazenamento das camadas em cache
O Docker armazena as camadas em cache de cima para baixo e invalida tudo abaixo de uma camada alterada. Ordene da que muda menos para a que muda mais frequentemente:
- Base + extensões (raramente)
composer.lock+ instalação (ocasionalmente)- Código-fonte do aplicativo (a cada confirmação)
- Geração do autoload (a cada confirmação)
Isso significa que uma alteração somente no código reutiliza integralmente a camada vendor armazenada em 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 --optimizeMontagens de cache do BuildKit
Com o BuildKit (DOCKER_BUILDKIT=1), você pode montar um cache persistente que sobrevive entre as compilações sem acabar na imagem. Isso é perfeito para o cache global do Composer, pois as compilações repetidas deixam de baixar os pacotes novamente.
# 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-distMedindo o tamanho da imagem
Inspecione o detalhamento das camadas para encontrar excessos. docker history mostra o tamanho adicionado por cada instrução; ferramentas como dive mostram o espaço desperdiçado. O objetivo é ter um estágio de execução sem compiladores, sem Composer e sem dependências de desenvolvimento.
# 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:latestEnxugando o estágio final
O estágio de execução NÃO deve conter o Composer, o script instalador de extensões nem sua suíte de testes. Copie vendor e o código-fonte dos estágios construtores; nunca execute composer no estágio final. Remova o instalador depois de usá-lo, caso precise executá-lo nesse estágio.
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"]Limitações de Distroless / Scratch
O PHP não pode ser executado em um scratch verdadeiramente vazio — ele precisa da libc e de bibliotecas compartilhadas. O limite prático é alpine (musl) ou um Debian mínimo no estilo distroless. O Alpine é menor, mas fique atento às bibliotecas nativas que esperam glibc; se ocorrerem falhas de segmentação com NSS/ICU, use php:8.3-fpm-bookworm como alternativa.
# 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-bookwormDirecionando os estágios
Um único Dockerfile pode atender ao desenvolvimento e à produção por meio de --target. Adicione um estágio dev sobre o estágio de execução, reintroduzindo as dependências de desenvolvimento do Composer e o Xdebug; use --target=runtime para a produção e --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 .Raciocinando sobre os tamanhos das camadas
Um modelo mental rápido ajuda você a prever o comportamento do cache. Este trecho da CLI simula o erro clássico de crescimento ilimitado da camada em contraste com um crescimento limitado, ilustrando por que é importante combinar a limpeza no mesmo 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";
?>Combine e limpe em um único RUN
Cada RUN é uma camada; excluir arquivos em uma camada posterior não reduz o tamanho da imagem, porque as camadas anteriores ainda contêm os bytes. Instale, use e limpe tudo em um único RUN para que os arquivos temporários nunca sejam confirmados na imagem.
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/*Verificação rápida
Por que as dependências de compilação precisam ser removidas no mesmo RUN em que foram instaladas?
Recapitulação
As compilações com vários estágios mantêm compiladores e dependências de desenvolvimento fora da produção. Você aprendeu a nomear estágios e usar COPY --from para copiar artefatos, ordenar as camadas da menos à mais volátil para aproveitar o cache, usar montagens de cache do BuildKit para o Composer, medir o tamanho com docker history/dive, escolher deliberadamente entre alpine e glibc, direcionar os estágios de desenvolvimento e produção e combinar a instalação e a limpeza em um único RUN.
Perguntas Frequentes
A aula “Compilações em múltiplas etapas e otimização” é grátis?
Sim — o texto completo de “Compilações em múltiplas etapas e otimização” é grátis para ler aqui na web. Para praticá-la interativamente (um editor de código integrado e um tutor de IA 24/7) e desbloquear o restante do curso de PHP Academy, atualize para CoddyKit PRO. O curso de PHP Academy inclui 4 aulas no total.
O que vou aprender em “Compilações em múltiplas etapas e otimização”?
Reduza as imagens e separe a compilação da execução. Você pratica PHP Academy com código prático que executa diretamente no navegador, e um tutor de IA 24/7 responde suas dúvidas enquanto trabalha na aula.
Preciso ter experiência prévia para começar PHP Academy?
Nenhuma experiência prévia é necessária. PHP Academy no CoddyKit é estruturado para alunos iniciantes até avançados, então você pode começar aqui ou desde o início e aprender no seu ritmo. Esta é a aula 2 de 4.
Quanto tempo leva a aula “Compilações em múltiplas etapas e otimização”?
A maioria das aulas CoddyKit leva cerca de 5–10 minutos. Cada uma é compacta e interativa, então você faz progresso constante e retoma exatamente de onde parou entre web e app.
Posso escrever e executar código nesta aula de PHP Academy?
Sim. Cada aula de PHP Academy inclui um editor de código integrado, então você escreve e executa código real direto no navegador e recebe feedback de IA instantaneamente — nenhuma configuração local necessária.
Todas as aulas deste curso
- Conteinerizando uma aplicação PHP
- Compilações em múltiplas etapas e otimização
- Docker Compose para ambientes locais
- CI/CD com GitHub Actions