Docker Multi-Stage Build ile konteyner imaj boyutunu küçültmek
(labs.iximiuz.com)- Docker konteyner imajları derlenirken Dockerfile Multi-Stage yapıda değilse, gereksiz dosyaların dahil edilme olasılığı yüksektir
- Bu durum imaj boyutunun büyümesine ve güvenlik açıklarının artmasına yol açar
- Konteyner imajlarında ortaya çıkabilen “gereksiz dosyaların” başlıca nedenleri analiz ediliyor ve bunların Multi-Stage Build ile nasıl çözülebileceği açıklanıyor
İmaj boyutunun büyüme nedenleri
- Uygulamalar, build zamanı ve çalışma zamanı bağımlılıklarına sahiptir.
- Build zamanı bağımlılıkları, çalışma zamanı bağımlılıklarından daha fazladır ve daha çok güvenlik açığı (CVE) içerir.
- Aynı imaj build ve çalıştırma için kullanıldığında, gereksiz build zamanı bağımlılıkları (compiler, linter vb.) da dahil edilir.
- Build ve runtime imajları ayrılmalıdır, ancak bu çoğu zaman gözden kaçar.
Hatalı Dockerfile yapısı örnekleri
Go uygulamaları için hatalı örnek
FROM golang:1.23
WORKDIR /app
COPY . .
RUN go build -o binary
CMD ["/app/binary"]
golang:1.23imajı derleme içindir, ancak bunu olduğu gibi production ortamında kullanırsanız tüm Go compiler'ı ve bağımlılıkları da dahil olur.- İmaj boyutu: 800MB üzeri, 800'den fazla güvenlik açığı mevcut.
Node.js uygulamaları için hatalı örnek
FROM node:lts-slim
WORKDIR /app
COPY . .
RUN npm ci
RUN npm run build
ENV NODE_ENV=production
EXPOSE 3000
CMD ["node", "/app/.output/index.mjs"]
node_modulesklasörü, çalışma zamanında gerekmeyen geliştirme bağımlılıklarını da içerebilir.- Bu durum
npm ci --omit=devile düzeltilemez; çünkü build sürecinde gerekli geliştirme bağımlılıkları kaldırılabilir.
Multi-Stage Build öncesinde lean imaj üretme yöntemleri
Builder pattern
- Uygulama
Dockerfile.buildiçinde derlenir:
FROM node:lts-slim
WORKDIR /app
COPY . .
RUN npm ci
RUN npm run build
- Derlenen artifact'ler host'a kopyalanır:
docker cp $(docker create build:v1):/app/.output .
Dockerfile.runiçinde runtime imajı oluşturulur:
FROM node:lts-slim
WORKDIR /app
COPY .output .
CMD ["node", "/app/.output/index.mjs"]
• Sorunlar: Birden fazla Dockerfile yazılması, build sırasının yönetilmesi ve ek script gereksinimi.
Multi-Stage Build'i anlamak
- Multi-Stage Build, Docker içinde Builder pattern'i uygulayan bir özelliktir.
- Birden fazla
FROMkomutu kullanarak tek bir Dockerfile içinde build ve runtime aşamaları tanımlanabilir. COPY --from=<stage>komutuyla önceki aşamada derlenen dosyalar alınabilir.
- Birden fazla
Multi-Stage Dockerfile örneği (Node.js)
# Build stage
FROM node:lts-slim AS build
WORKDIR /app
COPY . .
RUN npm ci
RUN npm run build
# Runtime stage
FROM node:lts-slim AS runtime
WORKDIR /app
COPY --from=build /app/.output .
ENV NODE_ENV=production
CMD ["node", "/app/.output/index.mjs"]
COPY --from=buildile derlenen artifact'ler doğrudan kopyalanır; böylece dosyalar host üzerinden geçmeden taşınabilir.
Multi-Stage Build pratik örnekleri
React uygulaması
# Build stage
FROM node:lts-slim AS build
WORKDIR /app
COPY . .
RUN npm ci
RUN npm run build
# Runtime stage
FROM nginx:alpine
COPY --from=build /app/build /usr/share/nginx/html
ENTRYPOINT ["nginx", "-g", "daemon off;"]
- React uygulamaları build sonrasında statik dosyalara dönüşür ve Nginx ile sunulabilir.
Go uygulaması
# Build stage
FROM golang:1.23 AS build
WORKDIR /app
COPY . .
RUN go build -o binary
# Runtime stage
FROM gcr.io/distroless/static-debian12:nonroot
COPY --from=build /app/binary /app/binary
ENTRYPOINT ["/app/binary"]
distrolessimajı kullanılarak en aza indirilmiş bir runtime ortamı sağlanır.
Java uygulaması
# Build stage
FROM eclipse-temurin:21-jdk-jammy AS build
WORKDIR /build
COPY . .
RUN ./mvnw package -DskipTests
# Runtime stage
FROM eclipse-temurin:21-jre-jammy
COPY --from=build /build/target/app.jar /app.jar
CMD ["java", "-jar", "/app.jar"]
- Build için JDK, runtime için ise daha hafif JRE kullanılır.
Sonuç
- Multi-Stage Build, build ve runtime ortamlarını ayırarak gereksiz geliştirme bağımlılıklarının imaj boyutunu büyütmesini önler
- Böylece imaj boyutu küçültülebilir, güvenlik güçlendirilebilir ve build süreci sadeleştirilebilir
- Multi-Stage Build, verimli konteyner imajları oluşturmanın standart yöntemidir ve gelişmiş özellikleri de destekler (ör. dallanma koşulları, build sırasında unit test)
6 yorum
Java tarafında
jlink9. sürümden beri kullanıma sunulmuş olsa da, bağımlı modüllerijdepsile bulup tek tek belirtmek gerekmesi gibi nedenlerle kullanılabilirliği pek iyi değil. İnsanların böyle yöntemleri bilmemesi ya da JRE araması, Java araçlarının yeterince tanıtılmadığını düşündürüyor; ayrıca tek bir komutla JRE üretecek şekilde iyileştirilmesi gerekiyor gibi görünüyor.Ben de o şekilde kullanıyorum ama build süresinin uzun sürmesi dezavantaj gibi görünüyor.
Build süresinde bir fark olmamalı. Fark varsa yanlış yapılandırılmıştır!
Ah, anladım!
Stratejiye bağlı olarak bazen bir stage’i tamamen cache’leyebildiğiniz için, benim durumumda build süresi aksine kısaldı!
Görünüşe göre Docker hakkında biraz daha fazla şey öğrenmem gerekecek!