PHP uygulamaları için Docker imajlarının 1 GB'ı aşması alışılmış bir durum. Bunun başlıca nedenleri build araçları, geliştirme paketleri ve hiç temizlenmeyen katman önbellek şişmesidir. Multi-stage build'ler bu sorunu temiz bir şekilde çözer: tek bir aşamada build alır, yalnızca runtime artefaktları son ve yalın imaja kopyalarsınız.

Sorun

Tipik tek aşamalı bir PHP Dockerfile; Composer, Node, npm, build-essential ve çeşitli geliştirme başlıklarını çeker. Bunların hiçbiri runtime'da gerekli olmasa da tamamı nihai imajda yer alır. Başlangıç noktamız orta ölçekli bir Laravel uygulaması için 1.2 GB'lık bir imajdı.

Multi-Stage Yaklaşımı

Püf nokta, build bağımlılıklarını runtime bağımlılıklarından ayırmaktır. Her FROM ifadesi yeni bir aşama başlatır; son aşama yalnızca açıkça COPY --from ile belirttiğiniz şeyleri alır.

# Aşama 1: Composer bağımlılıkları
FROM composer:2 AS vendor
WORKDIR /app
COPY composer.json composer.lock ./
RUN composer install --no-dev --optimize-autoloader --no-scripts

# Aşama 2: Node varlıkları
FROM node:20-alpine AS assets
WORKDIR /app
COPY package.json package-lock.json vite.config.js ./
COPY resources ./resources
RUN npm ci && npm run build

# Aşama 3: Runtime imajı
FROM php:8.3-fpm-alpine
WORKDIR /var/www/html
COPY . .
COPY --from=vendor /app/vendor ./vendor
COPY --from=assets /app/public/build ./public/build
RUN php artisan config:cache && php artisan route:cache

Sonuçlar

Bu yapıyı uyguladıktan sonra imaj 1.2 GB'dan 180 MB'a indi — yüzde 85 küçülme. Docker katman önbelleği sayesinde vendor ve asset aşamaları yalnızca ilgili lock dosyaları değiştiğinde yeniden build aldığından, build süresi aşağı yukarı aynı kaldı.

Dikkat Edilmesi Gerekenler

  • Tam uygulama bir araya gelmeden artisan komutlarının çalışmasını önlemek için vendor aşamasında composer install --no-scripts kullanın
  • .env dosyasını imaja kopyalamayın — ortam değişkenlerini runtime'da enjekte edin
  • Runtime aşamasındaki php artisan config:cache konfigürasyonu build sırasında sabitler; CI ortamınızda tüm gerekli ortam değişkenlerinin tanımlı olduğundan emin olun