部署实战 从克隆到出片,一条可复现的流水线
T AGENT 开发纪实 · EP34 · MiniMax-H3 六期原理课 ⑤
前五期讲完了原理、量化、硬件、补丁——这一期把它们装进一条能跑的流水线:镜像怎么构建、补丁怎么进去、每个启动参数是干嘛的、怎么验证没跑偏。
个 Dockerfile
1
个启动参数
8
个 make 命令
6
冷启动
9min
💡 本节要点:EP34:部署实战——从克隆到出片的一条可复现流水线:固定 digest 镜像 + 文件级补丁覆盖 + 在线动态 FP8 + OpenAI 兼容 API + fail-closed 验证链路
起点问题
四个问题:一条流水线由什么组成?
- Q1 · 镜像哪来的? · 不是随便 pull 一个 vLLM-Omni,是固定 digest 的 ARM64 镜像——可复现的第一前提。
- Q2 · 补丁怎么进去? · Dockerfile 把补丁文件直接覆盖进 dist-packages——兼容层以"文件级覆盖"交付。
- Q3 · 启动参数是干嘛的? · 八个参数,每个都对应一个 SM121 规避项或内存控制——一个都不能删。
- Q4 · 怎么验证没跑偏? · fail-closed 验证链:HTTP 状态、媒体容器、ffprobe 全解码——不满足就不转正。
部署的本质: 把"能跑"变成"别人也能跑、还能证明跑对了"
💡 本节要点:起点问题:镜像哪来的?补丁怎么进去的?每个启动参数是干嘛的?怎么验证没跑偏?——四个问题对应部署的四层:镜像、注入、启动、验收
流水线全景
兼容层的完整形态:一条单向流水线
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:性能与方法论(收尾)