# ------------------------- # MULTI-STAGE DOCKERFILE # ------------------------- # ⭐ GLOBAL DEFAULT → CPU TORCH ARG TORCH_CHANNEL=cpu # === Stage 0: base layer =============================================================== FROM python:3.11-slim-bookworm AS base WORKDIR /install ARG TORCH_CHANNEL ENV PIP_EXTRA_INDEX_URL=https://download.pytorch.org/whl/${TORCH_CHANNEL} RUN pip install --upgrade pip setuptools wheel # === Stage 1: install UNHASHED packages =============================================== FROM base AS unhashed_builder ARG TORCH_CHANNEL COPY api_unhashed_reqs.txt . RUN echo "opencv-python-headless" >> api_unhashed_reqs.txt RUN pip install \ --prefix=/install/packages \ --no-cache-dir \ --default-timeout=100 \ --retries 10 \ -r api_unhashed_reqs.txt # === Stage 2: install HASHED stable trusted packages =================================== FROM base AS hashed_builder ARG TORCH_CHANNEL COPY api_reqs_hashed.txt . RUN pip install \ --prefix=/install/packages \ --require-hashes \ --no-cache-dir \ --default-timeout=100 \ --retries 10 \ -r api_reqs_hashed.txt # === Stage R: build Rust extensions ==================================================== FROM python:3.11-slim-bookworm AS rust_builder RUN apt-get update && \ apt-get install -y --no-install-recommends \ curl \ gcc \ libc6-dev \ pkg-config \ libssl-dev \ && \ apt-get clean && rm -rf /var/lib/apt/lists/* RUN curl https://sh.rustup.rs -sSf | sh -s -- -y \ --default-toolchain stable \ --profile minimal \ --no-modify-path ENV PATH="/root/.cargo/bin:${PATH}" RUN pip install --no-cache-dir "maturin[patchelf]" COPY rust/delta_normalizer /build/delta_normalizer COPY rust/request_router /build/request_router COPY rust/fc_parser /build/fc_parser WORKDIR /build/delta_normalizer RUN maturin build --release --strip WORKDIR /build/request_router RUN maturin build --release --strip WORKDIR /build/fc_parser RUN maturin build --release --strip # === Stage 3: runtime container ======================================================== FROM python:3.11-slim-bookworm AS runtime WORKDIR /app RUN rm -f /etc/apt/sources.list.d/*.sources && \ printf "deb http://deb.debian.org/debian bookworm main contrib non-free non-free-firmware\n\ deb http://deb.debian.org/debian bookworm-updates main contrib non-free non-free-firmware\n\ deb http://security.debian.org/debian-security bookworm-security main contrib non-free non-free-firmware\n" \ > /etc/apt/sources.list && \ apt-get update && \ apt-get install -y --no-install-recommends \ libatomic1 \ libgl1 \ libglib2.0-0 \ libmagic1 \ firejail \ supervisor \ wget \ vim \ nano \ git \ dos2unix \ ca-certificates \ curl && \ apt-get clean && \ rm -rf /var/lib/apt/lists/* COPY --from=unhashed_builder /install/packages /usr/local COPY --from=hashed_builder /install/packages /usr/local RUN pip install --force-reinstall --no-cache-dir charset-normalizer RUN pip install --no-cache-dir html2text playwright && \ playwright install --with-deps chromium # Install all Rust-compiled extensions COPY --from=rust_builder /build/delta_normalizer/target/wheels/ /tmp/rs_wheels/ COPY --from=rust_builder /build/request_router/target/wheels/ /tmp/rs_wheels/ COPY --from=rust_builder /build/fc_parser/target/wheels/ /tmp/rs_wheels/ RUN pip install --no-cache-dir /tmp/rs_wheels/*.whl && \ rm -rf /tmp/rs_wheels COPY src /app/src COPY docker/api/init_and_run_api.sh /app/init_and_run_api.sh COPY docker/api/supervisord.conf /etc/supervisor/conf.d/supervisord.conf # Copy migration assets through a fresh layer and verify them during build. COPY alembic.ini /tmp/alembic.ini COPY migrations /app/migrations RUN cp /tmp/alembic.ini /app/alembic.ini \ && grep -q '^\[alembic\]' /app/alembic.ini \ && test -s /app/alembic.ini \ && rm /tmp/alembic.ini RUN dos2unix /app/init_and_run_api.sh && chmod +x /app/init_and_run_api.sh ENV PYTHONUNBUFFERED=1 ENV PYTHONPATH=/app/src/api EXPOSE 9000 ENTRYPOINT ["/app/init_and_run_api.sh"] CMD ["/usr/bin/supervisord", "-n"]