Docker Multi-Stage Build ile Production-Ready Image Oluşturma

Docker Multi-Stage Build ile Production-Ready Image Oluşturma

Standart bir Dockerfile ile oluşturulan image'lar genellikle build araçları, geliştirme bağımlılıkları ve gereksiz dosyalar içerir. Bu durum image boyutunu şişirir ve saldırı yüzeyini genişletir. Docker multi-stage build, tek bir Dockerfile içinde birden fazla aşama tanımlayarak yalnızca üretim için

Standart bir Dockerfile ile oluşturulan image'lar genellikle build araçları, geliştirme bağımlılıkları ve gereksiz dosyalar içerir. Bu durum image boyutunu şişirir ve saldırı yüzeyini genişletir. Docker multi-stage build, tek bir Dockerfile içinde birden fazla aşama tanımlayarak yalnızca üretim için gerekli dosyaları son image'a kopyalamanızı sağlar.

Neden Multi-Stage Build Kullanmalısınız?

Tek aşamalı bir Node.js Dockerfile'ı düşünün: node_modules içindeki devDependencies, TypeScript compiler, test framework'leri ve kaynak dosyaları hepsi son image'a dahil olur. Tipik bir Node.js uygulamasında bu fark 800 MB ile 150 MB arasında olabilir.

Kriter Tek Aşamalı Multi-Stage
Image Boyutu 600-1200 MB 80-200 MB
Build Araçları Image'da kalır Yalnızca build aşamasında
Güvenlik Yüzeyi Geniş (gereksiz paketler) Minimal (yalnızca runtime)
Pull/Deploy Süresi Yavaş Hızlı

Node.js Multi-Stage Build Örneği

Aşağıdaki Dockerfile üç aşamadan oluşur: bağımlılık kurulumu, build ve üretim. Her aşama yalnızca ihtiyaç duyduğu dosyaları bir sonraki aşamaya aktarır.

Dockerfile
# Asama 1: Bagimliliklari kur
FROM node:20-alpine AS deps
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci

# Asama 2: Uygulamayi derle
FROM node:20-alpine AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
RUN npm run build

# Asama 3: Uretim image'i
FROM node:20-alpine AS runner
WORKDIR /app
ENV NODE_ENV=production

# Guvenlik: root olmayan kullanici
RUN addgroup -S appgroup && adduser -S appuser -G appgroup

# Yalnizca uretim bagimliliklari
COPY --from=deps /app/package.json ./
RUN npm ci --only=production && npm cache clean --force

# Derlenmis dosyalar
COPY --from=builder /app/dist ./dist

USER appuser
EXPOSE 3000
CMD ["node", "dist/main.js"]

💡 İpucu: npm ci komutu npm install'dan farklı olarak package-lock.json'ı birebir takip eder ve mevcut node_modules'ı siler. CI/CD ortamlarında tekrarlanabilir build'ler için her zaman npm ci kullanın.

Go Uygulaması: Scratch Image ile Minimal Build

Go statik binary ürettiği için son image olarak scratch (tamamen boş) veya distroless kullanabilirsiniz. Bu sayede image boyutu 10-15 MB'a düşer ve shell bile bulunmaz.

Dockerfile.go
# Build asamasi
FROM golang:1.22-alpine AS builder
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -ldflags="-s -w" -o /app/server ./cmd/server

# Uretim: scratch = tamamen bos image
FROM scratch
COPY --from=builder /app/server /server
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
EXPOSE 8080
ENTRYPOINT ["/server"]

Python Multi-Stage Build

Python uygulamalarında build aşamasında C extension'ları derlemek için gcc ve build-essential gerekir. Multi-stage build ile bu araçları son image'dan çıkarabilirsiniz.

Dockerfile.python
# Build: C extension'lari derle
FROM python:3.12-slim AS builder
RUN apt-get update && apt-get install -y --no-install-recommends \
    gcc libpq-dev && rm -rf /var/lib/apt/lists/*
WORKDIR /app
COPY requirements.txt .
RUN pip install --user --no-cache-dir -r requirements.txt

# Uretim: gcc yok, yalnizca runtime
FROM python:3.12-slim
WORKDIR /app
COPY --from=builder /root/.local /root/.local
COPY . .
ENV PATH=/root/.local/bin:$PATH
EXPOSE 8000
CMD ["gunicorn", "app:create_app()", "-b", "0.0.0.0:8000"]

Layer Cache Optimizasyonu

Docker her Dockerfile komutunu ayrı bir layer olarak saklar. Bir layer değiştiğinde ondan sonraki tüm layer'lar yeniden oluşturulur. Bu nedenle sık değişen dosyaları (kaynak kod) en sona, nadir değişenleri (bağımlılıklar) en başa koyun.

  • Önce package.json, sonra kaynak kod Bağımlılık dosyalarını ayrı COPY komutuyla kopyalayın. Kaynak kod değiştiğinde npm ci tekrar çalışmaz.
  • .dockerignore Kullanın node_modules, .git, dist, test ve log dosyalarını .dockerignore'a ekleyin. Build context boyutunu küçültür ve cache'i korur.
  • RUN Komutlarını Birleştirin Birden fazla apt-get veya apk komutunu tek RUN'da birleştirin. Her RUN yeni bir layer oluşturur.
.dockerignore
node_modules
dist
.git
.gitignore
*.md
docker-compose*.yml
.env*
coverage
tests
__tests__
.nyc_output

CI/CD Pipeline Entegrasyonu

Multi-stage build'i GitHub Actions ile birleştirerek her push'ta otomatik build, tarama ve deploy yapabilirsiniz.

terminal
# Belirli bir asamayi hedefleyerek build
docker build --target builder -t myapp:test .

# BuildKit ile paralel build (daha hizli)
DOCKER_BUILDKIT=1 docker build -t myapp:v1.0.0 .

# Build sonrasi image boyutunu kontrol et
docker images myapp
# REPOSITORY  TAG     SIZE
# myapp       v1.0.0  147MB

# Guvenlik taramasi
trivy image --severity CRITICAL,HIGH myapp:v1.0.0

Docker image güvenlik taraması için Container Güvenliği rehberimizi, CI/CD pipeline kurulumu için GitHub Actions rehberimizi inceleyin. Kubernetes'e deploy için Kubernetes Giriş rehberimize göz atın. Docker resmi dokümantasyonundaki multi-stage build kılavuzu ve build cache optimizasyonu ek kaynak olarak faydalıdır.

Sıkça Sorulan Sorular

Multi-stage build, build süresini uzatır mı?

İlk build'de ek süre olabilir ancak Docker layer cache sayesinde sonraki build'ler genellikle daha hızlıdır. BuildKit ile aşamalar paralel çalışabilir ve toplam süre kısalır.

Kaç aşama kullanmalıyım?

Genellikle 2-3 aşama yeterlidir: bağımlılık kurulumu, build ve üretim. Daha fazla aşama karmaşıklık ekler. Her aşamanın net bir amacı olmalıdır.

Alpine image'lar her zaman en küçük seçenek mi?

Alpine genellikle en küçük seçenektir ancak musl libc kullanır. Bazı Python ve Node.js paketleri glibc gerektirir. Bu durumda Debian slim veya distroless tercih edin. Go uygulamaları için scratch en küçük seçenektir.

Ara aşamadaki dosyalara nasıl erişirim?

COPY --from=aşama_adı komutuyla herhangi bir önceki aşamadan dosya kopyalayabilirsiniz. Aşama adı yerine numara da kullanılabilir (0, 1, 2) ancak isimli aşamalar daha okunabilirdir.

BuildKit nedir ve neden kullanmalıyım?

BuildKit, Docker'ın yeni nesil build motorudur. Paralel aşama çalıştırma, gelişmiş cache yönetimi ve secret mount desteği sunar. Docker 23.0+ sürümlerinde varsayılan olarak aktiftir.

Sonuç

Docker multi-stage build ile image boyutunu %70-90 küçültebilir, güvenlik yüzeyini daraltabilir ve deploy sürelerini kısaltabilirsiniz. Bağımlılık kurulumu ve build aşamalarını ayırın, .dockerignore kullanın ve layer cache'i optimize edin. Her dil için uygun base image seçimi yaparak üretim ortamınızı güvenli ve hızlı tutun.

Container Altyapınız İçin Yüksek Performans

Hosted Cloud bulut sunucuları ile Docker container'larınızı NVMe SSD hızında çalıştırın.

Bulut Sunucu Planlarını İncele →
M

Merve Arslan

WordPress & Hosting Uzmanı

WordPress performans optimizasyonu, hosting seçimi ve e-ticaret altyapıları üzerine rehber içerikler hazırlamaktadır.

Yorumlar yakında