Image Temelleri — Base Image Seçimi ve Tag Stratejileri
Bu bölümün son dersine geldik. Şimdiye kadar image'ın ne olduğunu, nasıl yönetileceğini ve registry'leri öğrendik. Şimdi image oluşturmanın temellerini — base image seçimini, tag stratejilerini ve boyut optimizasyonunu — konuşacağız. Bu konular, professional Docker kullanımının yapı taşları.
Bir ev inşa ediyorsun diyelim. Temelden mi başlarsın, yoksa yarı mamul bir yapı üzerine mi eklersin? Eğer sadece bir oda ekliyorsan, sıfırdan temel atmak saçmalık — mevcut yapının üstüne çıkarsın. Docker image'larında da aynı mantık geçerli. Base image seçimi evin temeli gibidir — üstüne ne inşa edersen et, temel sağlam olmalı.
Base Image Nedir?
Her Docker image bir "base image" üzerine inşa edilir. Dockerfile'daki FROM satırı bu temeli belirler. Image'lar bir hiyerarşi oluşturur:
scratch (tamamen boş — 0 byte)
└── alpine:3.19 (7MB — minimal Linux)
└── node:20-alpine (130MB — Alpine + Node.js)
└── myapp:v1 (150MB — Node.js + uygulama kodu)scratch tamamen boş bir image — içinde hiçbir şey yok. Alpine onun üstüne minimal bir Linux kuruyor. Node.js Alpine'ın üstüne kurulmuş. Ve senin uygulamanın Node.js'in üstüne. Her katman bir öncekinin üzerine ekleniyor.
Varyantları Tanıyalım — Full, Slim, Alpine
Çoğu resmi image birden fazla varyantla sunuluyor. Her birinin farklı kullanım amacı var ve doğru olanı seçmek önemli.
Full (Default) — Geliştirme İçin
docker pull python:3.12 # ~1.02GBİçinde Debian Bookworm'un tam versiyonu var: gcc, make, wget, curl, git, build-essential ve daha birçok araç. Bir şeyi build etmen gerektiğinde (C extension'lar, native modüller) bu image işini görür. Geliştirme ortamında debug yaparken de tüm araçlar elinin altında olur.
Ama boyutu büyük — 1GB'ın üzerinde. Production'da bu kadar şeye ihtiyacın yok.
Slim — Production İçin En İyi Tercih
docker pull python:3.12-slim # ~150MBDebian'ın minimal versiyonu. Build tools yok ama Python'un (veya Node.js'in, Java'nın) çalışması için gereken minimum her şey var. Boyut full'ün altıda biri.
Çoğu production senaryosunda slim en iyi tercih. Yeterince küçük, yeterince uyumlu. C extension'lar zaten pip'ten pre-built olarak geliyorsa, slim yeterli.
Alpine — Minimum Boyut
docker pull python:3.12-alpine # ~51MBAlpine Linux tabanlı — sadece 7MB'lık bir Linux dağıtımı üzerine kurulu. Boyut açısından harika ama bir uyarı var: Alpine, standart glibc yerine musl libc kullanıyor. Bu bazı C kütüphaneleriyle uyumluluk sorunlarına yol açabiliyor.
# Bu çalışmayabilir!
docker run --rm python:3.12-alpine pip install pandas
# ERROR: Could not build wheels for numpynumpy gibi C extension'ları Alpine'da sorun çıkarabiliyor. Çözüm: ya build bağımlılıklarını kur (ama o zaman image büyür ve build süresi uzar) ya da slim kullan.
Tavsiyem: Alpine'ı körü körüne tercih etme. Uygulamanı test et — çalışıyorsa harika, sorun çıkarıyorsa slim'e geç.
Distroless — Maximum Güvenlik
Google'ın geliştirdiği distroless image'lar, shell bile olmayan ultra-minimal image'lar:
FROM gcr.io/distroless/nodejs20-debian12İçinde shell yok, package manager yok. docker exec bash yapamazsın — çünkü bash yok. Bu da saldırı yüzeyini minimuma indirir. Ama debug çok zor.
Scratch — Tamamen Boş
FROM scratch
COPY mybinary /mybinary
CMD ["/mybinary"]Hiçbir şey yok — 0 byte. Sadece statik derlenmiş binary'ler için kullanılır (Go, Rust). Son image sadece binary kadar yer kaplar (~5-20MB).
Karar Matrisi
Hangi varyantı ne zaman kullanmalısın? Şöyle düşün:
Geliştirme ve debug için full image kullan. Tüm araçlar hazır. Production'da çoğu uygulama için slim kullan. Yeterli ve güvenilir. Boyut kritik, basit uygulamalar için Alpine kullan. Ama uyumluluk testi yap. Maximum güvenlik için distroless kullan. Debug'dan vazgeç, güvenliği seç. Statik binary'ler (Go, Rust) için scratch kullan. Minimum boyut.
Dil/Framework Bazlı Rehber
Node.js
# node:20 1.1GB — Geliştirme
# node:20-slim 220MB — Production (önerilen)
# node:20-alpine 130MB — Boyut kritikseProduction Node.js Dockerfile'ı şöyle görünür:
FROM node:20-slim
RUN apt-get update && apt-get install -y --no-install-recommends dumb-init && \
rm -rf /var/lib/apt/lists/*
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci --only=production && npm cache clean --force
COPY . .
USER node
EXPOSE 3000
ENTRYPOINT ["dumb-init", "--"]
CMD ["node", "server.js"]Python
# python:3.12 1.02GB — C extension build gerekiyorsa
# python:3.12-slim 150MB — Çoğu proje için (önerilen)
# python:3.12-alpine 51MB — Dikkatli kullanJava
# eclipse-temurin:21-jdk ~350MB — Build için
# eclipse-temurin:21-jre ~250MB — Runtime (önerilen)
# eclipse-temurin:21-jre-alpine ~150MB — Boyut kritikseJava için multi-stage build çok yaygın: JDK ile build et, JRE ile çalıştır.
Go
# golang:1.22 ~800MB — Build için
# scratch 0B — Runtime (Go static binary)Go'da en küçük image'ları elde edersin çünkü Go, statik binary üretir — runtime'a ihtiyaç duymaz:
FROM golang:1.22-alpine AS builder
WORKDIR /app
COPY . .
RUN CGO_ENABLED=0 go build -o /myapp .
FROM scratch
COPY --from=builder /myapp /myapp
CMD ["/myapp"]
# Final image: ~10MBTag Stratejileri — Versiyonlama Sanatı
Image tag'leri versiyon kontrolü sağlar. Doğru tag stratejisi, deployment süreçlerinin sağlığı için kritik.
Semantic Versioning (SemVer)
En yaygın ve önerilen strateji:
myapp:1.0.0 # MAJOR.MINOR.PATCH — en spesifik
myapp:1.0 # Minor — patch otomatik güncellenir
myapp:1 # Major — minor ve patch güncellenir
myapp:latest # Tag belirtilmezse varsayılanHer yeni release'de:
docker tag myapp:1.2.3 myapp:1.2
docker tag myapp:1.2.3 myapp:1
docker tag myapp:1.2.3 myapp:latestGit-Based Tagging
VERSION=$(git rev-parse --short HEAD)
docker build -t myapp:${VERSION} .
# myapp:abc123f — hangi commit'ten build edildiği bellilatest Hakkında Son Bir Uyarı
latest ne "en yeni" ne "en güvenli" demek. Sadece "tag belirtilmediğinde varsayılan" demek.
Production Dockerfile'ında FROM node:latest yazma — FROM node:20.11.0 yaz. Production deploy'larında myapp:latest kullanma — myapp:v1.2.3 kullan. Geliştirmede kullanabilirsin ama production'da belirli versiyon şart.
Image Boyut Optimizasyonu
Küçük image neden önemli? Daha hızlı pull/push (CI/CD hızlanır). Daha az disk ve bant genişliği. Daha hızlı scaling (yeni instance'lar çabuk kalkar). Ve daha az saldırı yüzeyi (güvenlik).
Hızlı Optimizasyon Teknikleri
# 1. Doğru base image seç
FROM node:20-slim # 220MB (1.1GB'lık full yerine)
# 2. RUN komutlarını birleştir
RUN apt-get update && \
apt-get install -y --no-install-recommends curl && \
rm -rf /var/lib/apt/lists/*
# 3. Cache temizle
RUN pip install --no-cache-dir -r requirements.txt
RUN npm ci && npm cache clean --force
# 4. .dockerignore kullan (node_modules, .git, test/ hariç tut)Image Boyutunu Analiz Et
# Katman bazlı boyut
docker history myapp:v1
# dive ile detaylı analiz
docker run --rm -it \
-v /var/run/docker.sock:/var/run/docker.sock \
wagoodman/dive myapp:v1Multi-Platform Image
Apple Silicon Mac'ler (M1/M2/M3) yaygınlaştıkça, hem AMD64 hem ARM64 destekleyen image'lar oluşturmak önemli hale geldi:
# Multi-platform builder oluştur
docker buildx create --name mybuilder --use
# Hem AMD64 hem ARM64 için build + push
docker buildx build \
--platform linux/amd64,linux/arm64 \
-t tolgahan/myapp:v1 \
--push .Kullanıcı docker pull yaptığında Docker otomatik olarak platformuna uygun olanı çeker. Tek tag, birden fazla platform.
Bu Derste Ne Öğrendik?
Base image seçimi projenin temelidir. Slim çoğu production senaryosu için en iyi tercih.
Full geliştirme, Slim production, Alpine boyut kritik, Distroless maximum güvenlik, Scratch statik binary.
Semantic versioning (v1.2.3) tag stratejisinin altın standardı.
latestyerine belirli versiyon kullan.Image boyutunu küçült: doğru base image + RUN birleştirme + cache temizleme + .dockerignore.
Multi-platform image oluştur (buildx ile) — ARM cihazlar için önemli.
Alpine'ı körü körüne tercih etme — musl libc uyumluluk sorunları yaşatabilir.
latest Tag'i — Derinlemesine
Bu konuyu bir kez daha vurgulayacağım çünkü Docker'ın en çok yanlış anlaşılan konsepti.
latest Ne Değil?
latest, "en yeni versiyon" demek değil. latest, sadece "tag belirtilmezse varsayılan" demek.
# Bu iki komut aynı şey
docker pull nginx
docker pull nginx:latest
# Bu iki komut da aynı şey
docker build -t myapp .
docker build -t myapp:latest .latest Tuzakları
Birinci tuzak: latest otomatik güncellenmez.
docker build -t myapp:v1.0 .
docker build -t myapp:v2.0 .
# myapp:latest hâlâ v1.0'ı gösteriyor!
# Açıkça tag'lemezsen güncellenmezİkinci tuzak: latest her pull'da farklı olabilir. Bugün nginx:latest 1.25.4'ü gösterebilir, yarın 1.27.0'ı. Araya breaking change girerse uygulamanız çöker.
Üçüncü tuzak: hangi versiyon çalışıyor belli olmaz.
docker ps
# IMAGE STATUS
# myapp:latest Up 5 days
# Bu hangi versiyon? v1.0? v2.0? v2.1? Belli değil!latest Kuralları
Geliştirmede kullanılabilir — hızlı test için sorun yok. Ama production'da kullanma. Dockerfile'daki FROM satırında kullanma. Ve her zaman belirli bir versiyonla birlikte tag'le:
docker tag myapp:v1.2.3 myapp:latest
docker push myapp:v1.2.3
docker push myapp:latestImage İmzalama — Güvenliği Bir Adım Öteye Taşı
Image'ının gerçekten senden geldiğini kanıtlamak istersen, imzalama kullanabilirsin.
Docker Content Trust (DCT)
# Content Trust'ı etkinleştir
export DOCKER_CONTENT_TRUST=1
# Push — otomatik imzalanır
docker push tolgahan/myapp:v1
# İlk seferde signing key'ler oluşturulur
# Pull — imza doğrulanır
docker pull tolgahan/myapp:v1
# İmza eşleşmezse pull reddedilirCosign (Modern Yaklaşım)
# Key pair oluştur
cosign generate-key-pair
# Image'ı imzala
cosign sign --key cosign.key tolgahan/myapp:v1
# İmzayı doğrula
cosign verify --key cosign.pub tolgahan/myapp:v1İmzalama, supply chain security için önemli. "Bu image gerçekten bizim CI/CD'den mi geldi, yoksa biri araya girip değiştirdi mi?" sorusunun cevabını verir.
Gerçek Dünya Senaryosu: Production Image Pipeline
Büyük bir projede image pipeline'ı şöyle görünür:
#!/bin/bash
# production-build.sh
set -euo pipefail
APP="myapp"
VERSION=$(cat VERSION)
COMMIT=$(git rev-parse --short HEAD)
REGISTRY="registry.mycompany.com"
echo "=== Building ${APP}:${VERSION} ==="
# 1. Güvenlik taraması (base image)
echo "Scanning base image..."
docker run --rm -v /var/run/docker.sock:/var/run/docker.sock \
aquasec/trivy image --severity CRITICAL node:20-slim
# 2. Build
echo "Building..."
docker build \
--build-arg VERSION=${VERSION} \
--build-arg COMMIT=${COMMIT} \
-t ${REGISTRY}/${APP}:${VERSION} \
-t ${REGISTRY}/${APP}:${VERSION}-${COMMIT} \
-t ${REGISTRY}/${APP}:latest .
# 3. Final image güvenlik taraması
echo "Scanning final image..."
docker run --rm -v /var/run/docker.sock:/var/run/docker.sock \
aquasec/trivy image --severity CRITICAL,HIGH \
${REGISTRY}/${APP}:${VERSION}
# 4. Push
echo "Pushing..."
docker push ${REGISTRY}/${APP}:${VERSION}
docker push ${REGISTRY}/${APP}:${VERSION}-${COMMIT}
docker push ${REGISTRY}/${APP}:latest
echo "=== Done: ${REGISTRY}/${APP}:${VERSION} ==="Bu pipeline'da: build öncesi base image taranıyor, build yapılıyor, final image taranıyor, sorun yoksa push ediliyor. Her adım bir güvenlik katmanı.
Image Lifecycle Yönetimi
Image'lar sonsuza kadar tutulmaz. Eski versiyonları düzenli silmek gerekir:
# Lokal temizlik — 1 haftadan eski image'ları sil
docker image prune -a --filter "until=168h"
# AWS ECR'da lifecycle policy — son 10 image'ı tut
aws ecr put-lifecycle-policy --repository-name myapp --lifecycle-policy-text '{
"rules": [{
"rulePriority": 1,
"description": "Keep last 10 images",
"selection": {"tagStatus": "any", "countType": "imageCountMoreThan", "countNumber": 10},
"action": {"type": "expire"}
}]
}'Rollback Stratejisi
Versiyon bazlı tag'lerin en büyük avantajı: kolay rollback.
# v1.2.3'te sorun çıktı → v1.2.2'ye dön
docker stop myapp-container
docker run -d --name myapp-container myapp:v1.2.2latest kullanıyorsan rollback yapamazsın — hangi versiyon olduğu belli değil. İşte bu yüzden belirli versiyon kullanmak bu kadar önemli.
Yaygın Hatalar ve Çözümleri
Alpine'da Paket Bulunamıyor
docker run -it python:3.12-alpine bash
# exec: "bash": executable file not foundAlpine'da bash yok — sh kullan. Veya: RUN apk add --no-cache bash
Image Çok Büyük — Neden?
docker history myapp:v1 --format "{{.Size}}\t{{.CreatedBy}}" | sort -rh | head -5
# 500MB COPY . /app ← node_modules dahil mi?!.dockerignore kontrol et — node_modules ve .git hariç tutulmalı.
Base Image Güncellemesi Breaking Change Getirdi
# ❌ FROM python:3 → 3.11'den 3.12'ye atlayabilir
# ✅ FROM python:3.12 → minor güncelleme alır, major almaz
# ✅✅ FROM python:3.12.1 → hiçbir otomatik güncelleme almazEn güvenli: digest ile pin'le. Ama periyodik olarak güncelle — güvenlik yamaları önemli.
Bölüm 2 tamamlandı! Image dünyasını detaylıca öğrendin — image nedir, nasıl yönetilir, registry'ler, base image seçimi, tag stratejileri. Bir sonraki bölümde Dockerfile yazmayı öğreneceğiz — kendi image'larını sıfırdan oluşturacaksın.
AI Asistan
Sorularını yanıtlamaya hazır