Giriş
Üretim ortamında konteyner imaj boyutu, güvenlik yüzeyi ve başlatma hızı kritik öneme sahiptir. Geleneksel Node.js tabanlı imajlar (ör. Debian/Alpine) paket yöneticileri, shell ve gereksiz araçlar içerdiğinden hem büyük olur hem de CVE yüzeyi genişler. Bu yazıda, Docker Multi-Stage Build ve Distroless yaklaşımını birleştirerek, Node.js 20 tabanlı bir API’yi onlarca megabayta düşürüp, saldırı yüzeyini minimize etmeyi gösteriyorum.
Neden Distroless?
Distroless imajlar, uygulamanızı çalıştırmak için gerekli en minimal dosyaları içerir; shell, paket yöneticisi veya gereksiz araçlar yoktur. Bu sayede: (1) İmaj boyutu küçülür, (2) Potansiyel güvenlik açıkları azalır, (3) Soğuk başlatma hızlanır, (4) Tedarik zinciri şeffaflığı artar. Dezavantajı ise hata ayıklamanın klasik yöntemlerle (bash, curl) yapılamamasıdır; ancak doğru loglama ve metrikler bu ihtiyacı büyük ölçüde ortadan kaldırır.
Ön Koşullar
- Docker 24+ (BuildKit aktif önerilir)
- Node.js 20 LTS ile uyumlu bir proje (ör. Express, Fastify, NestJS)
- Üretim derlemesi yapan bir betik: npm run build (ör. TypeScript için tsc)
Multi-Stage Dockerfile Mantığı
Amaç, bağımlılıkları ve derlemeyi ağır bir builder evresinde yapmak, ardından sadece çalışması gereken artefaktları Distroless evresine kopyalamaktır. Aşağıda örnek bir Dockerfile yapısı yer alır:
# 1) Bağımlılıklar
FROM node:20-bookworm-slim AS deps
WORKDIR /app
COPY package*.json ./
RUN npm ci
# 2) Derleme
FROM deps AS build
COPY . .
RUN npm run build && npm prune --production
# 3) Distroless (final)
FROM gcr.io/distroless/nodejs20-debian12 AS runner
WORKDIR /app
ENV NODE_ENV=production
# Distroless'ta yerleşik 'nonroot' kullanıcısı mevcut
USER nonroot
COPY --from=build /app/dist ./dist
COPY --from=build /app/package*.json ./
COPY --from=build /app/node_modules ./node_modules
EXPOSE 3000
# Distroless nodejs imajı 'node' ENTRYPOINT'i ile gelir
CMD ["dist/index.js"]
Adım Adım Kurulum
1) package.json’ı hazırlayın: Üretim derlemesi için "build" komutunuz olsun (ör. "build": "tsc"). Sunucuyu relative path ile başlatabileceğinizden emin olun (ör. "start": "node dist/index.js").
2) Dockerfile’ı ekleyin: Yukarıdaki örneği kök dizine Dockerfile adıyla koyun. TypeScript kullanıyorsanız dist klasörünün üretildiğinden emin olun.
3) İmajı derleyin: docker build -t api:distroless . komutuyla derleyin. Build cache’i hızlandırmak için package*.json dosyalarını erken kopyaladığımıza dikkat edin.
4) Konteyneri çalıştırın: docker run -p 3000:3000 api:distroless komutuyla ayağa kaldırın. Uygulamanız http://localhost:3000 üzerinden yanıt vermelidir.
Güvenlik ve İyileştirme İpuçları
Non-root çalıştırma: Distroless imajlarda USER nonroot kritik bir adımdır. Bu, olası bir ihlalde yetkileri sınırlar.
ENV ve gizli bilgiler: Üretimde sırları ENV ile bake etmeyin. Orkestratör (Kubernetes Secrets, ECS, Swarm) üzerinden enjekte edin. Yerel testte --env-file kullanabilirsiniz.
SBOM ve tarama: Tedarik zinciri görünürlüğü için Syft ile SBOM çıkarın (syft packages docker:api:distroless) ve Grype/Trivy ile tarayın. Distroless, raporlanan CVE sayısını anlamlı biçimde azaltır.
Boyut ve performans: Tipik bir Node imajı 300–900 MB olabilirken distroless ile 70–150 MB bandı sık görülür. Daha küçük imaj, daha hızlı pull ve daha hızlı ölçekleme demektir.
Healthcheck: Distroless’ta shell olmadığından curl yoktur. Sağlık kontrolünü uygulama içi /health endpoint’i ile orkestratörden yapın. Alternatif olarak Node ile bir komut yazabilirsiniz (ör. HEALTHCHECK CMD ["node","-e","require('http').request({host:'127.0.0.1',port:3000,path:'/health'},r=>process.exit(r.statusCode===200?0:1)).on('error',()=>process.exit(1)).end()"]).
Sık Yapılan Hatalar
COPY sırası: COPY . . satırını npm ci’dan önce yazmak, her değişimde cache’i bozar ve build’i yavaşlatır. Önce package*.json, sonra bağımlılıklar, en sonda kaynak kod.
Gereksiz dev bağımlılıkları: Üretim imajına devDependencies taşımayın. npm prune --production veya npm ci --omit=dev kullanın.
Shell beklentisi: Distroless’ta /bin/sh yoktur. Debug için logs, structured logging (JSON) ve APM/Tracing (OpenTelemetry) tercih edin. İhtiyaç halinde aynı artefaktlarla ikinci bir “debug” imajı üretebilirsiniz (ör. Alpine tabanlı).
Güncel Pratikler
- Node 20 LTS kullanın ve düzenli güvenlik güncellemelerini takip edin.
- İmajınızı cosign ile imzalayıp politikalar aracılığıyla doğrulayın.
- Read-only root filesystem ve drop capabilities (Kubernetes) gibi kısıtlamalarla koşun.
- ROOTLESS Docker ya da Pod Security Standartlarıyla ek sertleştirme sağlayın.
Sonuç
Distroless ve Multi-Stage Build, Node.js servislerinizi hem hafif hem de güvenli hale getirmenin pratik bir yoludur. Daha küçük imajlar, daha hızlı dağıtım ve daha düşük saldırı yüzeyi anlamına gelir. Üretime geçerken non-root kullanıcı, SBOM, tarama ve gözlemlenebilirlik (logs/metrics/traces) gibi temel prensipleri uyguladığınızda, bakım maliyeti düşer ve ölçeklenebilirlik artar. Mevcut Dockerfile’ınızı yukarıdaki adımlarla iyileştirerek ilk günden somut kazanım elde edebilirsiniz.
Hiç yorum yok:
Yorum Gönder