Docker image küçültme, hem dağıtım hızını hem de güvenliğini doğrudan etkileyen bir konudur. Birkaç gigabaytlık bir imaj; build sürelerini uzatır, registry maliyetini artırır, deploy'u yavaşlatır ve içinde gereksiz paketler barındırdığı için saldırı yüzeyini büyütür. İyi haber şu: çoğu projede birkaç temel teknikle imaj boyutunu yüzde 70-80 oranında düşürmek mümkün. Bu rehberde multi-stage build, alpine tabanlı imajlar, .dockerignore ve katman optimizasyonunu pratik örneklerle ele alıyorum.
Neden imaj boyutu önemli?
Şişkin bir imajın bedelini her aşamada ödersiniz. Büyük imajlar registry'e daha uzun sürede push edilir, CI/CD pipeline'ında daha uzun beklersiniz ve production sunucuya çekilirken (pull) trafiği şişirir. Otomatik ölçeklenen ortamlarda her yeni pod veya konteyner imajı baştan indirir; 50 MB ile 1.2 GB arasındaki fark, yüzlerce konteynerde dakikalara dönüşür.
- Hız: Daha küçük imaj = daha hızlı pull, push ve cold start.
- Güvenlik: Daha az paket = daha az CVE, daha küçük saldırı yüzeyi.
- Maliyet: Registry depolama ve ağ trafiği doğrudan boyutla orantılıdır.
Doğru temel imajı seçmek
En kolay kazanç, base image seçiminden gelir. Resmi dil imajlarının çoğu, tam bir Debian üzerine kuruludur ve derleme araçları, dökümantasyon ve dilin tüm eklentilerini içerir. Çoğu durumda ihtiyacınız olan tek şey çalışma zamanıdır.
- alpine: musl libc tabanlı, ~5 MB'lık çok küçük bir dağıtım.
node:20-alpineveyapython:3.12-alpinegibi varyantlar tam sürümün onda biri kadardır. - slim:
python:3.12-slimgibi varyantlar Debian tabanlıdır ama gereksiz paketler çıkarılmıştır. Alpine'in musl uyumsuzluk riskini istemiyorsanız iyi bir orta yoldur. - distroless: Google'ın
gcr.io/distrolessimajları sadece uygulamanızı ve çalışma zamanını içerir; shell bile yoktur. Production için en güvenli, en küçük seçeneklerden biridir.
Bir uyarı: alpine, musl libc kullandığı için glibc'ye bağımlı native eklentilerde (özellikle Python'da numpy, pandas gibi C uzantıları olan paketlerde) derleme sorunları çıkarabilir. Böyle durumlarda slim daha pürüzsüz olur.
Multi-stage build ile derleme ve çalışma ortamını ayırın
İmaj küçültmenin en güçlü tekniği multi-stage build'dir. Fikir basit: uygulamayı bir "builder" aşamasında tüm derleme araçlarıyla derlersiniz, sonra yalnızca üretilen çıktıyı temiz ve küçük bir final aşamasına kopyalarsınız. Derleyiciler, dev bağımlılıkları ve ara dosyalar son imaja hiç girmez.
Bir Node.js uygulaması için tipik örnek:
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
FROM node:20-alpine
WORKDIR /app
ENV NODE_ENV=production
COPY package*.json ./
RUN npm ci --omit=dev
COPY --from=builder /app/dist ./dist
CMD ["node", "dist/index.js"]
Burada npm ci --omit=dev ile son imajda yalnızca production bağımlılıkları kalır; COPY --from=builder ise yalnızca derlenmiş dist klasörünü taşır. Derlemeye özgü her şey ilk aşamada kalır ve atılır.
Derlenmiş diller için kazanç daha da büyüktür. Go örneği, statik binary üretip sıfırdan (scratch) bir imaja koymanın gücünü gösterir:
FROM golang:1.22 AS build
WORKDIR /src
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 go build -o /app/server ./cmd/server
FROM gcr.io/distroless/static-debian12
COPY --from=build /app/server /server
ENTRYPOINT ["/server"]
Bu yaklaşımla yüzlerce megabaytlık bir Go derleme ortamı, birkaç megabaytlık tek binary'ye iner.
Katman sayısını ve içeriğini optimize edin
Her RUN, COPY ve ADD komutu yeni bir katman oluşturur ve bir katmana eklenen veri, sonraki bir katmanda silinse bile imaj geçmişinde kalır. Bu yüzden temizliği aynı RUN içinde yapmak gerekir:
RUN apt-get update \
&& apt-get install -y --no-install-recommends curl ca-certificates \
&& rm -rf /var/lib/apt/lists/*
Burada --no-install-recommends önerilen ama gereksiz paketleri atlar, rm -rf /var/lib/apt/lists/* ise apt önbelleğini aynı katmanda siler. Bunları ayrı satırlara bölerseniz cache imajda kalmaya devam eder.
Bir diğer kritik nokta katman önbelleği için komut sırasıdır: az değişen şeyleri (bağımlılık manifestoları) önce, sık değişen şeyleri (kaynak kod) sonra kopyalayın. COPY package*.json ./ satırını COPY . . öncesine koymak, kod değiştiğinde npm ci'nin yeniden çalışmamasını sağlar.
.dockerignore ile gereksizleri dışarıda bırakın
Build context'e gönderilen her dosya imaja sızabilir ve build'i yavaşlatır. .dockerignore dosyası, .gitignore mantığıyla çalışır ve genellikle en hızlı kazançlardan birini sağlar:
.git
node_modules
dist
*.log
.env
.env.*
coverage
Dockerfile
docker-compose.yml
README.md
Özellikle node_modules ve .git klasörlerini dışlamak hem build context'i hem de yanlışlıkla kopyalanan dosyalardan kaynaklı şişmeyi engeller. .env dosyalarını da dışlamak güvenlik açısından şarttır.
Boyutu ölçün ve analiz edin
Optimize ederken neyin işe yaradığını görmek için ölçüm yapın. docker images komutu hızlı bir karşılaştırma verir:
docker images myapp --format "{{.Repository}}:{{.Tag}} {{.Size}}"
Daha derin analiz için dive aracı paha biçilmezdir; her katmanın boyutunu ve hangi dosyaların eklendiğini gösterir, böylece "verimliliği" düşüren katmanları yakalarsınız. Ayrıca BuildKit'i (DOCKER_BUILDKIT=1 veya modern Docker'da varsayılan) açık tutmak, paralel ve daha verimli build sağlar. Build cache'i daha akıllı kullanmak için RUN --mount=type=cache ile bağımlılık önbelleğini kalıcı tutabilirsiniz.
Sık Sorulan Sorular
Alpine her zaman en iyi seçim mi?
Hayır. Alpine çok küçüktür ama musl libc kullandığı için bazı native paketlerde derleme sorunları ve nadiren DNS davranış farkları yaşanabilir. Native bağımlılıkları yoğun bir Python/Node projeniz varsa slim varyantı genellikle daha sorunsuzdur ve yine de tam imajdan çok daha küçüktür.
Multi-stage build CI/CD'yi yavaşlatır mı?
Pratikte hayır. Builder aşaması cache'lenir ve yalnızca bağımlılıklar ya da kaynak değiştiğinde yeniden çalışır. Üretilen küçük imaj sayesinde push ve pull süreleri kısalır; toplamda pipeline genellikle hızlanır.
İmajı küçülttüm ama hâlâ büyük, başka ne yapabilirim?
dive ile en büyük katmanları bulun. Çoğu zaman suçlu; silinmeyen apt/npm önbelleği, gereksiz dev bağımlılıkları, build context'e sızan node_modules veya kopyalanan büyük statik dosyalardır. Distroless veya scratch tabanlı final aşamaya geçmek de ek bir kazanç sağlar.
İmajlarınız hâlâ gigabaytlarca yer mi kaplıyor? Dockerfile'ınızı multi-stage'e taşıyıp CI/CD pipeline'ınızı hızlandırmanız için yardıma ihtiyacınız varsa benimle iletişime geçin.