Giriş

Daha tek bir container başlamadan, imajınızın boyutunun, build hızının ve saldırı yüzeyinin büyük kısmına Dockerfile karar verir. Aşağıdaki pratikler her projede uyguladıklarımız — performans ve güvenlik bir arada, çünkü söz konusu bir Dockerfile olduğunda ikisi aynı disiplinin parçasıdır. Aşağıdaki bölümlerin çoğunun geri dönüp başvurduğu, Node 22 servisi için referans niteliğinde bir multi-stage build şöyle:

# syntax=docker/dockerfile:1

FROM node:22-bookworm-slim AS build
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci
COPY . .
RUN npm run build && npm prune --omit=dev

FROM node:22-bookworm-slim
ENV NODE_ENV=production
WORKDIR /app
COPY --from=build /app/dist ./dist
COPY --from=build /app/node_modules ./node_modules
USER node
EXPOSE 3000
HEALTHCHECK --interval=30s --timeout=3s --retries=3 \
  CMD ["node", "dist/healthcheck.js"]
CMD ["node", "dist/server.js"]

Build Pratikleri

1. Minimal ve Desteklenen Bir Base İmajla Başlayın — ve Sabitleyin

Resmî imajların slim, alpine veya distroless varyantlarını kullanın ve asla ömrünü tamamlamış bir işletim sistemi sürümü üzerine build almayın (örneğin Ubuntu 20.04, Nisan 2025'te standart destekten çıktı). 22-bookworm-slim gibi bir tag mutable'dır; tekrarlanabilir build'ler için digest ile sabitleyin ve güncellemeleri Renovate ya da Dependabot önersin:

docker buildx imagetools inspect node:22-bookworm-slim
# sonra sabitleyin:  FROM node:22-bookworm-slim@sha256:<digest>
2. Multi-Stage Build Kullanın

Derleyiciler, dev bağımlılıkları ve build cache'leri, sonunda atılacak bir build aşamasına aittir. Yukarıdaki runtime aşaması yalnızca derlenmiş çıktıyı ve production bağımlılıklarını taşır — daha küçük imaj, daha hızlı pull ve CVE tarayıcısının işaretleyeceği çok daha az paket. Derlenen diller için runtime aşaması, içinde hiç shell bulunmayan bir distroless imaj olabilir.

3. Talimatları Layer Cache'e Göre Sıralayın

Docker layer'ları yukarıdan aşağıya cache'ler ve ilk değişikliğin altındaki her şeyi geçersiz kılar. Bağımlılık manifest'lerini kopyalayıp paketleri, kaynak kodu kopyalamadan önce kurun — örnekteki gibi — böylece sıradan bir kod değişikliği, her şeyi yeniden kurmak yerine cache'lenmiş npm ci layer'ını yeniden kullanır.

4. Temizliği Aynı RUN Layer'ında Yapın

Her RUN bir layer oluşturur; sonraki bir talimatta silinen dosyalar önceki layer'da taşınmaya devam eder. Kurulum ve temizliği zincirleyin, build'in gerçekten tekrarlanabilir olması için sürümleri sabitleyin:

RUN apt-get update \
 && apt-get install -y --no-install-recommends ca-certificates curl \
 && rm -rf /var/lib/apt/lists/*
5. .dockerignore Kullanın, ADD Yerine COPY'yi Tercih Edin

.git, node_modules, .env ve yerel build çıktısını dışarıda bırakan bir .dockerignore, build context'ini küçük tutar; secret'ları ve gereksiz dosyaları COPY . .'nin dışında bırakır. ADD'in arşiv açma özelliğine özellikle ihtiyacınız yoksa COPY kullanın — ADD'in URL'den içerik çekme davranışı, imajlarda sürpriz içeriğin klasik kaynağıdır.

Güvenlik Pratikleri

6. Root Olmayan Bir Kullanıcıyla Çalıştırın

Root olarak çalışan bir container, host üzerinde root olmaktan yalnızca bir kernel açığı uzaktadır. Resmî dil imajları yetkisiz bir kullanıcıyla gelir (yukarıdaki node); yoksa useradd ile bir kullanıcı oluşturun ve CMD'den önce USER ile ona geçin. Yalnızca uygulamanın ihtiyaç duyduğunu kurun — her fazladan paket, saldırı yüzeyi ve tarayıcı gürültüsü demektir.

7. Secret'ları Asla Layer'lara Gömmeyin

ARG, ENV ya da kopyalanıp sonra silinen bir dosyayla geçirilen token'lar, imaj geçmişinden geri çıkarılabilir. BuildKit secret mount'ları kimlik bilgilerini yalnızca onlara ihtiyaç duyan talimat için açar ve hiçbir layer'da iz bırakmaz:

RUN --mount=type=secret,id=npmrc,target=/root/.npmrc npm ci

# build sırasında:
docker build --secret id=npmrc,src=$HOME/.npmrc -t shop-api .
8. Her Build'de Lint ve Tarama Yapın

Dockerfile'ları hadolint ile lint'leyin, build edilen imajları CI'da Trivy veya Grype ile CVE'lere karşı tarayın ve kritik bulgularda pipeline'ı fail ettirin:

hadolint Dockerfile
trivy image --severity HIGH,CRITICAL --exit-code 1 shop-api:1.8.3

Yaygın biçimde dolaştığı için açıkça düzeltmeye değer bir nokta: Docker Bench for Security, Docker host'unu ve daemon'ını CIS benchmark'ına göre denetler — Dockerfile ya da imaj taramaz. Onu host sıkılaştırması için kullanın; imajlar için Trivy, Grype veya Docker Scout kullanın. Tarama registry tarafında da yapılmalı, çünkü yeni CVE'ler haftalar önce build ettiğiniz imajlara karşı yayınlanır — Docker imajları nerede saklanır rehberimiz registry tarafında tarama, imzalama ve saklama politikalarını ele alıyor.

9. HEALTHCHECK Ekleyin

HEALTHCHECK; Docker'ın, Compose'un ve orkestratörlerin "süreç çalışıyor" ile "servis gerçekten yanıt veriyor"u ayırt etmesini sağlar — restart politikaları ve depends_on koşulları tam olarak buna bakar. Shell içermeyen imajlarda da çalışması için, referans Dockerfile'daki gibi exec formunu kullanın.

Sonuç

Bu pratiklerin hiçbiri egzotik değil; asıl zorluk, onları onlarca repository'de tutarlı biçimde uygulamak. Bu, bir Dockerfile sorunu olduğu kadar bir pipeline ve politika sorunudur — SecOps ekibimizin uygulanır kalsın diye CI'a gömdüğü, DevOps pratiğimizin ise hızlı tuttuğu türden bir taban çizgisi. Yukarıdaki referans build ile başlayın, tarayıcı kapısını ekleyin ve oradan iterasyonla ilerleyin.

Bunu production'da mı çalıştıracaksınız? AWS Yönetilen Hizmetler ekibimiz böyle ortamları 7/24 işletir — izleme, olay müdahalesi, yama ve maliyet kontrolü; ismini bildiğiniz kıdemli mühendislerle, aylık 3.000 €'dan başlayan fiyatlarla.