Files
2026-06-23 18:22:32 +08:00

587 lines
25 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Docker 构建问题汇总
> 目标:Ubuntu 24.04 + ROCm 7.2.1 + vllm + MinerU Docker 镜像
> 环境:原生 Linux + RX 9070 (gfx1201) + Privoxy (127.0.0.1:8118)
---
## 1. 网络类
### 1.1 Docker Hub 拉基础镜像超时
**现象**:`dial tcp [2a03:2880:...]:443 i/o timeout`
**原因**:`docker.io` 被墙,IPv6 直连超时
**解决**:Docker daemon 配置代理 + 国内镜像加速器
```bash
# /etc/systemd/system/docker.service.d/http-proxy.conf
Environment="HTTP_PROXY=http://127.0.0.1:8118"
Environment="HTTPS_PROXY=http://127.0.0.1:8118"
# /etc/docker/daemon.json
{
"registry-mirrors": ["https://docker.1panel.live", ...],
"ipv6": false
}
```
### 1.2 容器内无法访问国内镜像
**现象**:`Could not resolve 'mirrors.tuna.tsinghua.edu.cn'`
**原因**:Docker 默认 bridge 网络无法路由到外网
**解决**:`docker-compose.yml` 构建时加 `network: host`(非代理,只是共享宿主机网络栈)
### 1.3 GitHub 直连失败
**现象**:`git clone github.com` → `GnuTLS recv error` / `TLS connection non-properly terminated`
**原因**:GitHub 在国内不通,无可靠国内镜像
**解决**:git/wget 通过 Privoxy 代理访问 GitHub(**仅 GitHub**,其他源用国内镜像)
```dockerfile
ARG GIT_PROXY=http://127.0.0.1:8118
git config --global http.proxy ${GIT_PROXY}
# wget: export http_proxy=${GIT_PROXY}
```
### 1.4 git clone 代理断连
**现象**:`RPC failed; curl 92 HTTP/2 stream 5 was not closed cleanly: CANCEL`
**原因**:HTTP/2 通过代理传大仓库不稳定
**解决**:禁用 HTTP/2 + 加大 buffer
```bash
git config --global http.version HTTP/1.1
git config --global http.postBuffer 524288000
```
### 1.5 PyTorch ROCm wheels 下载中断
**现象**:`IncompleteRead` (pip 下载 .whl 到一半断开)
**原因**:PyTorch 官方 CDN (`download.pytorch.org`) 在国内直连不稳定
**解决**:下载 PyTorch 时设环境变量走代理
```bash
export http_proxy=http://127.0.0.1:8118 https_proxy=http://127.0.0.1:8118
pip install ... --pre torch==2.11.0+rocm7.2 ...
```
### 1.6 flash-attention 子模块代理递归断连
**现象**:`--recursive` 克隆 flash-attention 时子模块 `cutlass` (~200MB) 下载失败:`RPC failed; curl 56 GnuTLS recv error`
**原因**:3 个子模块(composable_kernel + cutlass + aiter)挤在一条代理链路上,大文件容易丢包
**解决**(可选):宿主机预克隆后 COPY 入镜像。或用 `--depth 1` + 逐个 `git submodule update --init --depth 1`(断了一个可单独重试)
---
## 2. Dockerfile 语法类
### 2.1 ENV 行内注释导致解析失败
**现象**:`Syntax error - can't find = in "\ "`,行号为 `HSA_ENABLE_SDMA=1 \ # 注释`
**原因**:Docker ENV 不允许行内 `#` 注释
**解决**:删除行内注释,或移到单独 `#` 行
### 2.2 多行 `python -c "..."` 被误解析
**现象**:`dockerfile parse error: unknown instruction: import` / `unknown instruction: from`
**原因**:Docker 解析器把 Python `-c` 的多行字符串当作 Dockerfile 指令(`from x import y` → 误认为 `FROM`;`import x` → 误认为未知指令)
**解决**:复杂 Python 代码写成独立脚本,用 `COPY` 进入镜像后 `python /opt/xxx.py` 执行
涉及的文件:
- `scripts/apply_mineru_patches.py` — MinerU RDNA 适配补丁
- `scripts/patch_vllm_platform.py` — vllm 平台检测补丁
---
## 3. ROCm 兼容性类
### 3.1 版本号锁定失效
**现象**:`Version '1.0.0.70201-38~24.04' for 'rocminfo' was not found`
**原因**:AMD 仓库更新后精确版本号过期
**解决**:改用 apt pinning 策略,不锁死版本号
```bash
# /etc/apt/preferences.d/rocm-pin-600
Package: *
Pin: release o=repo.radeon.com
Pin-Priority: 600
```
### 3.2 hipcc.pl 符号链接创建损坏链接
**现象**:cmake 报 `Can't find CUDA or HIP installation`(PyTorch 的 `LoadHIP.cmake:179`)
**原因**:`ln -sf /usr/bin/hipcc.pl /opt/rocm/bin/hipcc`,`/usr/bin/hipcc.pl` 在原生 Linux 上不存在,创建了损坏的符号链接
**解决**:条件创建,源文件不存在时跳过
```bash
([ -f /usr/bin/hipcc.pl ] && ln -sf /usr/bin/hipcc.pl /opt/rocm/bin/hipcc) || echo "hipcc.pl not found"
```
### 3.3 缺 rocm-cmake 导致 find_package(HIP) 失败
**现象**:PyTorch `LoadHIP.cmake:179` 调用 `find_package_and_print_version` 找不到 HIP
**原因**:未安装 `rocm-cmake` 包,缺少 HIP cmake 发现模块
**解决**:在阶段 6 加装 `rocm-cmake`
### 3.4 vllm mamba operator+ 补丁失效
**现象**:`sed: can't read csrc/mamba/mamba_ssm/selective_scan.h: No such file or directory`
**原因**:vllm 上游已移除 mamba 模块
**解决**:改为条件判断,文件不存在就跳过
```bash
if [ -f csrc/mamba/mamba_ssm/selective_scan.h ]; then sed ...; else echo "skipping"; fi
```
### 3.5 原生 Linux 需要 amdsmi + 平台检测补丁
**现象**(社区反馈):`ModuleNotFoundError: amdsmi` → `current_platform = UnspecifiedPlatform` → `Device string must not be empty`
**原因**:amdsmi 未安装,且 `logger.warning_once()` 触发 vllm 循环导入
**解决**:
1. 安装 amdsmi(原生 Linux 上可用,WSL2 不需要)
2. 补丁 6:`platforms/__init__.py` 加 `torch.version.hip` 兜底
3. 补丁 7:`platforms/rocm.py` 中 `logger.warning_once()` → `sys.stderr.write()`
### 3.6 缺 hip_version.h 头文件
**现象**:`Could not find hip/hip_version.h or rocm-core/rocm_version.h`
**原因**:ROCm 7.2.1 的 apt 安装已将 `hip_version.h` 从默认路径移除,且 `rocm-core` 包未安装
**解决**:
1. 安装 `rocm-core` 包(提供 `rocm_version.h`)
2. 手动创建 `/opt/rocm/include/hip/hip_version.h` 兼容头文件
```bash
apt-get install -y rocm-core
# 兜底:手动生成 hip_version.h
printf '#define HIP_VERSION_MAJOR 7\n#define HIP_VERSION_MINOR 2\n...'
```
### 3.7 cmake 需显式设 HIP 编译器路径
**现象**:仅设 `PATH` + `HIP_ROOT_DIR` + `ROCM_PATH` 不足以让 cmake 发现 HIP
**解决**:显式指定
```
-DCMAKE_HIP_COMPILER=/opt/rocm/llvm/bin/clang++ # (非 hipcc,CMake 4.0 拒绝)
-DHIP_COMPILER=/opt/rocm/llvm/bin/clang++ # PyTorch LoadHIP 也需要
-DHIP_PATH=/opt/rocm
-DCMAKE_PREFIX_PATH="/opt/rocm;..."
```
### 3.8 PyTorch Caffe2Targets 缺失 cmake target(Docker 容器特有)
**现象**:cmake 反复报 `hip::amdhip64` / `hiprtc::hiprtc` / `roc::hipblas` / `roc::hipblaslt` 等 target 未找到
**原因**:裸机部署时 ROCm 的 cmake 配置通过 `/opt/rocm` 完整安装,cmake 能自动发现所有 target。但 Docker 容器内 apt 安装的 dev 包缺少部分 cmake config 文件,PyTorch 的 `Caffe2Targets.cmake` 硬编码引用了这些 target
**涉及 target 及修复**:
| 报错 target | 命名空间问题 | 最终 alias |
|:--|:--|:--|
| `hip::amdhip64` | 普通 target | SHARED IMPORTED → `libamdhip64.so` |
| `hiprtc::hiprtc` | 需双冒号命名空间 | `hiprtc::hiprtc` ALIAS `hip::hiprtc` |
| `roc::hipblas` | roc:: 而非 hip:: | `roc::hipblas` ALIAS `roc::rocblas` |
| `roc::hipblaslt` | 同上 | `roc::hipblaslt` ALIAS `hip::hipblaslt` |
| `roc::hiprand` / `roc::hipsolver` 等 | 同上 | 各指向对应 `roc::roc*` target |
**注意**:`roc::hip*` → 应直接指向底层 `roc::roc*`(不能通过 `hip::hip*` 中转,因为后者本身是 ALIAS,CMake 不允许链式 ALIAS)
### 3.9 rocm-core-dev → rocm-core 包名错误
**现象**:`E: Unable to locate package rocm-core-dev`
**原因**:ROCm 7.2.1 仓库中的包名为 `rocm-core`(无 -dev 后缀)
**解决**:`s/rocm-core-dev/rocm-core/`
### 3.10 -lMIOpen 链接失败
**现象**:`/usr/bin/ld: cannot find -lMIOpen`
**原因**:miopen cmake config 使用了 `INTERFACE IMPORTED`(无实际 .so 路径),链接器找不到 `libMIOpen.so`
**解决**:改为 `SHARED IMPORTED` + 指定 `IMPORTED_LOCATION`,并检查版本化目录的符号链接
```cmake
# 错误
add_library(miopen INTERFACE IMPORTED)
# 正确
add_library(miopen SHARED IMPORTED)
set_target_properties(miopen PROPERTIES
IMPORTED_LOCATION "/opt/rocm/lib/libMIOpen.so"
INTERFACE_INCLUDE_DIRECTORIES "/opt/rocm/include")
```
### 3.11 printf 参数计数错误
**现象**:cmake 报 `add_library` 在第 11 行语法错误(`hiprtc-config.cmake:11`)
**原因**:生成 cmake config 的 shell printf 格式串含 13 个 `%s`,但只传了 12 个参数,最后一行 `hip::%s` 未替换
**解决**:减少单行 printf 参数,复杂结构拆成独立循环
---
## 4. Docker 缓存策略
### 4.1 vllm 编译+验证同层导致缓存丢失
**现象**:ninja 编译 30 分钟成功后被后面验证失败拖垮,全部重来
**解决**:拆为 8a(编译)和 8b(安装+验证)
| 层 | 内容 | 缓存行为 |
|:--|:--|:--|
| 8a | git clone + cmake + ninja | 编译成功即缓存,**永不丢失** |
| 8b | pip install + PyTorch 修复 + 平台补丁 | 失败可重试,不影响 8a |
### 4.2 缓存失效条件
任一 RUN 层的指令或上下文变化都会导致该层及之后所有层重建。修改 `ARG`、`ENV`、`COPY` 文件内容均会触发。
### 4.3 COPY 脚本在 RUN 引用之后
**现象**:阶段 8b 执行 `python /opt/patch_vllm_platform.py` → 文件不存在
**原因**:`COPY scripts/patch_vllm_platform.py` 在阶段 8.5(原序号),但阶段 8b 已引用该脚本——Dockerfile 中 COPY 位于引用它的 RUN 之后
**解决**:将 COPY 指令移到所有需要脚本的 RUN 之前(阶段 7.5)。规则:**COPY 必须在引用它的 RUN 之前**
---
## 5. 网络策略总览
| 资源 | 访问方式 |
|:--|:--|
| Docker Hub | daemon 代理 + 国内镜像加速器 |
| Ubuntu apt | 清华镜像 `mirrors.tuna.tsinghua.edu.cn` |
| PyPI pip | 清华镜像 `pypi.tuna.tsinghua.edu.cn/simple` |
| AMD ROCm apt | `repo.radeon.com` 直连 |
| PyTorch ROCm wheels | `download.pytorch.org` 直连 |
| GitHub (git/wget) | **Privoxy 代理 127.0.0.1:8118**(唯一例外) |
---
## 6. 构建阶段类
### 6.1 阶段 8b `pip install -e .` 触发 cmake 配置失败
**现象**:阶段 8a(cmake + ninja 手动编译)成功,但阶段 8b 执行 `pip install -e . --no-build-isolation --no-deps` 时报错:
```
TorchConfig.cmake:62 (find_package)
→ CMakeLists.txt:95 (find_package)
→ Configuring incomplete, errors occurred!
error: failed-wheel-build-for-install
× Failed to build installable wheels for some pyproject.toml based projects
╰─> vllm
```
**排查过程**:
Dockerfile 设计为阶段 8a 手动执行 `cmake -S ... -B ... -G Ninja`(携带完整 `-D` 参数)编译 vllm C++ 扩展并复制 `.abi3.so` 文件,阶段 8b 再用 `pip install -e .` 注册 Python 包。但 `pip install -e .` 通过 vllm 的 setuptools 构建扩展**再次触发 cmake**,且 vllm 内部的 cmake 调用与手动 cmake 在以下方面不可控:
1. **尝试一(ENV 透传)**:在 Dockerfile `ENV` 块中添加 `CMAKE_PREFIX_PATH`、`CMAKE_HIP_COMPILER`、`ROCM_PATH` 等 8 个 cmake 环境变量,期望 pip 触发的 cmake 继承这些值。**无效**——vllm 的 `cmake_build_ext` 内部会独立构造 cmake 参数,不完全依赖环境变量。
2. 错误始终出现在 `TorchConfig.cmake:62` 调用 `find_package(Caffe2)` 时,Caffe2 targets 引用的 ROCm cmake 目标无法被 pip 触发的 cmake 解析。
**结论**:`pip install -e .` 触发的 cmake 构建过程与阶段 8a 的手动 cmake 不在同一个可控环境中,且阶段 8b 的 `pip install -e .` 本质上是**冗余的**——C++ 扩展已在阶段 8a 编译完成。
**最终解决**:**跳过 pip install -e .,手动创建 vllm editable package 元数据**。
核心思路:阶段 8a 负责所有 C++ 编译(`.abi3.so`),阶段 8b 只负责 Python 包注册(不触发 cmake)。
具体改动(Dockerfile 阶段 8b):
```bash
# 替换前(会触发 cmake,失败):
cd /opt/vllm && \
${VENV}/bin/pip install -e . --no-build-isolation --no-deps && \
# 替换后(手动创建 egg-info,不触发 cmake):
cd /opt/vllm && \
mkdir -p vllm.egg-info && \
cat > vllm.egg-info/PKG-INFO << 'VLLM_EOF' && \
Metadata-Version: 2.1
Name: vllm
Version: 0.0.0
Summary: A high-throughput and memory-efficient inference and serving engine for LLMs
VLLM_EOF
echo "vllm" > vllm.egg-info/top_level.txt && \
touch vllm.egg-info/dependency_links.txt && \
touch vllm.egg-info/requires.txt && \
find vllm -name "*.py" -type f 2>/dev/null | sort > vllm.egg-info/SOURCES.txt && \
echo "vllm egg-info created (cmake build skipped, C++ extensions from stage 8a)" && \
```
手动创建的 egg-info 包含以下文件:
| 文件 | 内容 | 用途 |
|------|------|------|
| `PKG-INFO` | `Name: vllm` + `Version: 0.0.0` | `pip freeze` / `importlib.metadata` 识别 |
| `top_level.txt` | `vllm` | 声明顶层包名 |
| `SOURCES.txt` | 所有 `.py` 文件列表 | 包文件索引 |
| `dependency_links.txt` | 空文件 | 依赖链接(无) |
| `requires.txt` | 空文件 | 运行依赖(由后续 pip install -r 安装) |
`import vllm` 的路径解析由现有的 `.pth` 文件保证:
```bash
echo "/opt/vllm" > ${VENV}/lib/python${PYTHON_VER}/site-packages/vllm.pth
```
**附:ENV 块新增的 cmake 变量(保留,对阶段 8a 和其他 cmake 调用有益)**:
```dockerfile
ENV ... \
CMAKE_PREFIX_PATH=/opt/rocm \
CMAKE_HIP_COMPILER=/opt/rocm/llvm/bin/clang++ \
HIP_COMPILER=/opt/rocm/llvm/bin/clang++ \
HIP_PATH=/opt/rocm \
HIP_ROOT_DIR=/opt/rocm \
HIP_INCLUDE_DIR=/opt/rocm/include \
HIP_PLATFORM=amd \
ROCM_PATH=/opt/rocm
```
**效果**:
- 阶段 8b 不再触发 cmake,消除了冗余编译(节省约 30 分钟)
- C++ 扩展只在阶段 8a 编译一次,职责清晰
- `pip list` / `pip freeze` 正常显示 vllm
- `import vllm` 在所有 Python 进程中正常工作
---
## 7. 运行时类
### 7.1 vllm `_version.py` 缺失导致 `InvalidVersion: 'dev'`
**现象**:镜像构建成功,WebUI 启动正常,但解析任务全部失败:
```
File ".../mineru/backend/vlm/utils.py", line 88, in set_default_gpu_memory_utilization
if version.parse(vllm_version) >= version.parse("0.11.0") and gpu_memory <= 8:
│ │ └ 'dev'
packaging.version.InvalidVersion: Invalid version: 'dev'
```
日志开头同时有信号:
```
/opt/vllm/vllm/__init__.py:7: RuntimeWarning: Failed to read commit hash:
No module named 'vllm._version'
vllm runtime import OK: dev, platform=UnspecifiedPlatform
```
**原因**:阶段 8b 跳过了 `pip install -e .`(见 6.1 节),而 `setuptools_scm` 正是在 `pip install -e .` 时生成 `vllm/_version.py`。该文件缺失后,vllm 的 `version.py` 回退成 `__version__ = 'dev'`。`'dev'` 不是合法 PEP 440 版本号,mineru 调 `packaging.version.parse('dev')` 即抛 `InvalidVersion`,导致每个解析任务必失败。
**解决**:阶段 8b 手写 `vllm/_version.py`,替代 `setuptools_scm` 生成:
```dockerfile
# 阶段 8b(egg-info 创建之后):
printf '__version__ = "0.11.0"\n__version_tuple__ = (0, 11, 0)\n' > /opt/vllm/vllm/_version.py
```
同时在 `patch_vllm_platform.py` 的 `ensure_vllm_dist_info()` 中对 `vllm.__version__` 做 PEP 440 合法性校验,非法时回退安全值 `0.11.0` 并回写 `vllm.__version__`(兜底,防止上游 version.py 再改回退逻辑)。
**版本号选 `0.11.0` 的理由**:
- 合法 PEP 440,`packaging.version.parse` 不报错
- 满足 `mineru[vllm]` 的约束 `>=0.10.1.1,<0.22.0`
- mineru 代码 `version.parse(vllm_version) >= version.parse("0.11.0")` 走新分支(更合理的 GPU 显存策略)
**注意**:`mineru[core]`(Dockerfile 阶段 9 安装的)= vlm + pipeline + gradio,**不包含 `mineru[vllm]`**,因此 vllm 版本约束不会触发 pip 依赖冲突——editable 注册的假版本号只要功能上不被校验即可,而 `0.11.0` 恰好让校验逻辑走正确分支。
### 7.2 amdsmi 缺失导致 `Device string must not be empty`
**现象**:7.1 修复后(`vllm runtime import OK: 0.11.0`),解析任务在模型初始化阶段失败:
```
File ".../vllm/config/device.py", line 78, in __post_init__
self.device = torch.device(self.device_type)
RuntimeError: Device string must not be empty
```
启动日志里同时有:
```
WARNING [rocm.py:39] Failed to import from amdsmi: No module named 'amdsmi'
vllm runtime import OK: 0.11.0, platform=UnspecifiedPlatform
WARNING: vLLM platform detection returned UnspecifiedPlatform.
```
**原因**:vllm main 的 ROCm 平台检测依赖 `amdsmi`。检测链:
1. `resolve_current_platform_cls_qualname()` 遍历 `builtin_platform_plugins`,调用 `rocm_platform_plugin()`
2. `rocm_platform_plugin()` 内部 `import amdsmi` → 失败(except)→ `is_rocm=False` → 返回 `None`
3. 无任何 builtin plugin 激活 → `platform_cls_qualname = "vllm.platforms.interface.UnspecifiedPlatform"`
4. `current_platform.device_type = ''` → `torch.device('')` 抛 `Device string must not be empty`
`amdsmi` 缺失的根因:阶段 6.5 的条件安装 `if [ -d /opt/rocm/share/amd_smi ]`,**ROCm 7.2 的 apt 包不再提供该目录**(`pip list` 无 amdsmi、`/opt/rocm/share/amd_smi` 不存在、dpkg 无 amdsmi 包),安装被跳过。
> 注:`rocm_platform_plugin` 定义在 `platforms/__init__.py`(第 111 行附近),**不是** `platforms/rocm.py`。早期补丁脚本尝试 `from vllm.platforms.rocm import rocm_platform_plugin` 注册 entry_point,路径错误导致 `ImportError`——这是 entry_point 注册失败的根因,但本方案不依赖 entry_point,改走 builtin plugin 修复。
**解决**:在 `patch_vllm_platform.py` 的补丁 6 中,向 `rocm_platform_plugin()` 的 `return` 语句前注入 `torch.version.hip` 兜底:
```python
# __init__.py 中 rocm_platform_plugin 的 return 前:
if not is_rocm:
try:
import torch as _torch
if _torch.version.hip is not None:
is_rocm = True
except Exception:
pass
return "vllm.platforms.rocm.RocmPlatform" if is_rocm else None
```
amdsmi 缺失时 `is_rocm=False`,补丁用 `torch.version.hip` 翻转为 `True` → 返回 `RocmPlatform` → `device_type='cuda'` → 错误消失。
**关键实现细节**(补丁 6 多次失效的教训):
- **不能用精确字符串匹配**。vllm main 频繁重构,引号风格、空格、行结构都会变。早期补丁用 `old = " return 'vllm.platforms.rocm.RocmPlatform' if is_rocm else None"`(单引号)匹配,但实际文件是双引号,导致 `pattern not found`。
- **改用语义定位**:遍历行,找同时含 `RocmPlatform` + `return` + `is_rocm` 的行作为注入点,取该行缩进对齐。兼容单/双引号。
- 幂等:注入前检查文件内是否已有 `torch.version.hip is not None`,避免重复注入。
**验证**:
```bash
docker exec mineru-gradio /opt/mineru_venv/bin/python -c "
from vllm.platforms import current_platform
print('platform:', type(current_platform).__name__)
print('device_type:', repr(current_platform.device_type))
"
# 期望:platform: RocmPlatform / device_type: 'cuda'
```
**放弃的方案**:`sitecustomize.py` 运行时强制注入 `current_platform`。失败原因:`current_platform` 是 lazy init(首次访问才 resolve),sitecustomize 在 Python 启动最早期执行时触发提前 resolve,而那时补丁 6 尚未应用(或被 `except: pass` 吞错),赋值后可能被后续逻辑覆盖。改 builtin plugin 本体(补丁 6)才是 vllm 官方检测路径,最干净。
### 7.3 热修复与镜像固化的操作流程
容器运行中(不重建镜像)热修复时注意:
1. **`./scripts:/opt/scripts:ro` 是只读卷挂载**(见 `docker-compose.yml`)。`docker cp` 写 `/opt/scripts` 会报 `mounted volume is marked read-only`。但容器内 `/opt/scripts` 直接映射宿主机 `./scripts`,**改宿主机文件即生效**,无需 `docker cp`。
2. **`/opt/vllm` 在镜像层**(非只读挂载),可直接 `docker exec` 写入,重启不丢失。
3. 补丁脚本幂等且检测 `already applied`,重启容器重跑 entrypoint 不会撤销已注入的改动。
完整固化流程(让修复进入镜像):
- 修改 `Dockerfile` 阶段 8b(写 `_version.py` + egg-info 用 `0.11.0`)
- 修改 `scripts/patch_vllm_platform.py`(补丁 6 语义定位 + PEP 440 校验)
- 重建镜像(8a 的 ninja 编译层有缓存,几分钟):
```bash
docker compose build && docker compose --profile gradio up -d --force-recreate
```
### 7.4 模型架构 inspect 失败(triton 版本过旧)
**现象**:7.1、7.2 修复后(`platform=RocmPlatform`),解析任务在 vllm 加载模型阶段失败:
```
1 validation error for ModelConfig
Value error, Model architectures ['Qwen2VLForConditionalGeneration']
failed to be inspected. Please check the logs for more details.
```
**真实根因**(gradio 的 ClickException 吞掉了 vllm 内部栈,需手动复现 `inspect_model_cls` 捕获完整 traceback):
```
inspect_model_cls(['Qwen2VLForConditionalGeneration'])
→ import vllm.model_executor.models.qwen2_vl
→ from ...attention import MMEncoderAttention
→ fa_utils.py: from flash_attn import flash_attn_varlen_func
→ flash_attn_interface.py: from aiter.ops.triton... import flash_attn_2
→ aiter/__init__.py: from .ops.attention import *
→ aiter/ops/attention.py: from aiter.ops.triton.gluon.pa_decode_gluon import ...
→ gluon/__init__.py:
RuntimeError: aiter gluon kernels require triton>=3.6.0, found 3.5.1
```
**vllm main 的 Qwen2VL 加载链一路 import 到 aiter,aiter 的 gluon kernels 要求 triton ≥ 3.6.0,但 `import triton` 实际得到 3.5.1。** 整个 import 链断裂 → Qwen2VL 类加载失败 → `failed to be inspected`。
> **排查弯路**:曾怀疑是 vllm registry 访问 `model_config.model_impl`(transformers v5 字段,v4 缺失)导致,写了补丁 9 给 10 处访问加 `getattr` 兜底。但补丁 9 应用后 7.4 依旧——说明 `model_impl` **不是**根因。补丁 9 无害(vllm ModelConfig 有该属性时透传,transformers config 缺时兜底 "auto"),保留,但真正解决 7.4 的是 triton 版本。
**triton 版本现状**(`pip list` 有 4 个相关包,容易误判):
| 包 | 版本 | 说明 |
|---|---|---|
| `pytorch-triton-rocm` | 3.5.1 | torch 依赖的 ROCm triton,**`import triton` 实际加载的是这个** |
| `triton` | 3.7.0 | 原生 triton(满足 ≥3.6.0,但被 pytorch-triton-rocm 覆盖) |
| `triton-rocm` | 3.6.0 | 另一个 ROCm triton |
| `triton_kernels` | 1.0.0+amd | 缺 `matmul_ogs` 模块(非致命) |
**关键陷阱**:`import triton` 返回 3.5.1 而非 pip 显示的 3.7.0——因为 `pytorch-triton-rocm` 把自己注册成 `triton` 顶层包,覆盖了原生 `triton`。所以 pip list 看版本会误导,必须看 `import triton; triton.__version__`。
**解决**:设环境变量 `AITER_USE_SYSTEM_TRITON=1`。aiter 的 gluon `__init__.py` 检测逻辑:
```python
if int(os.environ.get("AITER_USE_SYSTEM_TRITON", 0)):
warnings.warn(...) # 仅警告,import 继续
else:
raise RuntimeError(...) # 阻断 import
```
设 1 后 RuntimeError 降级为 warning,import 链不再阻断,Qwen2VL 类正常加载。**这是 aiter 官方提供的逃生口**。
固化进 `docker-compose.yml`(gradio / worker0 / worker1 都要加):
```yaml
environment:
- AITER_USE_SYSTEM_TRITON=1
```
**验证**:
```bash
docker exec -e AITER_USE_SYSTEM_TRITON=1 mineru-gradio /opt/mineru_venv/bin/python -c "
from vllm.config.model import ModelConfig
from vllm.model_executor.models.registry import ModelRegistry
mp='/opt/models/modelscope/models/OpenDataLab/MinerU2.5-Pro-2605-1.2B'
cfg = ModelConfig(model=mp, tokenizer=mp, trust_remote_code=True, dtype='auto', seed=0)
print(ModelRegistry.inspect_model_cls(cfg.architectures, cfg))
"
# 期望输出:(..., 'Qwen2VLForConditionalGeneration')
```
**遗留隐患**(非阻塞):
- `AITER_USE_SYSTEM_TRITON=1` 只是不阻断 import,triton 3.5.1 实际可能不支持 gluon kernel 的某些 API。实测 Qwen2VL 推理跑通(142 页 PDF 解析成功),说明推理路径未真正调用 gluon kernel——只是 import 链被牵连。
- 若未来某模型真用 gluon kernel 且 triton 3.5.1 API 不够,需真正升级 ROCm triton 到 ≥3.6.0(注意 `pytorch-triton-rocm` 与 torch 版本绑定,升级有风险,见 1.5 节)。
### 7.5 `_version.py` 与 force-recreate 的坑
**现象**:`docker compose up -d --force-recreate` 后,`Invalid version: 'dev'`(7.1)复发。
**原因**:热修复时用 `docker exec` 写入 `/opt/vllm/vllm/_version.py`,这只存在于**运行容器的可写层**,不在镜像层。`--force-recreate` 重建容器后丢失。补丁 6/7/9 是 entrypoint 跑脚本应用的,会自动重应用;但 `_version.py` 是 Dockerfile 阶段 8b 写的,**镜像里如果 Dockerfile 没改,重建即丢**。
**解决**:把 `_version.py` 写入固化进 Dockerfile 阶段 8b(见 6.1/7.1 节)。固化前用 `docker restart`(保留容器层),不要 `--force-recreate`。
---
*最后更新: 2026-06-23*