Dockerfile
Rama main · réplica de lectura del repositorio original.
# MiniMax-H3-Lite — 容器化(GPU 单机工作台)
#
# 形态:FastAPI 单端口工作台(Jinja2 页面 + /api + /assets 静态资源),进程内 Comfy 内核推理。
# 基础镜像:python:3.12-slim-bookworm —— 与家族 SeedVR2-Lite / Image-DuoModel 一致。
# torch 走 cu130 轮子(本机已验证 torch 2.9.1+cu130,见 scripts/clean_launch.py 注释),
# 由宿主机 nvidia-container-toolkit 提供 GPU 访问,无需在镜像里装 CUDA toolkit。
#
# 构建:docker build -t minimax-h3-lite:2.9.1-cu130 \
# --build-arg VCS_REF=$(git rev-parse HEAD) \
# --build-arg BUILD_DATE=$(date -u +%Y-%m-%dT%H:%M:%SZ) .
# 运行:docker compose up -d --build (见 docker-compose.yml)
# === 全局构建参数(FROM 前仅允许 ARG;LABEL 须在 FROM 之后的 stage 内,
# 否则 classic/buildx 解析报 "no build stage in current context")===
ARG VCS_REF=unknown
ARG BUILD_DATE=unknown
ARG VERSION=2.6.0
FROM python:3.12-slim-bookworm
# === OCI 镜像元数据(Trivy/GHCR 可读;引用上方全局 ARG)===
LABEL org.opencontainers.image.title="minimax-h3-lite" \
org.opencontainers.image.description="MiniMax H3 video generation timeline workbench" \
org.opencontainers.image.source="https://github.com/ReSerendipity/MiniMax-H3-Lite" \
org.opencontainers.image.url="https://github.com/ReSerendipity/MiniMax-H3-Lite" \
org.opencontainers.image.version="${VERSION}" \
org.opencontainers.image.revision="${VCS_REF}" \
org.opencontainers.image.created="${BUILD_DATE}" \
org.opencontainers.image.licenses="Apache-2.0" \
org.opencontainers.image.vendor="ReSerendipity"
ENV DEBIAN_FRONTEND=noninteractive \
PYTHONUNBUFFERED=1 \
PIP_NO_CACHE_DIR=1
# 系统依赖:backend/watermark.py 以子进程调用 ffmpeg / ffprobe。
# apt-get upgrade:基础镜像的 OS 包(如 libpcre2-8-0 10.42-1 未含 2026 安全
# 补丁)会以 HIGH 级被 Trivy gate 拦截(2026-09-05 实证 5 项 pcre2 CVE),
# 升级到 bookworm 最新补丁线一次清偿并防后续同类拦截。
RUN apt-get update \
&& apt-get upgrade -y \
&& apt-get install -y --no-install-recommends \
ffmpeg \
&& rm -rf /var/lib/apt/lists/*
WORKDIR /app
# 升级 pip(构建工具链,越早越好)
RUN pip install --no-cache-dir --upgrade pip
# torch 由 CUDA Python 环境(WinPython)提供,requirements.txt 不含 torch;
# 仅 cu130 索引提供 +cu130 本地版本,pip 据此从 PyTorch 源拉取对应轮子。
# 若 cu130 索引不可用,请将下方版本/索引改为实际可用组合(如 cu128)。
RUN pip install --no-cache-dir \
torch==2.9.1+cu130 \
--extra-index-url https://download.pytorch.org/whl/cu130
# Python 依赖(放在 COPY 代码前以复用层缓存)
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# 安全升级:修依赖链 HIGH 漏洞(CVE-2025-47273 setuptools 路径穿越、
# GHSA-6v7p-g79w-8964 msgpack 越界读、CVE-2026-59890 setuptools MANIFEST 绕过)。
# ⚠ 必须位于 requirements 安装之后:requirements.txt 未 pin 这两包,传递依赖
# 解析可能把早期升级压回旧版——2026-09-05 Trivy gate 实测复拦即此因。
#
# ⚠ 另注(2026-09-05/06 Trivy gate 取证结论):CI 门禁最终采用
# docker export 合并视图 + `trivy rootfs` 扫描并 skip pip 的 vendor SBOM
# (pip/_vendor/bom.cdx.json 内含 msgpack 1.1.2/setuptools 70.3.0 声明,
# 曾被 Trivy 误当作扫描目标组件清单)。本层保持纯净升级即可。
RUN pip install --no-cache-dir --upgrade setuptools msgpack
# 复制项目代码(model/ 等大权重经 .dockerignore 排除,运行时挂载)
COPY . .
# 编译检查(致命:backend/scripts 内任一 Python 语法错误都会阻断构建——不再用
# `|| true` 吞掉退出码,也不再 `2>/dev/null` 隐藏报错;与 QUALITY_CONTRACT.md
# 「已移除 continue-on-error / || true,发布门禁恢复阻断」一致,见 ADR-0004)。
RUN python -m compileall -q backend scripts
# 非 root 运行(安全基线;与家族 SeedVR2-Lite 一致)
RUN useradd -m -u 1000 appuser && chown -R appuser:appuser /app
USER appuser
# 离线模型读取 / 显存分配(对齐 clean_launch.py 约定)
ENV HF_HUB_OFFLINE=1 \
TRANSFORMERS_OFFLINE=1 \
MODELSCOPE_OFFLINE=1 \
PYTORCH_CUDA_ALLOC_CONF=expandable_segments:True \
COMFYUI_DISABLE_UPDATE_CHECK=1 \
MMH3_PORT=18080
EXPOSE 18080
# 健康检查:backend/main.py:76 定义 GET /api/health;响应含 model_loaded 真实就绪信号。
# R1/P2-a(评估 v1.0.0 · D1-a 解析 JSON):HEALTHCHECK 现解析 JSON 的 model_loaded 字段,
# 仅当模型已就绪才返回 0,避免「healthy ≠ 可推理」。
# 注意:模型为惰性加载(首次成功推理后置位 runtime_state.MODEL_LOADED),
# 冷启动后若无推理请求会持续 unhealthy;本仓 restart: unless-stopped 不依据 health 重启,
# 故仅影响监控直觉,不触发重启循环(见 docker-compose.yml:23)。
# H3 模型首次加载常超 2 分钟,冷启动 start-period 300s;预热后 ~10s。SOPS-7 §7。
HEALTHCHECK --interval=30s --timeout=10s --retries=3 --start-period=300s \
CMD python -c "import sys,json,urllib.request; d=json.load(urllib.request.urlopen('http://127.0.0.1:18080/api/health')); sys.exit(0 if d.get('model_loaded') else 1)"
# 容器内绑定地址(R3 / 问答一 方案 A 的安全落地变体)。
# 容器内 uvicorn 固定绑定 0.0.0.0——这是「部署形态下的等价安全替代」:
# ① 暴露面完全由 docker-compose `ports` 收口(默认 127.0.0.1:18080:18080);
# ② compose 端口发布依赖容器内 0.0.0.0 监听(DNAT 才能投递),故不可改为 127.0.0.1,
# 否则 docker compose up 后对外端口将无响应(致命 footgun);
# ③ 裸机侧回环强制(scripts/clean_launch.py._require_loopback + config.py MMH3_HOST
# fail-fast)保持不变,不可动。
# MMH3_DOCKER_BIND 为「显式、可审计」的部署意图声明(值固定 0.0.0.0);覆盖为其他值
# 不支持(会破坏端口发布),故此处直接固定而非读取可变开关——既闭合「容器侧无对等控制」
# 的语义缺口,又规避了「默认 127.0.0.1 + 开关翻转」会令标准部署失效的反模式。
ENV MMH3_DOCKER_BIND=0.0.0.0
CMD ["python", "-m", "uvicorn", "backend.main:app", "--host", "0.0.0.0", "--port", "18080"]