Python #
Di ekosistem Java kita mengenal fat JAR, di Node.js ada node_modules, sementara di Python—terutama Django—biang masalah ukuran Docker image hampir selalu adalah dependency Python + OS package.
Tidak sedikit aplikasi Django sederhana yang berakhir dengan image 500–800 MB, padahal logikanya hanya CRUD API. Artikel ini membahas secara mendetail, praktis, dan engineering-driven bagaimana membuat Docker image Django yang ramping, aman, dan production-grade, setara filosofi distroless di Java.
Realita Ukuran Docker Image Django #
Tanpa Optimasi #
| Setup | Ukuran Image |
|---|---|
| python:latest + pip install | 600–900 MB |
| python:slim (tanpa cleanup) | 350–500 MB |
Dengan Optimasi #
| Setup | Ukuran Image |
|---|---|
| python:slim + multi-stage | 180–250 MB |
| distroless python | 90–140 MB |
| bundling + distroless | 50–80 MB |
⚠️ Jika image Django kamu >300 MB, hampir pasti ada dependency atau OS package yang bocor ke runtime.
Kenapa Image Django Mudah Membengkak? #
Python Tidak Memisahkan Build vs Runtime #
Banyak package Python:
- Perlu compiler (
gcc,make) - Perlu header OS (
libpq-dev,openssl-dev)
Jika tidak dipisahkan, semua ini ikut masuk image final.
Dependency C Extension #
Contoh umum:
psycopg2Pillowcryptography
Mereka membutuhkan library native saat build.
pip Tidak Prune Secara Otomatis #
Berbeda dengan npm atau Maven, pip tidak punya konsep dev vs prod dependency bawaan.
Prinsip Utama Mengecilkan Image Python #
Semua tool build harus mati di runtime image.
Runtime Django idealnya hanya berisi:
- Python runtime
- Virtual environment
- Dependency runtime
- Source code
Pendekatan 1: Multi-stage Build (Fondasi Wajib) #
Dockerfile Dasar (Belum Optimal) #
# =====================
# Build stage
# =====================
FROM python:3.12-slim AS builder
WORKDIR /app
RUN apt-get update && apt-get install -y \
build-essential \
libpq-dev \
&& rm -rf /var/lib/apt/lists/*
COPY requirements.txt .
RUN python -m venv /venv \
&& /venv/bin/pip install --upgrade pip \
&& /venv/bin/pip install -r requirements.txt
# =====================
# Runtime stage
# =====================
FROM python:3.12-slim
WORKDIR /app
COPY --from=builder /venv /venv
COPY . .
ENV PATH="/venv/bin:$PATH"
EXPOSE 8000
CMD ["gunicorn", "config.wsgi:application"]
Ukuran: 180–250 MB (sudah jauh lebih baik).
Pendekatan 2: Pisahkan Dependency Build vs Runtime #
Gunakan Binary Wheel #
Contoh:
psycopg2-binaryuvicorn[standard]
Ini menghindari kebutuhan compiler di runtime.
Hindari *-dev Package di Runtime
#
Runtime tidak perlu:
gccmake- header library
Pendekatan 3: Distroless untuk Python #
Dockerfile Django + Distroless #
FROM python:3.12-slim AS builder
WORKDIR /app
RUN apt-get update && apt-get install -y \
build-essential \
&& rm -rf /var/lib/apt/lists/*
COPY requirements.txt .
RUN python -m venv /venv \
&& /venv/bin/pip install --no-cache-dir -r requirements.txt
FROM gcr.io/distroless/python3-debian12
WORKDIR /app
COPY --from=builder /venv /venv
COPY . .
ENV PATH="/venv/bin:$PATH"
EXPOSE 8000
CMD ["/venv/bin/gunicorn", "config.wsgi:application"]
Ukuran Realistis #
| Tipe App | Ukuran |
|---|---|
| Django API sederhana | 90–110 MB |
| Django + ORM + auth | 110–140 MB |
Pendekatan 4: Dependency Audit (Sering Diabaikan) #
Banyak proyek Django membawa:
django-debug-toolbar- testing libs (
pytest,factory-boy) - migration helper
Pisahkan:
requirements.txt(prod)requirements-dev.txt
Pendekatan 5: Bundling Python (Advanced) #
Gunakan:
- PyInstaller
- Nuitka
Contoh: PyInstaller #
pyinstaller manage.py --onefile
Runtime:
FROM gcr.io/distroless/base-debian12
WORKDIR /app
COPY manage .
CMD ["./manage"]
Ukuran Akhir #
👉 50–80 MB
Namun:
- build lebih kompleks
- debugging lebih sulit
Perbandingan Pendekatan #
| Pendekatan | Virtualenv | Distroless | Ukuran | Kompleksitas |
|---|---|---|---|---|
| Slim only | Ya | Tidak | Besar | Rendah |
| Multi-stage | Ya | Tidak | Sedang | Menengah |
| Distroless | Ya | Ya | Kecil | Menengah |
| Bundling | Tidak | Ya | Sangat kecil | Tinggi |
Trade-off yang Wajib Dipahami #
Debugging #
- Distroless: tidak ada shell
- Bundling: stack trace minimal
Native Dependency #
- Lebih sensitif dibanding Node/Java
Observability #
- Logging harus STDOUT
- Error harus eksplisit
Kapan Pendekatan Mana Paling Tepat? #
| Kondisi | Rekomendasi |
|---|---|
| Django monolith | slim + multi-stage |
| API production | distroless |
| Serverless | bundling |
| Banyak native lib | slim + audit |
Penutup #
Python bukan ekosistem yang “container-friendly” secara default. Image besar bukan karena Django buruk, tapi karena:
- build dan runtime sering dicampur
- dependency native tidak diaudit
- base image dipilih sembarangan
Dengan disiplin yang sama seperti Java distroless, Django bisa berjalan di image <150 MB, bahkan <100 MB.
Masalahnya bukan di Python—tapi di kebiasaan engineering yang terlalu memanjakan runtime.
“Container kecil bukan hasil trik Docker, tapi hasil kejelasan batas antara build dan runtime.”