Perkecil Docker Image: Multi-stage Build dari 2GB ke 80MB
Build pertama berjalan mulus, image sukses di-push, lalu pipeline deploy gagal: no space left on device. Registry mulai komplain, pull lambat, dan CI yang dulu 2 menit jadi 8 menit. Penyebab paling umum bukan kode aplikasi — melainkan image yang membawa jauh lebih banyak dari yang seharusnya: compiler toolchain, dependency build-time, file sampah, dan base image raksasa yang tidak pernah dirapikan.
Panduan ini membedah cara mengecilkan image Docker dari skala GB ke skala puluhan MB memakai multi-stage build, tanpa mengorbankan keamanan.
1. Ukur Dulu: Apa yang Memakan Tempat
docker images # lihat ukuran image
docker history myapp:latest --no-trunc # lihat kontribusi tiap layer
dive myapp:latest # inspeksi per layer (tool interaktif)
Temuan paling sering:
- Build toolchain (gcc, make, JDK) ikut terbawa karena hanya ada satu
RUNstage apt-get installtanpa--no-install-recommendsdan tanpa membersihkan/var/lib/apt/lists- Dependency dev (
node_modulespenuh source test) di-copy ke runtime - .env, .git, dan cache build ikut masuk karena
.dockerignoretidak ada
2. Multi-stage Build: Prinsipnya
Satu Dockerfile, dua (atau lebih) stage. Stage pertama dipakai untuk build — boleh gemuk. Stage terakhir adalah runtime — hanya berisi hasil jadi. Builder stage tidak pernah masuk ke image final.
Contoh aplikasi Node.js:
# ---- Stage 1: build ----
FROM node:20 AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build # hasil: dist/
# ---- Stage 2: runtime ----
FROM node:20-slim
ENV NODE_ENV=production
WORKDIR /app
COPY package*.json ./
RUN npm ci --omit=dev # hanya production deps
COPY --from=builder /app/dist ./dist
USER node
CMD ["node", "dist/server.js"]
Hasil ukuran (kasus nyata, Express + TypeScript):
| Pendekatan | Ukuran |
|---|---|
| node:20 penuh, 1 stage | ±1.4 GB |
| node:20-slim, 1 stage (deps + src) | ±480 MB |
| Multi-stage + npm ci –omit=dev | ±120 MB |
| Distroless / alpine runtime final | ±80 MB |
3. Pilih Base Image yang Tepat
| Base | Ukuran | Cocok untuk |
|---|---|---|
| ubuntu:24.04 / debian:12 | ~70–120 MB | Butuh banyak binary sistem |
| alpine:3.20 | ~7 MB (di atas scratch) | Budget ketat; berhati-hatilah: musl libc kadang beda perilaku |
| *-slim (node:20-slim, python:3.12-slim) | ~40–80 MB | Rasio aman/kecil — pilihan default |
| gcr.io/distroless/*-debian12 | terkecil, tanpa shell | Runtime final saat build di stage lain; attack surface paling kecil |
Catatan: distroless tidak punya shell — debugging pakai docker exec tidak bisa. Untuk produksi yang sudah stabil, ini justru kelebihan.
4. Contoh Python (FastAPI): dari 1.1GB ke 95MB
FROM python:3.12 AS builder
WORKDIR /app
RUN python -m venv /opt/venv
ENV PATH="/opt/venv/bin:$PATH"
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
FROM python:3.12-slim
COPY --from=builder /opt/venv /opt/venv
ENV PATH="/opt/venv/bin:$PATH"
WORKDIR /app
COPY . .
EXPOSE 8000
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
Trik serupa untuk Go: build statik RUN CGO_ENABLED=0 go build -o /app, lalu stage final cuma FROM scratch + binary — image bisa di bawah 10 MB.
5. Aturan Penghematan yang Selalu Berlaku
- Layer order — copy file yang jarang berubah dulu (requirements.txt, package.json), source code belakangan. Build cache tidak bakal invalidasi install dependency tiap kali commit.
- Bersihkan dalam satu RUN —
RUN apt-get update && apt-get install -y --no-install-recommends gcc && apt-get purge -y gcc && rm -rf /var/lib/apt/lists/*. Terpisah-pisah = tiap layer tetap membawa sisa. - .dockerignore wajib —
.git,node_modules,__pycache__,.env,dist/, berkas lokal. - Jangan COPY “.” yang kotor — salin spesifik: hanya folder
src/+ artefak build. - Audit security — setelah optimasi jalankan
trivy image myapp:latest; image kecil biasanya juga lebih sedikit CVE-nya.
6. Verifikasi Hasil
docker build -t myapp:slim .
dive myapp:slim # periksa layer terbuang
docker run --rm myapp:slim # tes jalan normal
trivy image myapp:slim # scan kerentanan
docker push registry/myapp:slim
Bandingkan docker images sebelum/sesudah, lalu pantau waktu pull di environment staging — biasanya turun drastis.
Kesimpulan
Multi-stage build adalah perbaikan dengan rasio usaha-hasil terbaik di ekosistem Docker: satu file Dockerfile, ukuran image turun 5–10x, waktu deploy membaik, dan attack surface berkurang. Ukur dulu dengan dive, pahami layer mana yang gemuk, lalu pisahkan fase build dari runtime. Mulai dari base image -slim, tambahkan distroless kalau butuh lebih ringan lagi.
Eksplorasi konten lain dari Oentoro
Berlangganan untuk dapatkan pos terbaru lewat email.