Kalimat “di komputer saya jalan” adalah pengakuan kegagalan deployment. Aplikasi Python bergantung pada versi interpreter, paket sistem, dan puluhan dependencies — kombinasi yang mudah berbeda antara laptop dan server. Docker menyelesaikannya dengan mengemas semuanya menjadi satu image yang identik di mana pun dijalankan. Pada chapter ini kita menyusun Dockerfile untuk aplikasi Python yang layak produksi, dan memahami setiap keputusan di dalamnya.
Konsep Inti: Image, Container, dan Layer #
Tiga istilah ini adalah fondasi Docker:
| Istilah | Analogi | Peran |
|---|---|---|
| Image | cetakan atau resep | berkas read-only berisi kode, interpreter, dan dependencies |
| Container | proses hasil cetak | instance berjalan dari sebuah image |
| Layer | lapisan kue | setiap instruksi Dockerfile menghasilkan lapisan yang di-cache |
Sistem layer inilah yang membuat Docker ramah bagi Python: instruksi yang tidak berubah tidak dijalankan ulang. Menyalin pyproject.toml lebih dulu, memasang dependencies, dan baru menyalin kode membuat dependency install terjadi sekali — bukan pada setiap perubahan baris kode.
Dockerfile untuk Aplikasi Python #
Berikut Dockerfile untuk project toko-api dengan src layout dari awal section ini:
FROM python:3.12-slim
WORKDIR /app
# Layer 1: dependensi — jarang berubah
COPY pyproject.toml README.md ./
RUN pip install --no-cache-dir "fastapi>=0.110,<1" "uvicorn[standard]>=0.29,<1"
# Layer 2: kode aplikasi — berubah di setiap commit
COPY src/ ./src/
RUN pip install --no-cache-dir .
# User non-root
RUN useradd --create-home appuser
USER appuser
EXPOSE 8000
CMD ["uvicorn", "toko_api.main:app", "--host", "0.0.0.0", "--port", "8000"]Mari kita telaah baris per baris:
- Baris 1 (
FROM python:3.12-slim): image dasar resmi Python versi 3.12 ragamslim— cukup lengkap untuk aplikasi web, jauh lebih kecil daripada image penuh. - Baris 3 (
WORKDIR /app): mengatur folder kerja container; instruksiCOPYberikutnya relatif ke sini. - Baris 6 (
COPY pyproject.toml README.md ./): menyalin hanya berkas metadata yang dibutuhkan instalasi dependencies —README.mdikut karena dirujuk kuncireadmedipyproject.toml. - Baris 7 (
RUN pip install ...): memasang dependencies aplikasi secara eksplisit. Layer ini hanya dibangun ulang bila kedua berkas di baris 6 berubah. - Baris 10-11 (
COPY src/,pip install .): kode disalin terakhir, lalu aplikasi dipasang sebagai package — entry points ikut tersedia di dalam image. - Baris 14-15 (
useradd,USER appuser): container tidak lagi berjalan sebagai root; bila aplikasi diretas, penyerang tidak serta-merta memperoleh hak penuh di dalam container. - Baris 17 (
EXPOSE 8000): dokumentasi port yang dipakai aplikasi. - Baris 18 (
CMD [...]): perintah default saat container start — format list exec form lebih aman karena langsung dieksekusi tanpa shell perantara.
Perbedaan CMD dan ENTRYPOINT singkatnya: CMD adalah perintah yang mudah diganti saat docker run (misalnya diganti bash untuk mengeksplorasi image), sedangkan ENTRYPOINT menjadikan container berperilaku seperti executable tetap. Aplikasi web cukup CMD; wrapper tool seperti docker run --rm mycli --help memanfaatkan ENTRYPOINT.
.dockerignore #
Sama seperti .gitignore, berkas .dockerignore di akar project mencegah berkas sampah ikut terkirim ke daemon Docker:
.git
.venv
__pycache__/
*.pyc
.env
dist/
build/
.pytest_cache/
tests/Entri .env penting: COPY src/ ./src/ tidak menyalinnya, tetapi berkas .env yang bocor ke image adalah kebocoran secret klasik. Image yang lebih kecil juga berarti build dan pull lebih cepat.
Membangun dan Menjalankan #
Docker tidak tersedia di lingkungan pengujian materi ini, sehingga seluruh transkrip pada bagian ini bersifat ilustratif — perintah dan output mengikuti dokumentasi resmi Docker.
docker build -t toko-api:1.4.0 .Output yang Dihasilkan (ilustratif — Docker tidak tersedia di lingkungan pengujian materi ini):
[+] Building 12.4s (11/11) FINISHED
=> [internal] load build definition from Dockerfile
=> [1/5] FROM python:3.12-slim
=> [2/5] WORKDIR /app
=> [3/5] COPY pyproject.toml README.md ./
=> [4/5] RUN pip install --no-cache-dir "fastapi>=0.110,<1" "uvicorn[standard]>=0.29,<1"
=> [5/5] COPY src/ ./src/
=> exporting to image
=> => naming to docker.io/library/toko-api:1.4.0Menjalankan container dengan port dipetakan dan environment dari berkas .env:
docker run --rm -p 8000:8000 --env-file .env toko-api:1.4.0Output yang Dihasilkan (ilustratif):
INFO: Started server process [1]
INFO: Waiting for application startup.
INFO: Application startup complete.
INFO: Uvicorn running on http://0.0.0.0:8000 (Press CTRL+C to quit)Flag --rm membersihkan container setelah berhenti, -p 8000:8000 memetakan port host ke port container, dan --env-file menyuntikkan konfigurasi tanpa membakarnya ke image. Untuk aplikasi beserta database-nya, docker compose merangkai kedua container:
services:
web:
build: .
ports:
- "8000:8000"
env_file:
- .env
depends_on:
- db
db:
image: postgres:17
environment:
POSTGRES_PASSWORD: ${TOKO_DB_PASSWORD}Satu perintah docker compose up menjalankan aplikasi dan database secara bersamaan — environment pengembangan yang dapat direplikasi seluruh tim.
Ukuran Image dan Multi-Stage #
Image yang ramping mempercepat deployment dan menghemat biaya penyimpanan. Tiga kebiasaan yang paling berdampak: pakai base image slim; selalu --no-cache-dir agar pip tidak menyimpan arsip di dalam image; dan jangan pasang tool build di image final. Teknik multi-stage build mewujudkan ketiganya — tahap pertama (builder) memasang compiler dan membangun semua wheel; tahap final hanya menyalin hasilnya:
FROM python:3.12-slim AS builder
WORKDIR /app
COPY pyproject.toml README.md ./
RUN pip wheel --wheel-dir /wheels "fastapi>=0.110,<1" "uvicorn[standard]>=0.29,<1"
FROM python:3.12-slim
WORKDIR /app
COPY --from=builder /wheels /wheels
RUN pip install --no-cache-dir /wheels/* && rm -rf /wheels
COPY src/ ./src/
RUN pip install --no-cache-dir .
CMD ["uvicorn", "toko_api.main:app", "--host", "0.0.0.0", "--port", "8000"]Berhati-hatilah dengan base image alpine untuk Python: compiler musl-nya memaksa banyak paket dibangun dari sumber, sering kali lebih lambat dan lebih rawan galat daripada slim. Terakhir, tool modern seperti uv dan Poetry bisa menggantikan pip di dalam Dockerfile — uv khususnya populer karena instalasi dependencies-nya jauh lebih cepat dan cache-nya bisa dimanfaatkan antar-build.
Container menuntut satu disiplin yang sudah kita siapkan: konfigurasi harus datang dari environment — fokus chapter berikutnya.