Tutorial 15: Docker untuk Data Science dan Machine Learning
Daftar Isi
Pendahuluan
Reprodusibilitas adalah salah satu tantangan terbesar dalam data science dan machine learning. Masalah terkenal "berjalan di mesin saya" semakin diperparah ketika proyek bergantung pada versi spesifik Python, CUDA, cuDNN, library sistem, dan puluhan paket Python. Model yang berlatih dengan sempurna di laptop Anda mungkin gagal total di workstation rekan kerja atau di lingkungan cloud.
Docker menyelesaikan masalah ini dengan mengemas kode, dependensi, dan lingkungan runtime Anda ke dalam unit portabel dan mandiri yang disebut container. Dengan Docker, Anda dapat menjamin bahwa pipeline ML Anda berjalan identik di mana saja — dari pengembangan lokal hingga CI/CD hingga penyajian produksi.
Tutorial ini mencakup semua yang Anda butuhkan untuk mengontainerisasi alur kerja ML: menulis Dockerfile yang efisien, memanfaatkan multi-stage build, mengaktifkan akselerasi GPU, mengorkestrasi stack multi-layanan dengan Docker Compose, dan mengintegrasikan container ke dalam pipeline CI/CD.
Prasyarat
- Docker Engine 24.0 atau lebih baru sudah terinstal
- Docker Compose v2 sudah terinstal
- Familiar dengan baris perintah Linux
- Python 3.9+ dan pip
- (Opsional) GPU NVIDIA dengan driver terinstal untuk bagian GPU
# Verifikasi instalasi Docker
docker --version
docker compose version
Dasar-Dasar Docker untuk Insinyur ML
Konsep Utama
Images adalah template read-only yang mendefinisikan lingkungan. Mereka dibangun dari Dockerfile dan disimpan di registry (Docker Hub, AWS ECR, GCP Artifact Registry). Containers adalah instance yang berjalan dari image. Mereka adalah proses terisolasi dan sementara dengan filesystem, jaringan, dan ruang proses sendiri. Layers adalah blok bangunan dari image. Setiap instruksi di Dockerfile membuat layer baru. Docker meng-cache layer, sehingga layer yang tidak berubah digunakan kembali saat rebuild — ini sangat penting untuk iterasi cepat. Registries menyimpan dan mendistribusikan image Docker. Registry publik termasuk Docker Hub, sementara registry privat digunakan untuk model ML dan kode proprietary.Mengapa Docker Penting untuk ML
| Tantangan | Solusi Docker |
|-----------|--------------|
| Konflik dependensi | Lingkungan terisolasi per proyek |
| Ketidakcocokan versi Python | Tentukan versi Python eksak di image |
| Kompatibilitas CUDA/cuDNN | Gunakan image dasar NVIDIA dengan versi yang cocok |
| Pelatihan yang dapat direproduksi | Image yang sama menghasilkan hasil yang sama di mana saja |
| Konsistensi deployment | Dev, staging, dan produksi menggunakan image yang sama |
Menulis Dockerfile untuk Proyek ML
Dockerfile ML Dasar
# Gunakan runtime Python resmi sebagai image dasar
FROM python:3.11-slim
Atur variabel lingkungan
ENV PYTHONDONTWRITEBYTECODE=1 \
PYTHONUNBUFFERED=1 \
PIPNOCACHEDIR=1 \
PIPDISABLEPIPVERSIONCHECK=1
Atur direktori kerja
WORKDIR /app
Instal dependensi sistem (umum untuk library ML)
RUN apt-get update && apt-get install -y --no-install-recommends \
build-essential \
libgomp1 \
libglib2.0-0 \
libsm6 \
libxext6 \
libxrender-dev \
libgl1-mesa-glx \
curl \
&& rm -rf /var/lib/apt/lists/
Salin dan instal dependensi Python terlebih dahulu (untuk caching layer)
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
Salin kode aplikasi
COPY . .
Ekspos port untuk API
EXPOSE 8000
Perintah default
CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "8000"]
File Requirements untuk Proyek ML
# requirements.txt
numpy==1.26.4
pandas==2.2.0
scikit-learn==1.4.0
xgboost==2.0.3
lightgbm==4.2.0
fastapi==0.109.0
uvicorn==0.27.0
pydantic==2.5.3
joblib==1.3.2
mlflow==2.10.0
Build dan Jalankan
# Build image
docker build -t ml-inference-api:v1.0 .
Jalankan container
docker run -d \
--name ml-api \
-p 8000:8000 \
-v $(pwd)/models:/app/models \
ml-inference-api:v1.0
Cek log
docker logs ml-api
Tes API
curl http://localhost:8000/health
Multi-Stage Build untuk Image yang Dioptimasi
Multi-stage build memungkinkan Anda memisahkan lingkungan build dari lingkungan runtime, menghasilkan image produksi yang jauh lebih kecil.
# ============================================
Tahap 1: Builder - instal dependensi
============================================
FROM python:3.11-slim AS builder
WORKDIR /build
Instal dependensi build
RUN apt-get update && apt-get install -y --no-install-recommends \
build-essential \
gcc \
g++ \
gfortran \
libopenblas-dev \
&& rm -rf /var/lib/apt/lists/
Buat virtual environment
RUN python -m venv /opt/venv
ENV PATH="/opt/venv/bin:$PATH"
Instal dependensi Python
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
============================================
Tahap 2: Pelatihan - jalankan pelatihan model
============================================
FROM builder AS trainer
WORKDIR /train
COPY . .
RUN python train.py --output /train/models/model.pkl
============================================
Tahap 3: Produksi - image runtime minimal
============================================
FROM python:3.11-slim AS production
Instal hanya dependensi sistem runtime
RUN apt-get update && apt-get install -y --no-install-recommends \
libgomp1 \
libglib2.0-0 \
curl \
&& rm -rf /var/lib/apt/lists/
Salin virtual environment dari builder
COPY --from=builder /opt/venv /opt/venv
ENV PATH="/opt/venv/bin:$PATH"
Salin kode aplikasi
WORKDIR /app
COPY app.py .
COPY utils/ ./utils/
Salin model terlatih dari tahap trainer
COPY --from=trainer /train/models/model.pkl ./models/model.pkl
Buat pengguna non-root untuk keamanan
RUN useradd --create-home --shell /bin/bash mluser
USER mluser
Health check
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
CMD curl -f http://localhost:8000/health || exit 1
EXPOSE 8000
CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "8000", "--workers", "4"]
Perbandingan ukuran image:
# Build single-stage (biasanya 2-3 GB untuk ML)
docker images ml-api-single
Build multi-stage (biasanya 500 MB - 1 GB)
docker images ml-api-multi
Dukungan GPU dengan NVIDIA Docker
Menyiapkan NVIDIA Container Toolkit
# Instal NVIDIA Container Toolkit
distribution=$(. /etc/os-release; echo $ID$VERSIONID)
curl -fsSL https://nvidia.github.io/libnvidia-container/gpgkey | \
sudo gpg --dearmor -o /usr/share/keyrings/nvidia-container-toolkit-keyring.gpg
curl -s -L "https://nvidia.github.io/libnvidia-container/$distribution/libnvidia-container.list" | \
sed 's#deb https://#deb [signed-by=/usr/share/keyrings/nvidia-container-toolkit-keyring.gpg] https://#g' | \
sudo tee /etc/apt/sources.list.d/nvidia-container-toolkit.list
sudo apt-get update
sudo apt-get install -y nvidia-container-toolkit
sudo nvidia-ctk runtime configure --runtime=docker
sudo systemctl restart docker
Verifikasi akses GPU di dalam container
docker run --rm --gpus all nvidia/cuda:12.3.1-base-ubuntu22.04 nvidia-smi
Dockerfile ML dengan GPU
# Gunakan image dasar NVIDIA CUDA
FROM nvidia/cuda:12.3.1-cudnn9-runtime-ubuntu22.04
Atur variabel lingkungan
ENV DEBIANFRONTEND=noninteractive \
PYTHONDONTWRITEBYTECODE=1 \
PYTHONUNBUFFERED=1
Instal Python dan dependensi sistem
RUN apt-get update && apt-get install -y --no-install-recommends \
python3.11 \
python3.11-venv \
python3-pip \
&& ln -sf /usr/bin/python3.11 /usr/bin/python \
&& rm -rf /var/lib/apt/lists/
WORKDIR /app
Instal PyTorch dengan dukungan CUDA
RUN pip install --no-cache-dir \
torch==2.2.0 \
torchvision==0.17.0 \
--index-url https://download.pytorch.org/whl/cu121
Instal dependensi ML lainnya
COPY requirements-gpu.txt .
RUN pip install --no-cache-dir -r requirements-gpu.txt
COPY . .
CMD ["python", "traingpu.py"]
Menjalankan dengan Akses GPU
# Jalankan dengan semua GPU
docker run --gpus all -v $(pwd)/data:/app/data ml-gpu-training:v1.0
Jalankan dengan GPU spesifik
docker run --gpus '"device=0,1"' ml-gpu-training:v1.0
Jalankan dengan shared memory yang lebih besar (penting untuk DataLoader PyTorch)
docker run --gpus all --shm-size=8g ml-gpu-training:v1.0
Docker Compose untuk Stack ML
Sistem ML tipikal melibatkan beberapa layanan. Docker Compose mengorkestrasinya bersama-sama.
version: '3.8'
services:
# Layanan API ML
ml-api:
build:
context: .
dockerfile: Dockerfile
target: production
ports:
- "8000:8000"
volumes:
- model-store:/app/models
- ./logs:/app/logs
environment:
- MODELPATH=/app/models/model.pkl
- LOGLEVEL=INFO
- REDISURL=redis://redis:6379/0
- DBURL=postgresql://mluser:mlpass@postgres:5432/mldb
dependson:
redis:
condition: service
healthy
postgres:
condition: servicehealthy
deploy:
resources:
limits:
cpus: '4.0'
memory: 8G
restart: unless-stopped
# Redis untuk caching dan feature store
redis:
image: redis:7-alpine
ports:
- "6379:6379"
volumes:
- redis-data:/data
command: redis-server --maxmemory 2gb --maxmemory-policy allkeys-lru
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 5s
retries: 3
# PostgreSQL untuk prediksi dan metadata
postgres:
image: postgres:16-alpine
environment:
POSTGRESUSER: mluser
POSTGRESPASSWORD: mlpass
POSTGRESDB: mldb
ports:
- "5432:5432"
volumes:
- postgres-data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pgisready -U mluser -d mldb"]
interval: 10s
timeout: 5s
retries: 5
# MLflow untuk pelacakan eksperimen
mlflow:
image: ghcr.io/mlflow/mlflow:v2.10.0
ports:
- "5000:5000"
volumes:
- mlflow-data:/mlflow
command: >
mlflow server
--backend-store-uri sqlite:///mlflow/mlflow.db
--default-artifact-root /mlflow/artifacts
--host 0.0.0.0
--port 5000
# Jupyter Notebook untuk eksperimentasi
jupyter:
build:
context: .
dockerfile: Dockerfile.jupyter
ports:
- "8888:8888"
volumes:
- ./notebooks:/app/notebooks
- ./data:/app/data
- model-store:/app/models
environment:
- JUPYTERTOKEN=mlsecret123
# Grafana untuk dashboard
grafana:
image: grafana/grafana:10.2.0
ports:
- "3000:3000"
volumes:
- grafana-data:/var/lib/grafana
environment:
- GFSECURITYADMINPASSWORD=admin123
volumes:
model-store:
redis-data:
postgres-data:
mlflow-data:
grafana-data:
Menjalankan Stack
# Mulai semua layanan
docker compose up -d
Cek status
docker compose ps
Lihat log untuk layanan tertentu
docker compose logs -f ml-api
Skalakan layanan API
docker compose up -d --scale ml-api=3
Hentikan semua
docker compose down
Hentikan dan hapus semua data
docker compose down -v
Volume Mount untuk Data dan Model
Manajemen volume yang tepat sangat penting untuk alur kerja ML di mana data dan model harus bertahan di antara restart container.
Jenis-Jenis Mount
# Bind mount: petakan direktori host ke path container
Terbaik untuk: pengembangan, kode sumber, dataset
docker run -v /host/data:/container/data ml-training
Named volume: penyimpanan persisten yang dikelola Docker
Terbaik untuk: database, artefak model, cache
docker volume create model-store
docker run -v model-store:/app/models ml-api
tmpfs mount: filesystem di memori
Terbaik untuk: data sensitif, cache sementara
docker run --tmpfs /tmp:rw,size=2g ml-training
Praktik Terbaik untuk Data ML
# app.py - Menangani pemuatan model dari volume yang di-mount
import os
import joblib
from pathlib import Path
DIREKTORIMODEL = Path(os.getenv('MODELDIR', '/app/models'))
DIREKTORIDATA = Path(os.getenv('DATADIR', '/app/data'))
def muatmodelterbaru():
"""Memuat file model yang paling baru dimodifikasi."""
filemodel = sorted(
DIREKTORIMODEL.glob('.pkl'),
key=lambda p: p.stat().stmtime,
reverse=True
)
if not filemodel:
raise FileNotFoundError(f"Tidak ada file model ditemukan di {DIREKTORIMODEL}")
pathmodel = filemodel[0]
print(f"Memuat model: {pathmodel}")
return joblib.load(pathmodel)
def simpanmodel(model, versi):
"""Menyimpan model dengan nama file berversi."""
DIREKTORIMODEL.mkdir(parents=True, existok=True)
path = DIREKTORIMODEL / f"modelv{versi}.pkl"
joblib.dump(model, path)
print(f"Model disimpan ke {path}")
return path
Manajemen Environment dan Dependensi
Menggunakan File .env
# .env
MODELVERSION=v2.1.0
MODELPATH=/app/models/modelv2.1.0.pkl
LOGLEVEL=INFO
REDISURL=redis://redis:6379/0
APIWORKERS=4
MAXBATCHSIZE=64
Mengelola Beberapa Environment
# docker-compose.override.yml (otomatis dimuat, untuk pengembangan)
version: '3.8'
services:
ml-api:
build:
target: builder # Gunakan tahap pengembangan
volumes:
- ./src:/app/src # Hot reload kode sumber
environment:
- DEBUG=1
- LOGLEVEL=DEBUG
command: uvicorn app:app --host 0.0.0.0 --reload
# Pengembangan (menggunakan docker-compose.yml + docker-compose.override.yml)
docker compose up
Produksi
docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d
Integrasi CI/CD
Pipeline GitHub Actions
# .github/workflows/ml-pipeline.yml
name: ML Pipeline CI/CD
on:
push:
branches: [main]
pullrequest:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Build image pengujian
run: docker build --target builder -t ml-test .
- name: Jalankan unit test
run: |
docker run --rm ml-test pytest tests/ -v --cov=src --cov-report=xml
- name: Jalankan validasi model
run: |
docker run --rm \
-v ${{ github.workspace }}/data:/app/data \
ml-test python validatemodel.py
build-and-push:
needs: test
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'
steps:
- uses: actions/checkout@v4
- name: Login ke Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUBTOKEN }}
- name: Build dan push image produksi
uses: docker/build-push-action@v5
with:
context: .
target: production
push: true
tags: |
ghcr.io/${{ github.repository }}:latest
ghcr.io/${{ github.repository }}:${{ github.sha }}
cache-from: type=gha
cache-to: type=gha,mode=max
Praktik Terbaik
1. Urutkan Instruksi Dockerfile Berdasarkan Frekuensi Perubahan
Tempatkan instruksi yang paling jarang berubah di atas untuk memaksimalkan caching layer:
# Jarang berubah - di-cache
FROM python:3.11-slim
RUN apt-get update && apt-get install -y libgomp1
Berubah kadang-kadang - rebuild dari sini saat dependensi berubah
COPY requirements.txt .
RUN pip install -r requirements.txt
Sering berubah - hanya layer ini yang di-rebuild
COPY src/ ./src/
2. Gunakan .dockerignore
# .dockerignore
.git
.github
pycache
.pyc
.env
.venv
data/raw/
*.egg-info
.pytestcache
notebooks/
3. Pin Semua Versi Dependensi
Selalu gunakan versi eksak untuk memastikan reprodusibilitas penuh.
4. Gunakan Pengguna Non-Root
RUN useradd --create-home --shell /bin/bash --uid 1000 mluser
USER mluser
5. Implementasikan Health Check
HEALTHCHECK --interval=30s --timeout=10s --start-period=15s --retries=3 \
CMD python -c "import requests; requests.get('http://localhost:8000/health').raiseforstatus()"
6. Jaga Image Tetap Kecil
- Gunakan image dasar
slimataualpinejika memungkinkan. - Gabungkan perintah
RUNuntuk mengurangi layer. - Hapus tools build setelah kompilasi.
- Gunakan
--no-cache-dirdengan pip. - Manfaatkan multi-stage build untuk mengecualikan dependensi build-only.
Kesimpulan
Docker mengubah cara data scientist dan insinyur ML membangun, berbagi, dan men-deploy pekerjaan mereka. Dalam tutorial ini, Anda telah mempelajari cara:
- Menulis Dockerfile yang efisien yang disesuaikan untuk proyek ML dengan caching layer yang tepat
- Menggunakan multi-stage build untuk membuat image produksi yang ramping
- Mengaktifkan akselerasi GPU dengan NVIDIA Container Toolkit
- Mengorkestrasi stack ML lengkap menggunakan Docker Compose dengan API, database, cache, monitoring, dan pelacakan eksperimen
- Mengelola data dan model dengan volume dan bind mount
- Menangani beberapa environment (pengembangan, staging, produksi)
- Mengintegrasikan Docker ke dalam pipeline CI/CD untuk pengujian dan deployment otomatis
Dengan mengontainerisasi alur kerja ML Anda, Anda menghilangkan masalah reprodusibilitas, mempercepat onboarding, dan menciptakan jalur yang jelas dari eksperimentasi ke produksi. Mulailah dengan Dockerfile sederhana, iterasi seiring proyek Anda berkembang, dan investasikan pada Docker Compose ketika sistem Anda melibatkan beberapa layanan.