# MiniMax H3 FL2VA 视频生成服务部署总结

> 平台:Ascend 910B4-1 ×8(64G HBM/卡)· 单机 754G RAM

> 框架:vllm-ascend v0.26.0 + vllm-omni(Hunyuan-3D-2.0-omni)

> 用途:FL2VA 文生视频/图生视频,支持 1~3 张参考图关键帧,最长 12s

> 日期:2026-08-11

---

## 目录

1. [整体架构](#1-整体架构)

2. [硬件与软件环境](#2-硬件与软件环境)

3. [下载与准备](#3-下载与准备)

4. [容器部署](#4-容器部署)

5. [环境安装](#5-环境安装)

6. [代码改动(关键)](#6-代码改动关键)

7. [服务启动](#7-服务启动)

8. [推理调用](#8-推理调用)

9. [性能与资源数据](#9-性能与资源数据)

10. [故障排查手册](#10-故障排查手册)

11. [运维经验教训](#11-运维经验教训)

---

## 1. 整体架构

```

宿主机 (754G RAM, 8×910B4-1 64G HBM)

└─ Docker 容器 h3-vllm  (quay.io/atlas-ci/vllm-ascend:v0.26.0)

   ├─ APIServer          FastAPI 监听 :9098  (路由 /v1/videos/sync)

   ├─ 4×DiffusionWorker  H3 50层 diffusion transformer, tp=4

   │                     (每 worker: ~80G RAM + ~47G HBM)

   ├─ 4×EngineCore       VAE tile 模式解码

   └─ text encoder       Qwen3-VL 50层, tp=4 (replicated vision)

挂载关系:

  /usr/local/Ascend/driver      → /usr/local/Ascend/driver

  /usr/local/Ascend/firmware    → /usr/local/Ascend/firmware

  /usr/local/sbin               → /usr/local/sbin

  /etc/ascend_install.info      → /etc/ascend_install.info

  /data1/fl2va                  → /workspace/models   (模型, 135G)

  /data1/h3work                 → /workspace          (工作区, 92G)

启动参数 (tp=4, usp=4, ring=1):

  --omni --task-type fl2va --num-gpus 4 --usp 4 --ring 1

  --text-encoder-tp-size 4 --enable-distributed-layerwise-offload

  --vae-parallel-mode tile --vae-use-tiling --vae-patch-parallel-size 4

  --diffusion-attention-backend FLASH_ATTN --load-format mmap

```

**进程模型**:vllm-omni 多进程(APIServer + 4 DiffusionWorker + 4 EngineCore + orchestrator),

属于 inline 串行执行(stage_runtime 单 stage 顺序调度)。

---

## 2. 硬件与软件环境

| 项 | 值 |

|---|---|

| NPU | 华为昇腾 910B4-1 ×8,每卡 HBM 65536 MB |

| CPU/RAM | 754G 总内存 |

| 磁盘 | /data1 NVMe(2.3T 可用),/tmp 为 tmpfs(不可放大文件) |

| 容器镜像 | quay.io/atlas-ci/vllm-ascend:v0.26.0 |

| Python | 容器内 /usr/local/python3.12.13 |

| 关键依赖 | vllm 0.26.0(site-packages)、vllm-omni 0.1.dev1(editable)、mindiesd(wheel)、ffmpeg |

| 驱动 | npu-smi 25.5.2 |

**NPU 卡资源分配**(8 卡共用,注意 NPU ID 与 ASCEND_RT_VISIBLE_DEVICES 的映射):

```

npu-smi info 中 NPU 2/3/4/5 → ASCEND_RT_VISIBLE_DEVICES=0,1,2,3(服务用)

NPU 6/7 被其他进程占用 (~53.5G/卡)

```

---

## 3. 下载与准备

### 3.1 模型下载(135G)

- 位置:`/data1/fl2va/FL2VA/`(含 `model_index.json`,全套 bf16)

- 组成:Qwen3-VL text encoder(50层)+ H3 diffusion transformer(50层)+ VAE + tokenizer 等

- 注意:**模型务必放 /data1(NVMe)**,放 /tmp(tmpfs)会把 754G 内存吃掉 135G,导致服务加载时 OOM 崩溃

### 3.2 框架源码

```bash

# vllm-omni(推理管线主体,代码改动都在这)

git clone https://github.com/AIS-Hackathon/Hunyuan-3D-2.0-omni.git  # → /data1/h3work/vllm-omni

# MindIE-SD wheel(VAE/算子库)

# → /data1/h3work/MindIE-SD/dist/mindiesd-*.whl

```

### 3.3 目录速查(/data1/h3work = 容器 /workspace)

| 路径 | 用途 |

|---|---|

| `start_h3.sh` | 服务启动脚本(env + vllm serve 参数) |

| `setup_env2.sh` / `setup_env2.log` | 环境安装脚本(vllm-omni editable + mindiesd + ffmpeg) |

| `h3_server.log` | 服务日志(重启前 `rm -f`) |

| `run_full.sh` | 完整 12s 视频请求脚本(curl 封装) |

| `refs/` | 参考图(分镜1.jpg / 角色1.jpg / 角色2.jpg) |

| `video_full_12s.mp4` | 生成产物 |

| `vllm-omni/` | 源码(editable 安装) |

---

## 4. 容器部署

```bash

docker run -itd --name h3-vllm --privileged --security-opt seccomp=unconfined --network host --shm-size=64g \

  -v /usr/local/Ascend/driver:/usr/local/Ascend/driver \

  -v /usr/local/Ascend/firmware:/usr/local/Ascend/firmware \

  -v /usr/local/sbin:/usr/local/sbin \

  -v /etc/ascend_install.info:/etc/ascend_install.info \

  -v /data1/fl2va:/workspace/models \

  -v /data1/h3work:/workspace \

  quay.io/atlas-ci/vllm-ascend:v0.26.0 bash

```

> ⚠️ 必须 `--privileged`(NPU 设备访问)+ `--shm-size=64g`(进程间共享内存)+ `--network host`(9098 端口直通)

>

> ⚠️ 重建容器(docker rm -f + run)会丢失:

> - pip 安装的 vllm-omni / mindiesd(需重跑 setup_env2.sh)

> - `/vllm-workspace/vllm` 目录内容(镜像 VOLUME 为空卷,需重建占位文件,见 10.4)

> - /workspace/refs 参考图(需重新 docker cp)

---

## 5. 环境安装

容器内执行(重建容器后必跑):

```bash

cd /workspace && bash setup_env2.sh

# 内容: pip install -e vllm-omni  +  pip install mindiesd wheel  +  ffmpeg 等

```

**重要**:

- setup_env2.sh 里 `patch_vllm.py` 步骤已删除(原作用是在 vllm weight_utils.py 注入 flock 网络锁防并发加载——现已不需要,且重跑会加回锁导致问题)

- 验证:`python -c "import mindiesd"`、`pip show vllm-omni` 有版本号

---

## 6. 代码改动(关键)

> 目标:让 FL2VA 支持 **3 张参考图关键帧**(原版只允许 1~2 张首/末帧)。

> 改动文件均在 `/workspace/vllm-omni/vllm_omni/diffusion/models/minimax_h3/`

### 6.1 pipeline_minimax_h3.py —— 放开 3 图计数检查(3 处)

```python

# ① 第 ~1593 行(fl2va 分支):

if len(images) > 2:

    raise OmniClientError("fl2va accepts at most first and last images")

# 改为:

if len(images) > 3:

    raise OmniClientError("fl2va accepts at most 3 keyframes")

# ② 第 ~1622 行(count 分支):

if count > 2:

    raise OmniClientError(...)

# 改为:

if count > 3:

    raise OmniClientError(...)

# ③ 第 ~1630 行(keyframe count check):

elif len(images) > 2:

    raise OmniClientError(f"... keyframe count check ...")

# 改为:

elif len(images) > 3:

    raise OmniClientError(f"... keyframe count check ...")

```

> `_resolve_fl2va_keyframe_indices()`(第 ~295 行)无需改:要求

> `len(frame_indices) == 图片数` 即放行。

### 6.2 packed_sequence.py —— 关键帧布局支持中间帧

```python

# ① 修复关键帧顺序被打乱(原 sorted(set()) 会把 [0,144,-1] 排成 [-1,0,144],

#    导致参考图与帧位置错位!)

def _keyframe_cond_frame_indices(...):

    ...

    return list(dict.fromkeys(keyframe_frame_indices))   # 保序去重

# ② 中间帧时间位置改为线性插值(原实现只支持首帧/末帧锚点)

#    在 minimax_h3_packed_sequence() 的 cond_t 循环 else 分支:

else:

    denom = (frame_count - 1) if frame_count else 1

    frac = pixel_index / denom

    cond_t = float(text_len) + frac * (_temporal_position_span(latent_t) - _FRAME_RESCALE)

```

> `MINIMAX_H3_FL2VA_KEYFRAME_SIGNATURES` 常量((0,), (-1,), (0,-1))虽存在但未被引用,无需处理。

### 6.3 其他历史改动(已回退/不需要)

| 改动 | 状态 |

|---|---|

| weight_utils.py 注入 flock 网络锁(patch_vllm.py) | 已回退,不再需要 |

| stage_init_utils / stage_runtime mmap 注入 | 已改为启动参数 `--load-format mmap` 直传 |

| stage_runtime inline 串行执行 | 保持默认即可 |

---

## 7. 服务启动

### 7.1 启动脚本 start_h3.sh

```bash

#!/bin/bash

set -e

export ASCEND_RT_VISIBLE_DEVICES=0,1,2,3

export PORT=${PORT:-9098}

export MODEL=/workspace/models/FL2VA

export VLLM_WORKER_MULTIPROC_METHOD=spawn

export VLLM_OMNI_VIDEO_SYNC_TIMEOUT=5400     # ⚠️ 必须 ≥5400,默认1800会掐断27分钟生成

export PYTHONDONTWRITEBYTECODE=1

export HF_HUB_OFFLINE=1

export TRANSFORMERS_OFFLINE=1

export PATH=/usr/local/python3.12.13/bin:$PATH

exec vllm serve "${MODEL}" \

  --load-format mmap \

  --omni --task-type fl2va --num-gpus 4 --usp 4 --ring 1 \

  --text-encoder-tp-size 4 --enable-distributed-layerwise-offload \

  --vae-parallel-mode tile --vae-use-tiling --vae-patch-parallel-size 4 \

  --diffusion-attention-backend FLASH_ATTN

```

### 7.2 启动与就绪探测

```bash

# 正确后台方式(关键!nohup & 会被 exec 会话回收,导致服务"假启动")

docker exec h3-vllm bash -c "rm -f /workspace/h3_server.log"

docker exec -d h3-vllm bash -c 'bash /workspace/start_h3.sh > /workspace/h3_server.log 2>&1'

# 就绪探测(用 /v1/models,health 有时输出异常)

curl -s -m 5 http://127.0.0.1:9098/v1/models   # 返回含 "object" 即就绪

# 重启

docker exec h3-vllm bash -c "pkill -9 -f 'vllm serve'; sleep 2; rm -f /workspace/h3_server.log"

```

### 7.3 状态检查

```bash

docker exec h3-vllm bash -c "tail -20 /workspace/h3_server.log"   # 日志

free -g                                                           # RAM

docker exec h3-vllm npu-smi info                                  # HBM

docker exec h3-vllm bash -c "ps aux --sort=-rss | grep '[p]ython3'"  # worker RSS

```

---

## 8. 推理调用

### 8.1 完整 12s 视频(3 张参考图关键帧)

```bash

curl -sS -m 7200 -o video_full_12s.mp4 http://127.0.0.1:9098/v1/videos/sync \

  -F "prompt=<完整分镜脚本>" \

  -F 'aspect_ratio=16:9' \

  -F 'seconds=12' -F 'fps=24' \

  -F 'num_inference_steps=25' \

  -F 'flow_shift=12' \

  -F 'task=fl2va' \

  -F 'extra_params={"frame_indices":[0,144,-1]}' \

  -F input_references=@分镜1.jpg \

  -F input_references=@角色1.jpg \

  -F input_references=@角色2.jpg

```

### 8.2 关键帧规则

| 图数量 | frame_indices | 含义 |

|---|---|---|

| 1 | `[0]` | 仅首帧锚定 |

| 2 | `[0,-1]` | 首帧 + 末帧 |

| 3 | `[0,N,-1]` | 首帧 + 中间帧(12s/288帧 时 N=144=8s 处)+ 末帧 |

- `-1` 表示最后一帧;中间帧必须落在 `[0, 帧数)`,各索引不得重复

- 图片顺序与索引顺序一一对应(分镜1→帧0,角色1→帧144,角色2→末帧)

### 8.3 限制与产物

- **最长 12 秒**(`seconds>12` 直接报错;脚本 16s 需裁剪为 12s)

- 产物:1376×768 / 24fps / 12.3s / H.264+AAC 音轨 / ~13MB

- 取回:`docker cp h3-vllm:/workspace/video_full_12s.mp4 <本地路径>`

---

## 9. 性能与资源数据

| 指标 | 数值 |

|---|---|

| 启动就绪 | 4 分钟(页缓存热)~ 22 分钟(冷启动) |

| 生成耗时 | ~64s/步;12s/25步 ≈ **27.5 分钟** |

| HBM 稳态 | 4×46.9G/64G(71.6%) |

| HBM 峰值 | 12s 16:9 激活 +35.2G/卡 → 临界,**偶发 OOM,重试即成功** |

| RAM 稳态 | ~665G/754G(4 worker RSS ~320G + 页缓存 ~94G) |

| RAM 加载峰值 | ~630G |

**显存/内存为什么都占着(常见疑问)**:

- 模型权重(~330G bf16 全套)**双份驻留**:

  - HBM:4×47G ≈ 187G(推理执行副本)

  - RAM:4 worker 各 ~80G ≈ 320G(vllm-ascend DLO 按层卸载机制的 CPU 后备副本,进程退出前不可释放)

- 另有 ~94G 可回收页缓存(mmap 读权重产生)

- 真正瓶颈是 **HBM**(激活 35G 逼近 64G),RAM 是设计冗余

---

## 10. 故障排查手册

### 10.1 服务起不来 / 启动即崩(procs=0)

```bash

docker exec h3-vllm bash -c "grep -aE 'ERROR|Traceback|corrupted|HCCL|dead|Exit code' /workspace/h3_server.log | tail"

```

- `corrupted size vs. prev_size` + `scheduler is dead (Exit -6/-11)` = HCCL/内存问题:

  - 先看 `free -g`:avail < 500G 时权重加载必崩(tmpfs 被占)

  - 偶发 → 重试 1~2 次

- 日志只有几行就停 = `vllm` 命令找不到 → 见 10.4

### 10.2 服务"假启动"(procs=1 但 api 不通)

- 用 `docker exec -d` 启动,**不要** `nohup bash ... &`(进程随 exec 会话被杀)

- 启动后日志无内容 → 检查 `/vllm-workspace` 占位(10.4)

### 10.3 生成报错

| 错误 | 原因 / 处理 |

|---|---|

| `at most 2 image reference frames` / `at most first and last` / `keyframe count check` | 计数检查未放开 → 改 pipeline_minimax_h3.py 3 处(6.1) |

| `packed layout only supports first/last keyframe anchors, got ...` | packed_sequence 中间帧插值未加(6.2②) |

| `frame index ... already bound` | frame_indices 有重复/越界 |

| `target.seconds must be <= 12` | seconds 超上限 |

| `NPU out of memory ... 35.21 GiB` | 12s 16:9 临界,**直接重试**(OOM 后 allocator 碎片清理,第二次常成功);仍失败则降 1:1 或 9s |

| `CancelledError` / 空错误(进度 46% 被断) | `VLLM_OMNI_VIDEO_SYNC_TIMEOUT` 太小 → 改 5400 重启 |

### 10.4 重建容器后 `vllm` 命令 No such file

- `/vllm-workspace/vllm` 是镜像 VOLUME,重建后为空 → driver.py:191 检查失败:

```bash

docker exec h3-vllm bash -c "mkdir -p /vllm-workspace/vllm/vllm/entrypoints/cli && touch /vllm-workspace/vllm/vllm/entrypoints/cli/main.py"

```

(vllm 本体在 site-packages,命令存在于 /usr/local/python3.12.13/bin/vllm)

### 10.5 内存不足

- 删除 /tmp 下大文件(模型 135G、工作区 92G、10G 日志):

```bash

rm -rf /tmp/mediamodels /tmp/h3work/setup_env.log   # tmpfs 释放 → avail 回升

```

- 工作区/模型常驻 /data1(NVMe)

---

## 11. 运维经验教训

1. **tmpfs 陷阱**:/tmp 是 tmpfs(内存盘)。模型/工作区/大日志放 /tmp = 直接吃 RAM。135G 模型放 tmpfs 会让 avail 从 630G 掉到 ~120G → 服务必崩。大文件一律放 /data1。

2. **后台进程**:容器内长任务必须 `docker exec -d`,nohup 不可靠。

3. **同步超时**:`VLLM_OMNI_VIDEO_SYNC_TIMEOUT` 按生成时长设(25步 ≈ 30 分钟 → 5400s)。

4. **OOM 重试机制**:16:9 12s 的激活峰值 35.2G 是 NPU 上限边缘,首次失败重试即可;若频繁失败用 1:1(768²)或 9s。

5. **改动生效需重启**:pipeline/packed_sequence 代码在 worker 进程内加载,改完必须重启服务。

6. **保存改动源**:所有代码改动在 /data1/h3work/vllm-omni(磁盘持久),重建容器只丢 pip 安装不丢源码。

7. **就绪判断**:用 `/v1/models`(返回 object),不要用 `/health`(该环境输出有异常)。

Logo

鲲鹏昇腾开发者社区是面向全社会开放的“联接全球计算开发者,聚合华为+生态”的社区,内容涵盖鲲鹏、昇腾资源,帮助开发者快速获取所需的知识、经验、软件、工具、算力,支撑开发者易学、好用、成功,成为核心开发者。

更多推荐