Docker imaj boyutu küçültme; build süreleri, registry depolaması ve deployment hızı için yapabileceğiniz en etkili şeylerden biri. Benim ortamımda 1.2 GB'lık imajları düzenli olarak 120 MB'a, bazen daha da aşağı indiriyorum — uygulamanın fonksiyonelliğini değiştirmeden. En büyük iki kaldıracınız multi-stage builds ve Alpine tabanlı imajlara geçiş. Geri kalan her şey (layer sıralaması, .dockerignore, cache temizliği) yardımcı olur ama asıl kazanç o ikisinde.
Eğer production'da şişman Ubuntu tabanlı imajlar çekip registry'nizin neden balon gibi şiştiğini merak ediyorsanız, aradığınız çözüm bu.
Docker İmajınız Neden Bu Kadar Büyük?
Şişmiş imajların çoğu aynı pattern'den gelir: ubuntu:22.04 veya node:18 ile başlarsınız, build araçları ve compiler'lar kurarsınız, kaynağınızı kopyalarsınız, npm install veya pip install çalıştırır ve hepsini paketleyip gönderirsiniz. Sorun şu ki final imajınız her şeyi taşır — compiler'ı, build cache'ini, dev dependency'leri, apt cache'ini, hatta kimsenin okumadığı man page'leri bile.
E-ticaret projelerinde gördüğüm tipik bir şişman Dockerfile şöyledir:
FROM node:18
WORKDIR /app
COPY . .
RUN npm install
CMD ["node", "server.js"]
Bu yaklaşık 1.1–1.3 GB'lık bir imaj üretir. Uygulamanın kendisi belki 50 MB'dir. Geri kalanı ölü ağırlıktır.
Sadece node:18 base imajı ~900 MB'dir çünkü tam bir Debian kurulumu, build-essential ve Node'un ihtiyaç duyabileceği her sistem kütüphanesini içerir. node_modules klasörünüz 200–400 MB daha ekler; bunların içinde sadece test için var olan dev dependency'ler de dahildir.
Multi-Stage Builds: En Büyük Tek Kazanç
Multi-stage builds size build için bir imaj, çalıştırmak için tamamen farklı (daha küçük) bir imaj kullanma imkanı verir. Build stage'i kodunuzu derler ve artifact'leri üretir. Final stage ise sadece o artifact'leri minimal bir base imaja kopyalar. Build stage'indeki her şey atılır.
İşte aynı Node uygulamasının multi-stage ile yeniden yazılmış hali:
# Build stage
FROM node:18 AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --production
COPY . .
RUN npm run build
# Final stage
FROM node:18-alpine
WORKDIR /app
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/dist ./dist
COPY package*.json ./
CMD ["node", "dist/server.js"]
Sadece bu, imajı ~1.2 GB'dan ~180 MB'a düşürür. Püf noktası şu: final stage asla build araçlarını, tam Node SDK'yı veya kaynak dosyalarını görmez — sadece derlenmiş çıktıyı ve production node_modules'u.
Zor yoldan öğrendiğim birkaç şey:
- Her zaman
package*.jsondosyalarını ayrı kopyalayın ve kaynağı kopyalamadan önce install çalıştırın. Docker layer'ı cache'ler, böylece sadece kodunuz değiştiğinde rebuild'lernpm ciadımını atlar. - CI'da
npm installyerinenpm cikullanın. Daha hızlıdır, deterministiktir ve lockfile'a saygı duyar. - Native dependency'leriniz varsa Alpine stage'inde de build araçlarına ihtiyacınız olabilir. Builder stage'inde
apk add --no-cache python3 make g++kullanın.
Alpine Tabanlı İmajlara Geçiş
Alpine Linux, musl libc ve busybox etrafında inşa edilmiştir, bu da onu küçücük yapar — base imaj ~5 MB'dir, Debian'ın ~80 MB'ına kıyasla. Çoğu yorumlanan dil (Node, Python, Go) için Alpine final stage olarak harika çalışır.
İşte bir Python örneği:
FROM python:3.11-slim AS builder
WORKDIR /app
COPY requirements.txt .
RUN pip install --user --no-cache-dir -r requirements.txt
FROM python:3.11-alpine
WORKDIR /app
COPY --from=builder /root/.local /root/.local
COPY . .
ENV PATH=/root/.local/bin:$PATH
CMD ["python", "main.py"]
python:3.11-slim builder size C extension'larını derlemek için tam bir ortam verirken, Alpine final stage ~50–70 MB civarında kalır.
Uyarı: Alpine musl libc kullanır, glibc değil. Önderlenmiş wheel'leri olan bazı Python paketleri glibc varsayar ve hata verebilir veya yeniden derleme gerektirebilir. musl import hataları alırsanız, ya Alpine stage'inde kaynaktan derleyin ya da her iki stage için python:3.11-slim kullanın. Production'a push etmeden önce iyice test edin.
Her Layer İçinde Temizlik Yapın
Multi-stage builds kullansanız bile individual layer'ları şişirebilirsiniz. Package manager'lar varsayılan olarak indirilen dosyaları cache'ler. Her zaman aynı RUN komutunda temizlik yapın:
RUN apk add --no-cache curl && \
curl -sSL https://example.com/tool.tar.gz | tar xz && \
rm -rf /var/cache/apk/* tool.tar.gz
apk add'i bir layer'da, rm'yi başka bir layer'da çalıştırırsanız Docker her iki layer'ı da tutar. Cache dosyası final layer'da silinmiş olsa bile intermediate layer'da yaşamaya devam eder.
apt tabanlı imajlar için:
RUN apt-get update && \
apt-get install -y --no-install-recommends curl && \
rm -rf /var/lib/apt/lists/*
Sadece --no-install-recommends flag'i, önerilen ama zorunlu olmayan paketleri atlayarak 20–40 MB tasarruf sağlar.
.dockerignore Dosyası Kullanın
Bunu kaçırmak kolay. .dockerignore olmadan COPY . . komutunuz proje dizininizdeki her şeyi kopyalar — .git, node_modules, test fixture'ları, log dosyaları ve IDE config'iniz dahil.
Çoğu Node projemde kullandığım .dockerignore şöyledir:
node_modules
npm-debug.log
.git
.gitignore
.env
.env.local
tests
*.md
coverage
.vscode
.idea
Bu, build context'inizden 50–200 MB kazandırabilir; ayrıca docker build adımını da hızlandırır çünkü Docker tüm o çöpü daemon'a göndermek zorunda kalmaz.
Go Binary'leri: Nihai Boyut Küçültme
Go servisleri geliştiriyorsanız, absürt derecede küçük imajlar elde edebilirsiniz. Go statik binary'ye derlenir, yani final stage'in bir işletim sistemine bile ihtiyacı yoktur:
FROM golang:1.21 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 .
FROM scratch
COPY --from=builder /app/app /app
CMD ["/app"]
-s -w flag'leri debug sembollerini ve DWARF tablolarını soyar. scratch base imajı kelimenin tam anlamıyla boştur — final imajınız sadece binary'dir, genellikle 10–20 MB.
Not: scratch'te shell yok, /tmp yok, CA sertifikası yok. Uygulamanız HTTPS çağrısı yapıyorsa builder stage'inden /etc/ssl/certs/ca-certificates.crt dosyasını kopyalamanız gerekir. Uygulamanız geçici dosya yazıyorsa /tmp'yi manuel oluşturun.
İmaj Boyutunu Ölçme ve Doğrulama
Tahmin etmeyin — ölçün. Her Dockerfile değişikliğinden sonra gerçek boyutu kontrol edin:
docker images --format "table {{.Repository}}\t{{.Tag}}\t{{.Size}}" | grep myapp
Her layer'ın içinde ne olduğunu daha derinlemesine görmek için dive kullanın:
dive myapp:latest
dive size layer layer hangi dosyaların eklendiğini, değiştirildiğini veya silindiğini gösterir. Yanlışlıkla 300 MB'lık bir log dosyası kopyaladığınız o layer'ı bulmanın en hızlı yolu budur.
Daha önce GitOps yazımda (https://furkanikkan.com/urun/gitops-ily-altyapi-yonetimi-manuel-deploy-lara-son-58) da belirttiğim gibi, daha küçük imajlar aynı zamanda CI/CD runner'larınızda ve production node'larınızda daha hızlı pull süreleri anlamına gelir — bu da deployment sıklığınızı ve rollback hızınızı doğrudan etkiler.
Kaçınılması Gereken Yaygın Tuzaklar
- Base imajlar için
latesttag'lerini kullanmayın. Altı ay sonraki bir rebuild farklı bir base çeker ve uygulamanızı bozabilir. Versiyonları sabitleyin:node:18.19.0-alpine3.19. - Package manager'lar için
--no-cache'yi unutmayın.apk add --no-cachevepip install --no-cache-dir, cache dosyalarının imajınıza girmesini engeller. - Root olarak çalışmayın. Boyutu etkilemez ama bir güvenlik baseline'ıdır. Final stage'inize
RUN adduser -D appuser && USER appuserekleyin. - Dev ve production dependency'lerini karıştırmayın. Test için devDependencies gerekiyorsa builder stage'inde yapın ve final imaja sadece production
node_modules'u kopyalayın.
Hızlı Checklist
Yeni bir Dockerfile optimize ederken benim rutinim şudur:
- Base imajı Alpine veya slim variant'a çevir
- Multi-stage build ekle (builder + final)
- Dependency manifest'lerini kopyala ve kaynaktan önce install et
.dockerignoredosyası ekle- Package manager cache'ini aynı
RUNlayer'ında temizle - Tüm base imaj versiyonlarını sabitle
docker imagesveyadiveile ölç
Çoğu web uygulaması için 1.2 GB'dan 120 MB'a inmek gerçekçidir. Go gibi derlenen dillerde tek haneli megabyte'lara ulaşabilirsiniz. Build başta biraz daha fazla düşünmeyi gerektirir ama her pull, her deploy ve her registry faturanız bundan faydalanır.
Kapak görseli: HD Wallpapers · CC0 (Openverse / kamu malı) · https://stocksnap.io/photo/light-abstract-V9L6XXK3LB
