AutoDL 部署 Qwen3-32B Lora Model 踩坑记录

type
status
date
slug
summary
tags
category
icon
password
AI summary
notion image
最近在做组里的项目,为了满足甲方的要求,在组内服务器上对 Qwen-32B 模型进行了 Lora 微调。之后甲方要求测试模型服务,但是不提供服务器进行部署 😅,没办法,只能租服务器部署提供远程访问。在租赁的服务器上部署模型之前,我在本地事先跑通了完整的部署和测试流程,整个过程比较简单,按照 ms-swift 文档 deploy 就行(前期使用 ms-swift 进行 sft 的)。
出于 AutoDL GPU 服务器的性价比高,在通过 vram-calculator 计算部署和推理需要的大致显存后,租赁了 vGPU 32GB x 4 的实例。原本以为很快就能交付部署,谁能想一个坑接着一个坑的排,最终 AI 在我的辅助下也是和我一起解决了所有问题。推理成功的那一刻再一次体会到了一点点解决编程 bug 的激动 😭
notion image

镜像环境不匹配

🚨
这是最坑的,官网没有任何提示 vGPU 不能用最新的镜像,最后还是给客服提 bug 时,客服才说 vGPU 用不了最新的镜像 🤡。我就请问了,不能用别在镜像列表里显示啊,显示也该提示允许的 GPU 型号吧,真服咧。(9.7 写这篇博客时已经支持了

1. 镜像环境选择

本着“用新不用旧”的原则,我最开始直接选择了平台提供的基于 CUDA 12.8 的官方镜像,使用了当时较新的 PyTorch 版本。
notion image
 

2. 多卡通信测试失败

其实在实际进行多卡测试之前,我是先走一遍正常的部署流程的:
pip install 相关依赖,然后执行推理,出现了vLLM 多进程 Worker 初始化失败错误,大致日志如下(复现 bug 时,无法选择此镜像了):
与 AI 进行多次讨论、排查之后,依然未解决问题。只能尝试选择联系客服,客服给我一个脚本,来测试多卡之间通信是否正常。
脚本主要的流程是:在多卡环境下,对一个大张量进行 all_reduce 操作(这里是连续进行 10 次 all_reduce 操作),并统计其平均带宽和耗时。内容如下:
使用主机自带的 Base 环境(先埋个坑)执行 torchrun 命令后,程序立即崩溃。错误日志如下:
进程因 exitcode: -11 而失败,并收到 Signal 11 (SIGSEGV),这是一个典型的段错误,意味着程序访问了无效的内存地址。这通常不是 Python 代码逻辑错误,而是底层 C++/CUDA 代码层面出现了问题。

3. 问题排查:分析 NCCL 通信失败原因

3.1 最小化张量测试

创建一个试脚本,使用极小的张量:
结果:即使是 2KB 的小张量也会触发段错误。

3.2 仅初始化进程组

结果:成功运行!说明进程组初始化没问题,问题出在 all_reduce 操作

3.3 启用 NCCL 调试信息

添加 NCCL 调试环境变量:
然后在日志中可以看到比较关键的警告信息:
发现:NCCL 无法正确调用某些 CUDA 函数,表明库之间的兼容性出现了问题。

3.4 切换到 Gloo 后端验证

使用 gloo 后端替代 nccl
结果:成功运行!这证实了问题确实是 NCCL 触发的,并且是兼容性问题。
💡
Gloo 是一个由 Facebook 开发的,用于分布式训练的通信库后端 。
  • 核心功能:Gloo 基于 TCP/IP 协议,提供了一套高效的集体通信操作,用于在不同计算节点之间同步数据和模型参数 。
  • GPU 分布式训练:虽然 Gloo 也支持 GPU,但性能通常不如专为 NVIDIA GPU 深度优化的 NCCL 后端。

4. 问题根因

  1. NCCL 2.27.3 无法正确调用 CUDA 12.8 的某些函数
      • 错误信息:CUDA some PFN functions not found in the library
      • 这表明 NCCL 库与当前 CUDA 版本存在不兼容
  1. 容器环境可能进一步导致了兼容性问题
      • AutoDL 容器环境中的 CUDA 配置可能与 NCCL 预期的环境有差异,某些底层 CUDA 函数的链接或加载可能受到影响
  1. 段错误发生在 NCCL 通信操作时
      • 初始化进程组成功,但 all_reduce 操作触发崩溃,与数据大小无关,这表明是 NCCL 库在调用底层 CUDA 函数时出错

5. 吐槽

最后给官方客服提交 bug 时,客服说这个镜像是专门为 5090 准备的,vGPU 不适配,然后告诉我说明在如下图中:
notion image
谁能知道这里面有镜像说明哈?不应该在选择镜像的时候进行提醒和说明吗?而且打开文档发现,也并没有提醒用户适配性的问题,只是说 5090、RTX PRO 6000 请使用此镜像,没有表明其他显卡不能用…… 糟糕的文档 🤡
notion image

9.7 日,写这篇博客时,再次复现 Bug 时,发现官网已经添加了镜像选择的提示,这算前人栽树后人乘凉嘛 🥹:
notion image

控制变量,循序渐进

选择了一个 CUDA 12.4 的镜像,具体环境配置如下:
  • PyTorch  2.5.1
  • Python  3.12
  • CUDA  12.4

① : 创建虚拟环境 & 安装 ms-swift

然后测试多卡通信,发现测试也是直接失败 😭,还是同样的错误,更懵逼:
再次询问客服,客服问我是否更换了 torch 的版本,让我尝试在实例自带的 Base 环境进行测试。在主机自带的 Base 环境下,运行通信测试脚本,GPU 通信成功!!输出如下:
Amazing,竟然有 torch 的问题。观察pip list 发现,ms-swift 安装的 pytorch 和实例镜像自带的 Base 环境中的 Pytorch 确实不一致。
  • 2.5.1+cu124:打包时已内置 CUDA 12.4 的 GPU 轮子(同时内置 NCCL),安装后不用本机 CUDA Toolkit 。
  • 2.8.0:可能是 CPU 版或通过 PyTorch 官网上的安装选择器拿到的某个 GPU 版,具体以安装的 wheel 为准。
查看 torch==2.8.0 具体的版本信息:
在这里问题就比较明确了,通过 ms-swift 安装的 torch 针对的是 CUDA 12.8,但是实例的 GPU 只能支持所有低于或等于 12.4 版本的 CUDA Toolkit 编译的程序:
事实好像并不是这样 😵‍💫。从官网查询得知,从 11.0 版本开始,NVIDIA 引入了 CUDA 的 Minor Version Compatibility 。这意味着,在同一主版本号内(12.x),一个较新版本的 CUDA Toolkit (如 12.8) 编译的程序,可以运行在一个支持较低版本 CUDA (如 12.4) 但支持 12.x 的驱动程序上。官网还有几个相似的概念,可能会有些困惑:
兼容性类型
驱动版本
CUDA 工具包版本
描述
向后兼容性 (Backward Compatibility)
(默认支持) 新驱动可以运行旧版 CUDA 工具包编译的应用。这是最常见的情况。
次要版本兼容性 (Minor-Version Compatibility)
新 (主版本号相同)
同一主版本号内(如驱动为 525.x,工具包为 12.y),旧驱动可运行新工具包的应用。
前向兼容性 (Forward Compatibility)
新 (主版本号可不同)
(需兼容包) 跨越主版本号,旧驱动可运行新工具包编译的应用(例如驱动为 525.x,工具包为 13.x)。
所以,具体分析现有组件,可以得出:
组件
版本
解释
NVIDIA Driver
550.120
由于版本够新,它通过Minor-Version Compatibility 支持了更新的 CUDA 12.8。
nvidia-smi CUDA
12.4
驱动程序原生支持的 CUDA 版本。在有兼容性的情况下,它不再是一个严格的上限,而更像一个参考基线。
PyTorch CUDA
12.8
编译 PyTorch 时使用的工具包版本。
torch.cuda.is_available()
True
最终的验证结果。这个 True 值确认了整个环境(驱动 + PyTorch)配置正确且可以协同工作。如果存在不兼容问题,这个命令会返回 False 或者程序会直接报错。
既然官方支持了“次要版本兼容性”,驱动也满足 12.x,那为什么会导致程序崩溃呢?为了进一步测试兼容性的问题,我新创建了一个实例,镜像一致,但是驱动更新,而且支持的最高 CUDA Version 为 13.0:
在这个环境上按照同样的流程测试,通信测试成功:
🤔
综合以上文档、实验和分析,只能得出报错的可能原因是:CUDA 版本不匹配导致 NCCL 在通信阶段崩溃(SIGSEGV)
完整梳理:
  • swift 安装的 PyTorch 是 2.8.0+cu128,也就是 CUDA 12.8 构建:
    • print(torch.__version__, torch.version.cuda) → 2.8.0+cu128 12.8
  • 宿主机驱动只支持到 CUDA 12.4
    • nvidia-smi → CUDA Version: 12.4 (Driver 550.120)
  • 低于运行时所需的驱动版本时,CUDA/NCCL 常在初始化或集合通信(如 all_reduce)里直接段错误(-11)而不是抛 Python 异常;日志里也显示首个失败进程为 local_rank: 3 收到 SIGSEGV
结论:用 CUDA 12.8 编译的 PyTorch/NCCL 在 仅支持 12.4 的驱动上运行,触发本地库崩溃。

② : 创建虚拟环境 & 安装指定的 torch

这里为了防止其他意外发生,指定了和 Base 环境同样的 torch 版本 2.5.1 进行测试。
正常运行:
在虚拟环境中安装和 Base 环境一样的 Pytorch 也能够测试成功,说明确实是安装的 PyTorch 二进制的问题。

③: ② & 安装 swift

在 ② 操作的基础上,再安装 swift,测试后再次发生了和 ① 一样的错误,查看依赖安装日志,发现 pip 在安装 ms-swift 时,自动卸载了 torch==2.5.1+cu124。此时再运行多卡通信测试,使用的就是 torch==2.7.1 ,所以 SIGSEGV 段错误再次出现。

④: ① & 删除 swift 自带的 torch & 安装指定的 torch

既然 swift 会删除原来的 torch,也并没有提供使用现有的 torch 选项,那就尝试在安装 swift 之后,卸载自带的 torch,再安装 torch==2.5.1+cu124 。此时需要注意的点是,替换 torch 后需要测试 swift 是否能够正常运行,防止 swift 强绑定了特定的 torch 版本。
正常运行:

⑤: ④ & 使用已有的 torch 从头编译 vllm

和直接安装 ms-swift 同理,安装 vllm 也会覆盖原有安装的 Pytorch,但是如果安装 vllm 后,再进行替换,会导致 Vllm 不可用,因为 vllm 为了保证其内部 CUDA 扩展的兼容性,通常会严格绑定一个特定的 PyTorch 版本。替换自定义的 Pytorch也会导致不兼容,使 vllm 无法使用。所以这里根据官方文档使用已有的 torch 从头编译 vllm:
刚开始也是没动脑子,直接 clone 最新的代码库就编译了,编译报错:
日志说明现在编译的 vLLM 代码启用了 FP8 相关内核,并引用了 PyTorch C++ 里较新的 dtype(at::Float8_e8m0fnu)。而当前环境里的 PyTorch=2.5.1 的头文件里还没有这个类型(CMake 也警告 “Pytorch version 2.7.1 expected…”)。换句话说:vLLM 当前源码分支比当前环境中的 PyTorch 版本新,所以编译时找不到该符号,导致失败。然后我也再次没动脑子的采用了 ChatGPT 的建议:
社区里在 torch 2.5.1 + CUDA 12.4 下成功安装 vLLM 0.7.3 的案例很多。
编译时,由于没有设置 MAX_JOBS ,导致编译过程中并发数太大,直接导致内存爆炸(OOM Killed):
  • 现象: 在编译过程中,特别是编译 FlashAttention 等复杂的 CUDA 内核时,进程突然终止,日志中出现 Killed 字样或 exitcode: 137
  • 分析: 这是典型的内存不足(Out of Memory)信号。nvcc 编译器在进行模板实例化时会消耗大量内存,如果 pip 的并行编译任务数(MAX_JOBS)过高,很容易超出实例的物理内存上限。
  • 解决: 限制并行编译任务数。
    一杯咖啡时间后,编译成功:
    测试通信,多卡通信正常,yeah ^_^

    运行时问题诊断与分析

    环境就绪后,开始进行完整的模型部署,但新的问题也随之而来。

    1. 因 vLLM 版本不匹配导致的显存溢出 (OOM)

    这个问题的根本原因在于编译的 Vllm 版本太低,还未对 Qwen3 Model 进行适配 🥲。也是自己疏忽,直接选择了 ChatGPT 提供的版本,ChatGPT 是根据 CUDA Version 进行选择的,但是没有考虑到模型的问题。
    • 现象: 服务启动时,显存被迅速占满,并抛出 torch.OutOfMemoryError。但根据先前的计算,显存是足够用于推理的,在加载阶段报错,应该是其他问题。
      • Qwen3ForCausalLM has no vLLM implementation, falling back to Transformers implementation
    • 根本原因: 从日志中可以看到,该 vLLM 版本不原生支持 Qwen3 模型,因此自动回退到 HuggingFace Transformers 库的后端实现。

    2. 多卡环境下 LoRA 加载超时

    查看官方的 Release 信息,可以得知 Vllm 是在 V0.8.4 开始支持 Qwen3 的,考虑到 V0.8.5 还修复了一些 bug,所以我选择升级 vLLM==0.8.5 并重新编译:
    又一次尝试运行部署脚本之后,终于部署成功了!!!
    但尝试请求服务进行推理时,新的问题出现了 🤡。大概就是 vllm 多进程推理时等待 worker 响应超时,出现超时错误
    日志如下,稍微长
    RPC (Remote Procedure Call): 远程过程调用。在 vLLM 中,主进程通过 RPC 指令与各个 GPU 上的工作进程(worker)通信。

    2.1 开始怀疑是否是 Lora 的问题

    既然能够成功部署并且多卡 GPU 之间也能正常通信,说明环境大致没问题了(应该还是有问题的,因为我本地非容器环境直接跑通了)。开始排查是否和 Lora 有关联?所以尝试不加载 Lora adapter 进行部署
    Ohhhhh,推理成功了,通过消融实验确定了就是 Lora 的问题,yeah 🤗!既然不加载 LoRA 时一切正常,问题显然出在 vLLM 动态加载 LoRA 的过程
    🤖
    AI: 在当前的多卡容器环境中,vLLM worker 进程在尝试应用 LoRA 权重时,其内部的进程间通信(IPC)机制出现了问题。这很可能与容器环境对共享内存(/dev/shm)的默认配置较小有关,导致 worker 进程在通信或等待资源时被卡死或崩溃,主进程因收不到响应而超时。
    但好像其实也不是很小 🤔?
    📌
    至于当前环境为什么应用 Lora 时就会通信问题,还是个未解问题。

    3. 采用 LoRA 离线合并策略

    既然在当前的特定环境下,动态加载 LoRA 会引发问题,所以尝试离线合并 Lora,将 LoRA 的权重合并到基础模型中,然后在部署时加载合并后的模型,避免应用 Lora。
    然后部署这个合并后的模型,此时部署脚本就不再需要 --adapters 参数了:
    苦尽甘来,这一次,终于推理成功了 🥹。使用训练时的 PROMPT 进行测试,回复也遵循训练时要求的格式,说明 Lora 应用成功。问题解决!Bingo~

    服务上线,远程访问

    根据需求,需要将服务暴露给外部调用,但 AutoDL 平台的容器默认没有公网 IP,除非进行企业认证。对于非企业用户,平台提供了将容器内 60066008 端口映射到公网的“自定义服务”功能。因此,将本地 6006 端口的流量转发到 vLLM 默认的 8000 端口。

    容器内设置端口转发

    使用 AutoDL 官方提供的 proxy_in_instance 工具进行端口转发:
    为确保 SSH 断开后服务能持续运行,这里使用了使用 tmux ,当然也可以用 nohup

    本地建立 SSH 隧道,实现端口转发

    • -C :启用压缩,提高数据传输效率,尤其是在带宽有限时有帮助。
    • -N :不执行远程命令,仅用于端口转发(不会打开 shell)。
    • -g :允许远程主机连接本地端口(通常用于端口转发,但在本地转发时一般不用,写了也无妨)。
    • -L [本地端口]:[远程地址]:[远程端口]-L 6006:127.0.0.1:6006
        1. 6006(本地端口)
            • 指的是你本地电脑上的端口号。
            • 访问 localhost:6006 时,数据会通过 SSH 隧道转发。
        1. 127.0.0.1(远程地址)
            • 指的是远程主机上的地址。
            • 127.0.0.1 表示远程主机自身(即远程服务器的本地环回地址)。
        1. 6006(远程端口)
            • 指的是远程主机上的端口号。
    整个命令就是通过建立一个 SSH 隧道,将本地 6006 端口流量转发到远程服务器的 6006 端口,便于在本地访问远程服务。
    Swift 部署的服务支持兼容 OpenAI 格式调用,所以在本地可以很便捷的调用 LLM 进行推理了:

    总结

    本次在 AutoDL 平台上部署 Qwen3-32B LoRA 模型,是一次深入的环境配置、依赖管理和问题排查的实践,也算是第一次完整的训练、部署和迁移模型,学到了很多东西,尽管过程中被各种报错整的小崩溃 🥲。btw,虽然整个过程 AI 辅助了很多操作,但是核心的排查思路还是自己一点点探索的,所以在部署成功的那一刻,我好想感受到了 LLM 还未出现之前,自己一点点排查程序 bug 到跑通的激动 😋
    Loading...

    © huhu 2023-2025