2026-06-12 16:04:03 +08:00
|
|
|
|
# Stage 1: 构建前端
|
|
|
|
|
|
FROM node:20-alpine AS frontend-builder
|
|
|
|
|
|
|
|
|
|
|
|
ARG NPM_REGISTRY=https://registry.npmmirror.com
|
|
|
|
|
|
|
|
|
|
|
|
WORKDIR /app/frontend
|
|
|
|
|
|
COPY frontend/package*.json ./
|
2026-06-14 15:14:40 +08:00
|
|
|
|
RUN --mount=type=cache,target=/root/.npm \
|
|
|
|
|
|
npm install --registry=${NPM_REGISTRY}
|
2026-06-12 16:04:03 +08:00
|
|
|
|
COPY frontend/ .
|
|
|
|
|
|
RUN npm run build
|
|
|
|
|
|
|
|
|
|
|
|
# Stage 2: Python 后端
|
|
|
|
|
|
FROM python:3.12-slim
|
|
|
|
|
|
|
2026-06-14 15:14:40 +08:00
|
|
|
|
ARG PIP_INDEX=https://mirrors.aliyun.com/pypi/simple/
|
2026-06-12 16:04:03 +08:00
|
|
|
|
|
|
|
|
|
|
WORKDIR /app
|
|
|
|
|
|
|
2026-06-14 15:14:40 +08:00
|
|
|
|
# 先只 COPY requirements.txt,利用 Docker 层缓存——只要依赖不变就命中缓存
|
2026-06-12 16:04:03 +08:00
|
|
|
|
COPY requirements.txt .
|
2026-06-14 15:14:40 +08:00
|
|
|
|
|
|
|
|
|
|
# 用 --only-binary=:all: 强制只下载预编译 wheel,避免编译 scikit-learn
|
|
|
|
|
|
# 若平台无 wheel 会报错,但 x86_64 上 scikit-learn/numpy/scipy 都有
|
|
|
|
|
|
RUN --mount=type=cache,target=/root/.cache/pip \
|
|
|
|
|
|
pip install --no-cache-dir -r requirements.txt \
|
|
|
|
|
|
-i ${PIP_INDEX} \
|
|
|
|
|
|
--trusted-host mirrors.aliyun.com \
|
|
|
|
|
|
--only-binary=:all: \
|
|
|
|
|
|
|| pip install --no-cache-dir -r requirements.txt \
|
|
|
|
|
|
-i ${PIP_INDEX} \
|
|
|
|
|
|
--trusted-host mirrors.aliyun.com
|
|
|
|
|
|
|
|
|
|
|
|
# 创建非 root 用户(不需要 gcc 了,去掉 apt-get 节省 ~40s)
|
|
|
|
|
|
RUN useradd --create-home --uid 1000 app
|
2026-06-12 16:04:03 +08:00
|
|
|
|
|
|
|
|
|
|
COPY . .
|
|
|
|
|
|
COPY --from=frontend-builder /app/frontend/dist ./static
|
|
|
|
|
|
|
|
|
|
|
|
# 确保数据目录对 app 用户可写
|
|
|
|
|
|
RUN mkdir -p /app/data && chown -R app:app /app/data
|
|
|
|
|
|
|
|
|
|
|
|
USER app
|
|
|
|
|
|
|
|
|
|
|
|
EXPOSE 7331
|
|
|
|
|
|
|
|
|
|
|
|
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "7331", "--workers", "1"]
|