18 poin oleh xguru 2024-11-17 | 6 komentar | Bagikan ke WhatsApp
  • Saat membangun image kontainer Docker, jika Dockerfile tidak menggunakan struktur Multi-Stage, kemungkinan besar file yang tidak perlu akan ikut disertakan
  • Hal ini menyebabkan ukuran image membesar dan meningkatkan kerentanan keamanan
  • Analisis penyebab utama “file yang tidak perlu” yang dapat muncul dalam image kontainer, serta penjelasan cara mengatasinya melalui Multi-Stage Build

Penyebab ukuran image membesar

  • Aplikasi memiliki dependensi build-time dan runtime.
  • Dependensi build-time lebih banyak daripada runtime dan memiliki lebih banyak kerentanan keamanan (CVE).
  • Jika image yang sama digunakan untuk build dan menjalankan aplikasi, dependensi build-time yang tidak perlu (kompiler, linter, dan lain-lain) akan ikut masuk.
  • Image build dan runtime seharusnya dipisahkan, tetapi hal ini sering terlewatkan.

Contoh struktur Dockerfile yang salah

Contoh yang salah untuk aplikasi Go

FROM golang:1.23  
WORKDIR /app  
COPY . .  
RUN go build -o binary  
CMD ["/app/binary"]  
  • Image golang:1.23 ditujukan untuk kompilasi, tetapi jika dipakai apa adanya di lingkungan produksi, seluruh compiler Go beserta dependensinya akan ikut disertakan.
  • Ukuran image: lebih dari 800MB, dengan lebih dari 800 kerentanan keamanan.

Contoh yang salah untuk aplikasi Node.js

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"]  
  • Folder node_modules akan ikut memuat dependensi pengembangan yang tidak dibutuhkan saat runtime.
  • Ini tidak bisa diperbaiki hanya dengan npm ci --omit=dev, karena dependensi pengembangan yang diperlukan dalam proses build bisa ikut terhapus.

Cara membuat image lean sebelum ada Multi-Stage Build

Pola Builder

  1. Build aplikasi di Dockerfile.build:
FROM node:lts-slim  
WORKDIR /app  
COPY . .  
RUN npm ci  
RUN npm run build  
  1. Salin artefak hasil build ke host:
docker cp $(docker create build:v1):/app/.output .  
  1. Buat image runtime di Dockerfile.run:
FROM node:lts-slim  
WORKDIR /app  
COPY .output .  
CMD ["node", "/app/.output/index.mjs"]  
•	Masalah: perlu menulis beberapa Dockerfile, mengelola urutan build, dan membutuhkan skrip tambahan.  

Memahami Multi-Stage Build

  • Multi-Stage Build adalah fitur yang mengimplementasikan pola Builder di dalam Docker.
    • Dengan beberapa perintah FROM, tahap build dan runtime dapat didefinisikan dalam satu Dockerfile.
    • Gunakan perintah COPY --from=<stage> untuk mengambil file yang dibangun dari stage sebelumnya.

Contoh Dockerfile Multi-Stage (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"]  
  • Dengan COPY --from=build, artefak hasil build dapat disalin langsung tanpa melewati host.

Contoh penerapan Multi-Stage Build

Aplikasi React

# 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;"]  
  • Setelah dibuild, aplikasi React menjadi file statis dan dapat disajikan dengan Nginx.

Aplikasi Go

# 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"]  
  • Menggunakan image distroless untuk menyediakan lingkungan runtime yang lebih minimal.

Aplikasi Java

# 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"]  
  • Gunakan JDK untuk build, lalu JRE yang lebih ringan untuk runtime.

Kesimpulan

  • Multi-Stage Build memisahkan lingkungan build dan runtime untuk mencegah ukuran image membesar akibat dependensi pengembangan yang tidak perlu
  • Dengan ini, ukuran image dapat dikurangi, keamanan ditingkatkan, dan proses build disederhanakan
  • Multi-Stage Build adalah metode standar untuk membuat image kontainer yang efisien, dan juga mendukung fitur lanjutan (misalnya kondisi percabangan, unit test saat build)

6 komentar

 
savvykang 2024-11-18

Untuk Java, jlink memang diperkenalkan sejak versi 9, tetapi kegunaannya kurang baik karena kita harus mencari dan menyebutkan modul dependensi dengan jdeps, dan sebagainya. Melihat banyak orang tidak tahu metode seperti itu atau masih mencari JRE, rasanya promosi untuk alat Java masih kurang, dan tampaknya perlu ada perbaikan agar JRE bisa dihasilkan hanya dengan satu perintah.

 
brainer 2024-11-17

Saya memang menggunakannya seperti itu, tetapi kekurangannya sepertinya waktu build jadi lama

 
kandk 2024-11-18

Waktu build seharusnya tidak berbeda. Kalau ada perbedaan, berarti konfigurasinya salah!

 
brainer 2024-11-18

Ah, begitu ya!

 
qurare 2024-11-18

Tergantung strateginya, satu stage bahkan bisa di-cache secara utuh, jadi menurut saya justru waktu build-nya jadi lebih singkat!

 
brainer 2024-11-18

Sepertinya saya perlu belajar lebih banyak tentang Docker!