第 5 课
← 返回系列列表

部署实战:从克隆到出片,一条可复现的流水线

MiniMax-H3 × DGX Spark — 六期原理实战

把前四期的原理、量化、硬件、补丁装进一条能跑的流水线:固定 digest 镜像 + 文件级补丁覆盖 + 八个启动参数 + fail-closed 验证链路。

下载视频 (MP4)

部署实战 从克隆到出片,一条可复现的流水线

T AGENT 开发纪实 · EP34 · MiniMax-H3 六期原理课 ⑤
前五期讲完了原理、量化、硬件、补丁——这一期把它们装进一条能跑的流水线:镜像怎么构建、补丁怎么进去、每个启动参数是干嘛的、怎么验证没跑偏。
个 Dockerfile
1
个启动参数
8
个 make 命令
6
冷启动
9min

💡 本节要点:EP34:部署实战——从克隆到出片的一条可复现流水线:固定 digest 镜像 + 文件级补丁覆盖 + 在线动态 FP8 + OpenAI 兼容 API + fail-closed 验证链路

起点问题

四个问题:一条流水线由什么组成?

流水线全景

兼容层的完整形态:一条单向流水线

FL2VA checkpoint 约 135GB 磁盘 模型权重 → 固定 digest vLLM-Omni 镜像 ARM64 可复现 → SM121 兼容补丁 文件级覆盖 4 处修正 → 在线动态 FP8 免校准 内存减半 → OpenAI 兼容 API /v1/videos/sync HTTP → 验证器 HTTP + FFmpeg fail-closed
- 📦 · 交付物 · 一个镜像 + 一份 .env + 六个 make 命令
- 🔁 · 可复现 · digest 固定、无宿主路径、无凭据入库
- 🛡️ · 可验证 · 每一步都有检查,不满足就 fail

💡 本节要点:流水线全景:FL2VA checkpoint → 固定 digest 的 vLLM-Omni 镜像 → SM121 兼容补丁 → 在线动态 FP8 → SDPA+eager → OpenAI 兼容视频 API → HTTP/FFmpeg 验证器

核心代码 · Dockerfile

兼容层 = 4 个文件覆盖进镜像

ARG BASE_IMAGE=vllm/vllm-omni:minimax-h3@sha256:e930db8e...  # 固定 digest

COPY patches/minimax_h3_transformer.py \
  /usr/local/lib/python3.12/dist-packages/vllm_omni/.../minimax_h3_transformer.py
  # ① 补丁文件直接覆盖同名源文件(文件级注入)

COPY start-fp8.sh /usr/local/bin/start-minimax-h3              # ② 启动器
COPY scripts/security-common.sh /usr/local/lib/minimax-h3/     # ③ 安全策略
COPY fp8-quant.json /etc/minimax-h3/fp8-quant.json             # ④ 量化配置
ENTRYPOINT ["/usr/local/bin/start-minimax-h3"]

关键设计: 兼容层不是"重新打包 vLLM-Omni",而是 在一个已验证的镜像上做最小文件级覆盖 ——一个补丁文件、一个启动脚本、一份量化配置、一份安全库。四个 COPY,就是全部改动。

💡 本节要点:Dockerfile:兼容层怎么'进'镜像——4 个 COPY:补丁覆盖 dist-packages、启动器、安全库、量化配置;基础镜像固定 digest

部署形态 · Compose

一个 compose 文件,一台机器,三个卷

services:
  minimax-h3:
    network_mode: host     # 共享宿主网络
    ipc: host
    shm_size: "8gb"        # 共享内存
    gpus: all              # 全部 GPU
    environment:
      PYTORCH_CUDA_ALLOC_CONF: expandable_segments:True
      VLLM_OMNI_VIDEO_SYNC_TIMEOUT: "1800"
    volumes:
      - ${MINIMAX_H3_MODEL_DIR}:/models/MiniMax-H3/FL2VA:ro  # 模型只读
      - ${HF_CACHE_DIR}:/root/.cache/huggingface             # HF 缓存
      - ./output:/output                                     # 产物

便携: 所有宿主特定路径都走 .env 注入 ——仓库里没有绝对路径,clone 下来改 .env 就能跑。
- 三个卷各管一件事 · · 模型:只读挂载,容器不写权重 · HF 缓存:复用已下载的依赖 · output:产物落在宿主可见目录
注意: MINIMAX_H3_MODEL_DIR 未设置直接 fail (${VAR:?} 语法)——缺配置比配错配置更早暴露。

💡 本节要点:compose.yaml:单 GPU 便携服务——host 网络 / ipc / shm 8GB / gpus all;三卷挂载(模型只读、HF 缓存、输出);.env 注入无宿主路径

核心代码 · 启动参数

八个参数,每个都是一个决策

参数 作用 / 对应决策
--omni 启用 vLLM-Omni 多模态流水线
--num-weight-load-threads 2 加载并发 2 → 控峰值内存(EP32)
--enforce-eager 避开 SM121 编译路径(EP33)
--diffusion-attention-backend TORCH_SDPA 避开 FA4 变长内核(EP33)
--diffusion-quantization-config 在线动态 FP8(EP31)
--force-cutlass-fp8 强制 Cutlass FP8 线性路径(EP31/33)
--stage-init-timeout 1800 / --init-timeout 2400 冷启动约 9 分钟,超时给足
规律: 八个参数,七个来自前几期的坑—— 每个规避项都沉淀成了一个启动参数 。这就是"踩过的坑变成配置"。
> 💡 本节要点:8 个启动参数逐条:--omni / --trust-remote-code / --num-weight-load-threads 2 / --enforce-eager / TORCH_SDPA / quant config / --force-cutlass-fp8 / 两个超时

踩坑 1 · 前置检查

坑 1:先查清楚,再启动——preflight 的意义

# preflight.sh 逐项检查,一项不过就 exit 1
[[ -f .env ]]                || fail "copy .env.example"
MINIMAX_H3_LICENSE_ACKNOWLEDGED=true  # 许可承认
ARCH=="aarch64"              || fail "架构不符"
[[ -f model_index.json ]]    || fail "模型目录缺文件"
MemAvailable >= 105 GiB      || fail "内存不够"

为什么是坑: 冷启动要 9 分钟、占 105GiB 内存—— 如果 license 没承认、架构不对、内存不够,白等九分钟才发现 。preflight 把这些"起不来的原因"全部提前。
- 检查清单 · · .env 存在 + license 承认 · docker / compose v2 / nvidia-smi · aarch64 架构 · 模型目录结构(model_index + transformer) · ≥105 GiB 可用内存 · 端口未占用 · API key 时 .env 权限 600

💡 本节要点:坑 1:冷启动前置条件——105GiB 可用内存门槛、license 承认、aarch64 架构校验、模型目录结构检查——preflight 把'起不来的原因'提前到启动前

踩坑 2 · 网络策略

坑 2:默认只对"本机"说话——fail-closed 网络

# 安全默认:只监听回环
BIND_HOST="${H3_BIND_HOST:-127.0.0.1}"
h3_validate_network_security \
  "$BIND_HOST" "$ALLOW_REMOTE" "$API_KEY" \
  || fail "network policy rejected"

# 远程访问是显式 opt-in:
#   H3_BIND_HOST=0.0.0.0
#   H3_ALLOW_REMOTE_API=true
#   VLLM_API_KEY=强密钥
#   .env 权限必须 600(组/其他不可读)

为什么是坑: 一个跑着 33B 参数生成模型、占用 92GB 内存的服务, 如果默认监听全网卡,等于把生成能力裸奔在网络上 。
设计: fail-closed:默认只绑 127.0.0.1;要远程,必须同时满足 三个条件 (非回环绑定 + 显式远程开关 + 强 API key),并且 API key 存在时 .env 权限必须 600。
- 原则 · 安全默认是"关",打开是显式行为,且打开有打开的门槛。

💡 本节要点:坑 2:网络策略 fail-closed——默认只绑 127.0.0.1;远程访问必须显式开启 + 强 API key + .env chmod 600;非回环绑定需要双重确认

踩坑 3 · 验证链路

坑 3:产物不验证,就不算生成成功

POST /v1/videos/sync smoke-t2va.sh 响应存 .part → HTTP 2xx? 非 2xx → 存 error.json exit 1 → ffprobe 确认容器? 非媒体 → 存 .invalid exit 1 → 转正 + verify-output mv .part → 正式文件 流/解码/音频/校验
- 📝 · HTTP 2xx · 模型可能返回 JSON 错误——先看状态码
- 🎞️ · ffprobe 容器 · 200 也可能是错误体——确认是媒体容器
- ✅ · 全量校验 · 流 / 全解码 / 音频 / 校验和——全过才转正

💡 本节要点:坑 3:验收请求的 fail-closed——响应先存 .part:HTTP 非 2xx → 存 error.json 退出;ffprobe 确认媒体容器 → 才转正;再跑 verify-output.sh 全量校验

复现路径

从克隆到出片:六条 make 命令

$ git clone https://github.com/joeynyc/MiniMax-H3-DGX-Spark.git
$ cd MiniMax-H3-DGX-Spark && cp .env.example .env
$ vim .env        # checkpoint 路径 / HF 缓存 / license 承认
$ make preflight  # ① 前置检查全过
$ make build      # ② 基于固定 digest 构建镜像
$ make up         # ③ compose 启动(冷启动约 9 分钟)
$ make status     # ④ 等两个 check 都过
$ make smoke      # ⑤ 验收请求(fail-closed)
$ make verify     # ⑥ 产物全量校验

可复现的三支柱: 固定 digest (镜像不变)· .env 注入 (无宿主路径)· 全链路重验 (改动 = 新配置,从冷启动重新走一遍)。

💡 本节要点:复现路径:clone → .env → make preflight → build → up → status(等两个 check)→ smoke → verify;冷启动约 9 分钟;任何改动都从冷启动重验

验证

端到端数字:这条流水线真的跑通了

环节 实测
前置检查 preflight 全过(架构 / 内存 / 模型目录 / 网络策略)
镜像构建 固定 digest 基镜像 + 4 个 COPY 覆盖
模型加载 13/13 shard,89.17 GiB,532s
验收请求 HTTP 200,服务端 154.9s,20 步扩散
产物 768×448 / 24fps / H.264 + AAC 立体声 / 全解码通过
回归测试 5/5 通过(精确基镜像内)
诚实声明: 镜像内 vLLM 与 vLLM-Omni 版本不一致警告未压制—— 该组合实测通过,但警告保留 ;仓库不含模型权重与生成媒体;模型社区许可有地域限制,使用前必须确认授权。
> 💡 本节要点:验证:端到端全链路数字——HTTP 200 / 服务端 154.9s / 加载 89.17GB/532s / 请求后 92,957 MiB / 产物 768×448 H.264+AAC 全解码通过 / 5 测试通过

总结

部署的本质:可复现 + 可验证

—— 镜像不变,跑法不变,复现的第一前提
固定 digest
—— 4 个 COPY 完成兼容层,不重打包整个框架
文件级补丁
—— 前几期的每个坑,都变成了一个启动参数
参数沉淀决策
—— preflight 前置、网络默认关闭、产物逐步转正
fail-closed 验证

流水线跑通了—— 可它到底多快、多省?下一期,六期系列的收尾:性能与方法论
- 🧩 · EP33 · 失败链与补丁 · 六连坑 + 窄补丁 ✅
- 🚀 · EP34 · 部署实战 · 本期:可复现流水线 ✅
- 📊 · EP35 · 性能与方法论 · 下期:系列收尾
本期证据:Dockerfile · compose.yaml · start-fp8.sh · scripts/preflight.sh · scripts/smoke-t2va.sh · scripts/verify-output.sh · README.md(复现路径)· docs/RESULTS.md
💡 本节要点:总结:部署 = 把'能跑'变成'可复现 + 可验证'——固定 digest、文件级补丁、参数沉淀决策、fail-closed 验证链;下期 EP35:性能与方法论(收尾)

← 失败链与兼容补丁:六个坑,五次换路,没有一次白踩 性能与方法论:134GB 跑在 128GB 上的六期复盘 →