| # ============================================================================ | |
| # Structured Data Extraction β API image | |
| # | |
| # Multi-stage build: builder installs Python deps into a venv, runtime is a | |
| # slim image with only the venv + source code. Cuts final image size roughly | |
| # in half vs a single-stage build (no pip cache, no build toolchain). | |
| # ============================================================================ | |
| # ---------- Stage 1: builder -------------------------------------------------- | |
| FROM python:3.11-slim AS builder | |
| # System build tools β only present in the builder stage, not runtime. | |
| # Needed because pymupdf / Pillow may compile C extensions on some archs. | |
| RUN apt-get update && apt-get install -y --no-install-recommends \ | |
| build-essential \ | |
| gcc \ | |
| && rm -rf /var/lib/apt/lists/* | |
| # Create an isolated venv so the runtime stage can just copy /opt/venv. | |
| RUN python -m venv /opt/venv | |
| ENV PATH="/opt/venv/bin:$PATH" | |
| WORKDIR /build | |
| COPY requirements.txt . | |
| RUN pip install --no-cache-dir --upgrade pip \ | |
| && pip install --no-cache-dir -r requirements.txt | |
| # ---------- Stage 2: runtime -------------------------------------------------- | |
| FROM python:3.11-slim AS runtime | |
| # poppler-utils supports pdf2image; libglib is a pymupdf runtime dep. | |
| RUN apt-get update && apt-get install -y --no-install-recommends \ | |
| poppler-utils \ | |
| libglib2.0-0 \ | |
| curl \ | |
| && rm -rf /var/lib/apt/lists/* | |
| # Copy the venv from the builder β no dev deps, no pip cache. | |
| COPY --from=builder /opt/venv /opt/venv | |
| ENV PATH="/opt/venv/bin:$PATH" \ | |
| PYTHONDONTWRITEBYTECODE=1 \ | |
| PYTHONUNBUFFERED=1 \ | |
| PYTHONPATH=/app | |
| # Non-root user β hardens against container escapes. FastAPI needs no privs. | |
| RUN groupadd --system app && useradd --system --gid app --home /app app | |
| WORKDIR /app | |
| # Copy only what the API needs at runtime. src/ contains the entire python | |
| # package; pyproject.toml is present for tooling that reads project metadata. | |
| COPY --chown=app:app src/ ./src/ | |
| COPY --chown=app:app pyproject.toml ./ | |
| USER app | |
| EXPOSE 8000 | |
| # Container healthcheck β Docker + docker-compose will use this to gate startup | |
| # of dependent services (the UI waits on `service_healthy`). | |
| HEALTHCHECK --interval=15s --timeout=3s --start-period=10s --retries=3 \ | |
| CMD curl -fsS http://localhost:8000/health || exit 1 | |
| # uvicorn's --host 0.0.0.0 makes the port reachable from other compose services. | |
| # Single worker keeps the FastAPI dependency singleton (DocumentExtractor) | |
| # consistent; if we ever need horizontal scale we'll switch to gunicorn. | |
| CMD ["uvicorn", "src.api.main:app", "--host", "0.0.0.0", "--port", "8000"] | |