← Semua pembelajaran / Python untuk AI Engineer
Fase 6 · Data, Testing, Deploy

uv — Using uv in Docker

Dockerfile untuk aplikasi Python modern: multi-stage, cache layer yang benar, dan image production yang ramping.

Sumber asli docs.astral.sh Resmi Rangkuman ~6 menit baca

Intisari

  • Salin pyproject.toml dan uv.lock sebelum kode aplikasi — supaya layer dependency bisa di-cache.
  • uv sync --frozen --no-dev di production: gagal kalau lockfile tidak sinkron, dan tidak memasang pytest.
  • Multi-stage build: builder memasang dependency, image akhir hanya menyalin hasilnya.
  • Jalankan sebagai non-root, dan letakkan .venv/bin di PATH.
  • .dockerignore yang baik memangkas ukuran konteks build secara drastis.

Dockerfile lengkap

# ── Stage 1: builder ─────────────────────────────────
FROM python:3.13-slim AS builder

COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /bin/

ENV UV_COMPILE_BYTECODE=1 \
    UV_LINK_MODE=copy \
    UV_PYTHON_DOWNLOADS=never

WORKDIR /app

# 1. Dependency dulu — layer ini di-cache selama lockfile tidak berubah
COPY pyproject.toml uv.lock ./
RUN --mount=type=cache,target=/root/.cache/uv \
    uv sync --frozen --no-install-project --no-dev

# 2. Baru kode aplikasi
COPY src/ ./src/
RUN --mount=type=cache,target=/root/.cache/uv \
    uv sync --frozen --no-dev


# ── Stage 2: runtime ─────────────────────────────────
FROM python:3.13-slim

RUN useradd --create-home --uid 1000 app
WORKDIR /app

COPY --from=builder --chown=app:app /app/.venv /app/.venv
COPY --from=builder --chown=app:app /app/src /app/src

ENV PATH="/app/.venv/bin:$PATH" \
    PYTHONUNBUFFERED=1 \
    PYTHONDONTWRITEBYTECODE=1

USER app
EXPOSE 8000

HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
    CMD python -c "import httpx; httpx.get('http://localhost:8000/health').raise_for_status()"

CMD ["uvicorn", "myapp.main:app", "--host", "0.0.0.0", "--port", "8000"]

Kenapa urutannya begitu

Docker meng-cache layer, dan cache batal begitu ada file yang berubah. Kode aplikasimu berubah puluhan kali sehari; dependency-mu mungkin sebulan sekali. Dengan menyalin pyproject.toml dan uv.lock lebih dulu, layer pemasangan dependency tetap ter-cache — dan build berikutnya hanya butuh beberapa detik alih-alih beberapa menit.

# ❌ salah — tiap perubahan kode memicu install ulang semua dependency
COPY . .
RUN uv sync --frozen

# ✅ benar
COPY pyproject.toml uv.lock ./
RUN uv sync --frozen --no-install-project --no-dev
COPY src/ ./src/
RUN uv sync --frozen --no-dev

Flag yang penting

FlagFungsinya
--frozenGagal kalau uv.lock tidak cocok dengan pyproject.toml — build jadi reprodusibel
--no-devLewati pytest, mypy, dll — image lebih kecil
--no-install-projectHanya dependency, belum kode proyeknya (untuk pemisahan layer)
UV_COMPILE_BYTECODE=1Pre-compile .pyc — startup lebih cepat
UV_LINK_MODE=copySalin, jangan hardlink — hardlink tidak bekerja lintas layer
UV_PYTHON_DOWNLOADS=neverPakai Python dari image dasar, jangan unduh lagi

Cache mount

RUN --mount=type=cache,target=/root/.cache/uv \
    uv sync --frozen --no-dev

Cache download uv bertahan antar build tanpa masuk ke dalam image. Butuh BuildKit (default di Docker modern). Ini bisa memangkas waktu build secara signifikan.

.dockerignore

.venv/
__pycache__/
*.pyc
.pytest_cache/
.mypy_cache/
.ruff_cache/
.git/
.github/
tests/
docs/
*.md
.env
.env.*
data/
*.db
.DS_Store

Menyertakan .venv/ ke konteks build adalah kesalahan yang sangat umum. Foldernya besar, spesifik OS, dan akan ditimpa oleh uv sync di dalam container. Ia hanya memperlambat build. Sama pentingnya: .env harus di-ignore supaya kredensial tidak ikut ke image.

Jangan jalankan sebagai root

RUN useradd --create-home --uid 1000 app
COPY --from=builder --chown=app:app /app/.venv /app/.venv
USER app

Kalau ada kerentanan di aplikasimu, penyerang mendapat hak akses user biasa — bukan root di dalam container.

Environment variable

ENV PATH="/app/.venv/bin:$PATH" \
    PYTHONUNBUFFERED=1 \
    PYTHONDONTWRITEBYTECODE=1
VariabelKenapa
PATH ke .venv/binSupaya uvicorn dan python memakai venv, tanpa uv run
PYTHONUNBUFFERED=1Wajib — tanpa ini log tertahan di buffer dan tidak muncul
PYTHONDONTWRITEBYTECODE=1Jangan tulis .pyc saat runtime (sudah di-compile saat build)

Rahasia

# ❌ JANGAN — key ikut tersimpan permanen di layer image
ENV ANTHROPIC_API_KEY=sk-ant-xxx
# ✅ saat runtime
services:
  api:
    image: myapp:latest
    environment:
      - MAX_TOKENS=8192
    secrets:
      - anthropic_api_key

secrets:
  anthropic_api_key:
    file: ./secrets/anthropic_api_key
class Settings(BaseSettings):
    model_config = SettingsConfigDict(secrets_dir="/run/secrets")
    anthropic_api_key: SecretStr

Layer image bersifat permanen. Menghapus rahasia di layer berikutnya tidak menghapusnya dari layer sebelumnya — siapa pun yang punya image itu bisa mengambilnya kembali. Rahasia hanya boleh masuk saat runtime.

Docker Compose untuk development

services:
  api:
    build: .
    ports:
      - "8000:8000"
    environment:
      - AWS_REGION=us-east-1
    env_file:
      - .env
    volumes:
      - ./src:/app/src          # hot reload saat development
    command: uvicorn myapp.main:app --host 0.0.0.0 --reload
    depends_on:
      chroma:
        condition: service_healthy

  chroma:
    image: chromadb/chroma:latest
    volumes:
      - chroma-data:/chroma/chroma
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:8000/api/v2/heartbeat"]
      interval: 10s

volumes:
  chroma-data:

Memeriksa hasilnya

docker build -t myapp .
docker images myapp                      # cek ukuran — target di bawah 300 MB
docker run --rm -p 8000:8000 --env-file .env myapp
docker history myapp                     # lihat kontribusi tiap layer

Kalau image-mu kegedean

  1. Pastikan memakai multi-stage — jangan sertakan toolchain build di image akhir
  2. Pastikan --no-dev dipakai
  3. Periksa .dockerignore
  4. Pakai python:3.13-slim, bukan tag penuh
  5. Cek apakah ada dependency berat yang sebenarnya tidak dipakai (torch, pandas di production)

Rangkuman ini sengaja dipangkas ke bagian yang dipakai di roadmap. Buka sumber aslinya saat kamu butuh detail lengkap atau referensi parameter.