morphos / Dockerfile
Jose Salazar
Autenticar también la precarga de modelos en la build
843a295
Raw
History Blame Contribute Delete
4.21 kB
# syntax=docker/dockerfile:1
# ---------- Stage 1: build del frontend (Vite + TS) ----------
FROM node:22-slim AS frontend
WORKDIR /build
COPY frontend/package*.json frontend/
RUN cd frontend && npm ci
# Se necesita el HTML/CSS de la raíz y los datos para la build.
COPY frontend/ frontend/
COPY index.html ./
COPY css/ css/
COPY assets/ assets/
COPY data/ data/
RUN cd frontend && npm run build # emite /build/dist
# ---------- Stage 2: runtime backend (FastAPI + uv) ----------
FROM python:3.12-slim AS runtime
# uv desde su imagen oficial (rápido y reproducible).
COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /bin/
WORKDIR /app
# Instala dependencias con el lockfile (capa cacheable). RAG viene ACTIVADO por defecto: con
# WITH_RAG=0 el retriever degrada en silencio y las interpretaciones salen sin fundamentar, que
# es justo el fallo que no queremos que pase inadvertido en producción.
ARG WITH_RAG=1
COPY backend/pyproject.toml backend/uv.lock* backend/
RUN cd backend && if [ "$WITH_RAG" = "1" ]; then \
uv sync --frozen --no-dev --group rag; \
else \
uv sync --frozen --no-dev; \
fi
# Código del backend
COPY backend/ backend/
# Estáticos públicos: build del frontend + datos de referencia (servidos en /data)
COPY --from=frontend /build/dist ./dist
COPY data/ ./data/
# Índice RAG horneado de sólo lectura. Vive en instance/ (fuera del webroot); no requiere
# almacenamiento persistente. La BD de usuarios (instance/*.db) queda EXCLUIDA vía
# .dockerignore: nunca se hornea.
#
# Dos vías: si instance/rag_index existe en el contexto de build se copia; si no (clon limpio,
# CI), se descarga del dataset PRIVADO del Hub con HF_TOKEN pasado como secreto de build.
COPY instance* ./instance/
# Se usa la API de Python y NO el CLI `hf`: en huggingface_hub 1.16.1 el CLI devuelve código 1
# aunque la operación vaya bien (incompatibilidad typer/click), lo que abortaría la build.
ARG HF_INDEX_REPO=blackmistcode/morphos-rag-index
RUN --mount=type=secret,id=hf_token,required=false \
if [ "$WITH_RAG" = "1" ] && [ ! -d /app/instance/rag_index ]; then \
if [ -f /run/secrets/hf_token ]; then \
HF_TOKEN="$(cat /run/secrets/hf_token)" backend/.venv/bin/python -c "\
from huggingface_hub import snapshot_download; \
snapshot_download(repo_id='$HF_INDEX_REPO', repo_type='dataset', local_dir='/app/instance/rag_index')"; \
else \
echo "AVISO: sin instance/rag_index y sin secreto hf_token; la imagen quedará SIN RAG." >&2; \
fi; \
fi
# Modelos de embeddings y reranking horneados en la imagen (~6.4 GB). Sin esto, la primera
# petición intentaría descargarlos de huggingface.co en caliente: lento en CPU-basic y, si la
# red falla, el reranker cae en silencio a orden RRF. Con HF_HUB_OFFLINE=1 en runtime, una
# descarga que faltase falla de forma ruidosa en vez de degradar sin avisar.
#
# Se monta el mismo secreto que en la descarga del índice: los dos modelos son públicos y la
# descarga funciona sin credencial, pero anónima cae en el límite de tasa del Hub — y son 6.4 GB,
# justo el caso donde eso muerde. `required=false` mantiene la build funcionando sin el secreto.
# El montaje sólo existe durante este RUN: el token no queda en ninguna capa de la imagen.
ARG RAG_EMBED_MODEL=BAAI/bge-m3
ARG RAG_RERANKER_MODEL=BAAI/bge-reranker-v2-m3
ENV HF_HOME=/opt/hf
RUN --mount=type=secret,id=hf_token,required=false \
if [ "$WITH_RAG" = "1" ]; then \
if [ -f /run/secrets/hf_token ]; then \
HF_TOKEN="$(cat /run/secrets/hf_token)"; export HF_TOKEN; \
fi; \
backend/.venv/bin/python -c "\
from sentence_transformers import SentenceTransformer, CrossEncoder; \
SentenceTransformer('$RAG_EMBED_MODEL'); CrossEncoder('$RAG_RERANKER_MODEL')"; \
fi
ENV MORPHOS_ENTORNO=prod \
MORPHOS_COOKIE_SECURE=true \
HF_HOME=/opt/hf \
HF_HUB_OFFLINE=1 \
PATH="/app/backend/.venv/bin:$PATH"
# HF Spaces expone el 7860.
EXPOSE 7860
COPY docker-entrypoint.sh /usr/local/bin/docker-entrypoint.sh
RUN chmod +x /usr/local/bin/docker-entrypoint.sh
ENTRYPOINT ["docker-entrypoint.sh"]
CMD ["uvicorn", "app.main:app", "--app-dir", "backend", "--host", "0.0.0.0", "--port", "7860"]