Gridlock / Dockerfile
parvmittal07's picture
Initial Demo complete
01eb82e
Raw
History Blame Contribute Delete
4.98 kB
# syntax=docker/dockerfile:1
#
# Gridlock Traffic Intelligence — single-image demo.
# One container serves the React frontend AND the FastAPI inference API on one
# port. Runs unchanged on Hugging Face Spaces (Docker SDK), Render, or any host.
#
# This Dockerfile lives at the repo root because Hugging Face Spaces builds the
# `Dockerfile` at the root with the repo as context. Build context = repo root:
# docker build -t gridlock .
# docker run -p 8000:8000 gridlock # -> http://localhost:8000
#
# ---------------------------------------------------------------------------- #
# Stage 1 — build the frontend with Node, output static assets to /build/dist
# ---------------------------------------------------------------------------- #
FROM node:20-slim AS frontend
WORKDIR /build
# Install deps first (cached unless the lockfile changes).
COPY app/frontend/package.json app/frontend/package-lock.json ./
RUN npm ci
# Build the production bundle.
COPY app/frontend/ ./
RUN npm run build
# ---------------------------------------------------------------------------- #
# Stage 2 — Python runtime serving API + the built frontend
# ---------------------------------------------------------------------------- #
FROM python:3.10-slim AS runtime
ENV PYTHONUNBUFFERED=1 \
PYTHONDONTWRITEBYTECODE=1 \
PIP_NO_CACHE_DIR=1
WORKDIR /app
# libgomp1 is required by LightGBM / XGBoost at runtime.
RUN apt-get update \
&& apt-get install -y --no-install-recommends libgomp1 \
&& rm -rf /var/lib/apt/lists/*
# Install the CPU-only PyTorch wheel first (avoids pulling ~2GB of CUDA libs),
# then the remaining inference dependencies. torch==2.12.1 in requirements is
# then already satisfied by the +cpu build, so it is not re-resolved from PyPI.
# pip is upgraded first because the 23.0.1 shipped in slim rejects the PyTorch
# index's `typing_extensions` wheels (name-normalization bug). xgboost declares
# nvidia-nccl-cu12 (~300MB) for multi-GPU only; CPU inference never loads it, so
# we drop it (scoped `|| true` keeps the build green if it is already absent).
COPY app/requirements.txt ./requirements.txt
RUN pip install --no-cache-dir --upgrade pip \
&& pip install --no-cache-dir torch==2.12.1 --index-url https://download.pytorch.org/whl/cpu \
&& pip install --no-cache-dir -r requirements.txt \
&& { pip uninstall -y nvidia-nccl-cu12 || true; }
# Hugging Face Spaces runs the container as UID 1000. Create that user and switch
# to it BEFORE downloading the model + copying code, so the HF cache and app
# files are owned by the runtime user without a costly `chown -R` layer. This is
# also valid on Render / any host (uvicorn binds a high port, so no root needed).
RUN useradd -m -u 1000 user
USER user
ENV HOME=/home/user \
PATH=/home/user/.local/bin:$PATH \
HF_HOME=/home/user/.cache/huggingface
# Pre-download the multilingual sentence-transformer into the image (owned by
# `user`) so the first /api/predict is fast and the container needs no network.
RUN python -c "from sentence_transformers import SentenceTransformer; \
SentenceTransformer('sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2')"
# ---- Application code + the artifacts inference actually loads (owned by user) ---- #
COPY --chown=user:user src/ ./src/
COPY --chown=user:user hotspot_model.py ./hotspot_model.py
COPY --chown=user:user app/__init__.py ./app/__init__.py
COPY --chown=user:user app/backend/ ./app/backend/
# Trained models + preprocessors (T1–T4) and the geo KMeans.
COPY --chown=user:user models/ ./models/
# Hotspot bundle + replay history (the raw training CSV is NOT shipped).
COPY --chown=user:user hotspot_artifacts/hotspot_bundle.joblib hotspot_artifacts/hotspot_history.parquet hotspot_artifacts/hotspot_metrics.json ./hotspot_artifacts/
# Causal target-rate history used by the feature pipeline at inference time.
COPY --chown=user:user data/processed/history.parquet ./data/processed/history.parquet
# Report figures (served by /api/figures) + metric JSON.
COPY --chown=user:user reports/ ./reports/
# Built frontend from stage 1.
COPY --chown=user:user --from=frontend /build/dist ./app/frontend/dist
# Build-time smoke test: load every pickled artifact (T1-T4 models, preprocessors,
# hotspot bundle + histories) through the real inference service. This fails the
# build early if there is any library-version skew or a missing file, rather than
# surfacing it at runtime on the first request.
RUN python -c "from app.backend.inference import get_service; get_service(); print('Inference artifacts load OK')"
# Skip network calls to Hugging Face now that the model is cached in the image.
ENV HF_HUB_OFFLINE=1 \
TRANSFORMERS_OFFLINE=1 \
PORT=8000
# HF Spaces routes to the port declared as `app_port` in README.md (8000 here);
# Render injects $PORT. Both are honoured by the CMD below.
EXPOSE 8000
CMD ["sh", "-c", "uvicorn app.backend.main:app --host 0.0.0.0 --port ${PORT:-8000}"]