Handbook AI Engineering
Bagian V - Bab 16

Containerization dengan Docker

Akar masalah works on my machine, perbedaan image vs container, anatomi Dockerfile, dan perintah Docker yang wajib dikuasai.

Apa yang Akan Kamu Pahami Setelah Bab Ini

Kode yang berjalan mulus di laptopmu sendiri tidak menjamin ia akan berjalan sama di server lain. Bab ini menjelaskan akar masalah "works on my machine" secara teknis, dan bagaimana Docker menyelesaikannya dengan cara membungkus aplikasi beserta seluruh lingkungannya menjadi satu unit yang konsisten di mana pun ia dijalankan.

16.1 Akar Masalah "Works on My Machine"

Ketika kamu menjalankan aplikasi di laptop, ia tidak berjalan sendirian, ia bergantung pada banyak hal di sekitarnya: versi Python yang terinstal, versi setiap library, variabel environment sistem, bahkan sistem operasi itu sendiri. Semua dependency implisit ini biasanya tidak terlihat sampai aplikasi dipindahkan ke lingkungan lain yang sedikit berbeda.

Aplikasi Berjalan Sempurna

Laptop Developer

  • Python 3.11, macOS
  • pandas==2.2.0 (terinstal manual)
  • Environment variable di .zshrc
  • libssl versi macOS bawaan
Karena semua dependency implisit terpenuhi
ModuleNotFoundError, Crash

Server Cloud (Linux)

  • Python 3.9, Ubuntu 22.04
  • pandas tidak terinstal sama sekali
  • Environment variable tidak ada
  • libssl versi Linux berbeda
Karena lingkungan berbeda sepenuhnya

Docker menyelesaikan masalah ini dengan prinsip yang sederhana: alih-alih berasumsi lingkungan target sudah memiliki semua yang dibutuhkan, kamu membungkus seluruh lingkungan (interpreter Python, semua library, environment variable, bahkan sistem operasi minimal) menjadi satu paket yang berjalan identik di mana pun.

16.2 Image vs Container

Dua istilah ini sering tertukar tapi memiliki perbedaan konseptual yang penting untuk dipahami sebelum menulis Dockerfile pertamamu.

16.3 Anatomi Dockerfile

Dockerfile adalah resep langkah demi langkah yang memberi tahu Docker cara membangun image. Setiap instruksi membentuk satu layer, dan memahami urutan instruksi sangat mempengaruhi kecepatan build.

FROM python:3.11-slim
# Image dasar. slim lebih kecil dari image Python penuh,
# mengurangi ukuran akhir image.

WORKDIR /app
# Direktori kerja di dalam container. Semua instruksi
# selanjutnya dijalankan relatif terhadap direktori ini.

COPY requirements.txt .
# Urutan ini disengaja. Copy requirements.txt dulu sebelum
# kode, Docker meng-cache layer ini, jadi rebuild lebih
# cepat jika hanya kode yang berubah.

RUN pip install --no-cache-dir -r requirements.txt
# --no-cache-dir mencegah pip menyimpan cache instalasi,
# mengurangi ukuran image akhir secara signifikan.

COPY . .
# Copy seluruh kode aplikasi setelah dependencies terinstal.
# Perubahan kode tidak memicu install ulang dependencies.

ENV PYTHONUNBUFFERED=1
# Memastikan output Python (print, log) langsung muncul
# tanpa buffering, penting agar docker logs menampilkan
# log secara real-time.

EXPOSE 8000
# Dokumentasi port yang digunakan aplikasi. Bersifat
# informatif, tidak otomatis membuka port (itu dilakukan
# saat docker run -p).

CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
# Perintah yang dijalankan saat container start.
# 0.0.0.0 wajib, bukan 127.0.0.1 agar bisa diakses
# dari luar container.

16.4 Perintah Docker yang Wajib Dikuasai

build, Membangun Image dari Dockerfile

docker build -t rework-api .

-t rework-api memberi nama (tag) pada image. Titik di akhir menunjukkan Dockerfile dicari di direktori saat ini.

run, Menjalankan Container dari Image

docker run -p 8000:8000 --env-file .env rework-api

-p 8000:8000 memetakan port lokal ke port container (format: lokal:container). --env-file .env memasukkan environment variable dari file .env ke dalam container.

logs, Melihat Output dan Error dari Container

docker logs -f <container_id>

-f (follow) menampilkan log secara real-time, seperti tail -f. Ini adalah perintah pertama yang harus dijalankan saat container error atau berperilaku tidak terduga.

exec, Masuk ke Dalam Container yang Sedang Berjalan

docker exec -it <container_id> /bin/bash

Membuka terminal interaktif di dalam container, berguna untuk memeriksa apakah file ada, environment variable terbaca dengan benar, atau menjalankan perintah debug secara langsung.

ps / stop, Melihat dan Menghentikan Container yang Berjalan

docker ps
docker stop <container_id>

docker ps menampilkan container yang sedang berjalan beserta ID-nya. Gunakan ID tersebut untuk perintah logs, exec, atau stop.

system prune, Membersihkan Image dan Container yang Tidak Terpakai

docker system prune -a

Menghapus semua image, container, dan cache yang tidak digunakan. Berguna ketika laptop kehabisan storage karena terlalu banyak image dari eksperimen sebelumnya.

Kesalahan Umum

Jangan menunggu proyek selesai untuk pertama kali mencoba Docker.

Semakin banyak kode yang ditambahkan sebelum Docker pernah dicoba, semakin sulit menemukan penyebab error saat akhirnya di-build. Buat Dockerfile sejak fitur pertama selesai, dan build ulang secara berkala setiap kali menambahkan dependency baru.

Jangan gunakan host="127.0.0.1" di dalam container.

127.0.0.1 hanya menerima koneksi dari dalam container itu sendiri, tidak bisa diakses dari luar, termasuk dari laptop kamu via port mapping. Selalu gunakan host="0.0.0.0" agar aplikasi menerima koneksi dari luar container.

Jangan menebak-nebak penyebab error Docker, selalu baca docker logs dulu.

Pesan error di terminal seperti "container exited with code 1" tidak memberi informasi yang cukup. Jalankan docker logs  untuk melihat traceback lengkap dari aplikasi di dalamnya, informasi sebenarnya selalu ada di sana.

File .env tidak otomatis ikut ke dalam container.

Karena .env ada di .gitignore , dan biasanya juga ditambahkan ke .dockerignore, file ini tidak ikut ter-copy ke image. Gunakan flag --env-file .env saat docker run untuk memasukkan environment variable secara terpisah saat container dijalankan.

Urutan instruksi di Dockerfile mempengaruhi kecepatan build.

Docker meng-cache setiap layer. Jika kamu copy seluruh kode sebelum install dependencies, setiap perubahan kode kecil akan memicu instalasi ulang seluruh dependencies. Selalu copy requirements.txt dan install dependencies terlebih dahulu, baru copy kode aplikasi.

Buat file .dockerignore seperti .gitignore.

File ini mencegah venv/__pycache__, dan file yang tidak perlu ikut ter-copy ke dalam image, mengurangi ukuran image dan mempercepat proses build secara signifikan.

Ringkasan Bab 16

Checklist Pemahaman
  • Saya memahami akar masalah "works on my machine", perbedaan dependency implisit antara lingkungan development dan lingkungan target.
  • Saya bisa menjelaskan perbedaan image (blueprint statis) dan container (instance yang berjalan), dan bahwa satu image bisa menghasilkan banyak container.
  • Saya bisa menulis Dockerfile lengkap untuk aplikasi FastAPI, termasuk urutan instruksi yang optimal untuk caching layer.
  • Saya bisa menjalankan siklus dasar Docker: build image, run container dengan port mapping dan environment variable, lihat logs, dan masuk ke dalam container dengan exec.
  • Saya selalu menggunakan host="0.0.0.0" (bukan 127.0.0.1) di aplikasi yang dijalankan dalam container.
  • Saya tahu cara membaca docker logs untuk mendiagnosis error, sebagai langkah pertama sebelum mengubah kode apapun.
  • Saya sudah membuat file .dockerignore untuk mengecualikan venv/, __pycache__/, dan file yang tidak perlu dari image.
  • Saya mencoba Docker sejak awal pengembangan, bukan menunggu proyek selesai sepenuhnya.