2026-06-05 11:14:53 +08:00
|
|
|
|
# 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
|
|
|
|
|
|
```
|
|
|
|
|
|
|
2026-06-09 11:22:04 +08:00
|
|
|
|
### 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`(断了一个可单独重试)
|
|
|
|
|
|
|
2026-06-05 11:14:53 +08:00
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 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()`
|
|
|
|
|
|
|
2026-06-09 11:22:04 +08:00
|
|
|
|
### 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...'
|
|
|
|
|
|
```
|
|
|
|
|
|
|
2026-06-05 11:14:53 +08:00
|
|
|
|
### 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;..."
|
|
|
|
|
|
```
|
|
|
|
|
|
|
2026-06-09 11:22:04 +08:00
|
|
|
|
### 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 参数,复杂结构拆成独立循环
|
|
|
|
|
|
|
2026-06-05 11:14:53 +08:00
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 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` 文件内容均会触发。
|
|
|
|
|
|
|
2026-06-09 11:22:04 +08:00
|
|
|
|
### 4.3 COPY 脚本在 RUN 引用之后
|
|
|
|
|
|
|
|
|
|
|
|
**现象**:阶段 8b 执行 `python /opt/patch_vllm_platform.py` → 文件不存在
|
|
|
|
|
|
|
|
|
|
|
|
**原因**:`COPY scripts/patch_vllm_platform.py` 在阶段 8.5(原序号),但阶段 8b 已引用该脚本——Dockerfile 中 COPY 位于引用它的 RUN 之后
|
2026-06-05 11:14:53 +08:00
|
|
|
|
|
2026-06-09 11:22:04 +08:00
|
|
|
|
**解决**:将 COPY 指令移到所有需要脚本的 RUN 之前(阶段 7.5)。规则:**COPY 必须在引用它的 RUN 之前**
|
|
|
|
|
|
|
|
|
|
|
|
---
|
2026-06-05 11:14:53 +08:00
|
|
|
|
## 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**(唯一例外) |
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
2026-06-17 16:33:02 +08:00
|
|
|
|
## 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 进程中正常工作
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
2026-06-23 13:58:15 +08:00
|
|
|
|
## 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
|
|
|
|
|
|
```
|
|
|
|
|
|
|
2026-06-23 18:22:32 +08:00
|
|
|
|
### 7.4 模型架构 inspect 失败(triton 版本过旧)
|
2026-06-23 13:58:15 +08:00
|
|
|
|
|
|
|
|
|
|
**现象**: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.
|
|
|
|
|
|
```
|
|
|
|
|
|
|
2026-06-23 18:22:32 +08:00
|
|
|
|
**真实根因**(gradio 的 ClickException 吞掉了 vllm 内部栈,需手动复现 `inspect_model_cls` 捕获完整 traceback):
|
2026-06-23 13:58:15 +08:00
|
|
|
|
|
|
|
|
|
|
```
|
2026-06-23 18:22:32 +08:00
|
|
|
|
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
|
2026-06-23 13:58:15 +08:00
|
|
|
|
```
|
|
|
|
|
|
|
2026-06-23 18:22:32 +08:00
|
|
|
|
**vllm main 的 Qwen2VL 加载链一路 import 到 aiter,aiter 的 gluon kernels 要求 triton ≥ 3.6.0,但 `import triton` 实际得到 3.5.1。** 整个 import 链断裂 → Qwen2VL 类加载失败 → `failed to be inspected`。
|
2026-06-23 13:58:15 +08:00
|
|
|
|
|
2026-06-23 18:22:32 +08:00
|
|
|
|
> **排查弯路**:曾怀疑是 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 13:58:15 +08:00
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
*最后更新: 2026-06-23*
|