在本地部署大语言模型时,很多开发者往往只关注模型本身的选择,却忽略了底层硬件与推理框架的适配成本。特别是在使用国产昇腾(Ascend)AI 处理器时,由于软件栈与常见的 CUDA 生态存在差异,直接套用通用的 vLLM 教程往往会遭遇编译失败、算子缺失或显存异常等问题。这不仅浪费了宝贵的算力资源,也让许多想要尝试国产化部署的团队望而却步。

其实,只要理清了从驱动层到应用层的依赖关系,并针对 Ascend 架构进行正确的参数配置,整个部署流程是可以做到标准化且稳定的。vLLM 作为目前主流的高吞吐推理引擎,其针对昇腾平台的适配版本(vLLM-Ascend)已经能够很好地支持主流开源模型的高效推理。关键在于如何正确地完成环境初始化、源码编译以及启动参数的精细化调整。

本文将基于实际的部署经验,一步步拆解在昇腾环境下构建 vLLM 推理服务的全过程。我们将从最基础的环境准备开始,深入探讨 CANN toolkit 的配置细节,分享源码编译中的常见坑点,并详细解析如何通过 Python API 和 OpenAI 兼容接口发起请求。此外,针对多卡并行策略和常见的显存报错,也会提供具体的排查思路与调优技巧,帮助你快速搭建起一个高性能、低延迟的本地推理服务。

① 运行环境准备与依赖安装

在开始任何安装操作之前,确保操作系统环境的纯净性是成功的关键。推荐使用 Ubuntu 20.04 或 22.04 LTS 版本,这两个版本在昇腾社区的兼容性测试最为充分。首先,需要检查系统自带的 GCC 和 CMake 版本,vLLM-Ascend 的编译通常要求 GCC 版本在 7.5 以上,CMake 版本不低于 3.18。如果系统默认版本过低,建议通过 apt 源或手动编译方式进行升级,避免后续链接错误。

Python 环境的隔离至关重要。务必使用 condavenv 创建一个独立的虚拟环境,推荐 Python 版本为 3.8 至 3.10 之间,过高版本的 Python 可能导致部分底层算子库无法找到对应的预编译包。创建环境后,第一步是安装 PyTorch 的昇腾适配版本。这与标准的 NVIDIA 版 PyTorch 不同,必须从昇腾官方提供的源或华为云镜像站下载带有 ascend 标识的 wheel 包。

# 创建并激活虚拟环境
conda create -n vllm-ascend python=3.9 -y
conda activate vllm-ascend

# 安装昇腾版 PyTorch (示例版本号,请以官网最新为准)
pip install torch==2.1.0+ascend torchvision torchaudio --extra-index-url https://repo.huaweicloud.com/kunpeng/archive/Ascend/PyTorch/torch_stable.html

除了 PyTorch,还需要安装一些基础的构建工具和网络库,如 libnuma-devlibssl-dev 等,这些是编译 C++ 扩展所必需的。可以使用 apt-get install 一次性补齐。同时,确认 pip 源配置正确,以便快速拉取 transformers、accelerate 等上游依赖,避免因网络超时导致安装中断。

② Ascend 驱动与 CANN toolkit 配置

硬件驱动与计算架构软件栈(CANN)是连接物理芯片与上层框架的桥梁。在安装 vLLM 之前,必须确认 Ascend 驱动已正确加载。可以通过 npu-smi info 命令查看显卡状态,如果该命令无法识别或报错,说明驱动未安装或版本不匹配,此时需联系服务器管理员重新安装对应型号的驱动包。

CANN toolkit 的安装更为关键,它提供了算子库和编译器。下载与当前驱动版本严格匹配的 CANN 包后,执行安装脚本。安装完成后,环境变量配置是极易出错的环节。必须在用户的 .bashrc.zshrc 文件中导出 ASCEND_HOME_PATHLD_LIBRARY_PATHPYTHONPATH 等关键变量。

例如,典型的配置如下:

export ASCEND_HOME_PATH=/usr/local/Ascend/ascend-toolkit/latest
export LD_LIBRARY_PATH=$ASCEND_HOME_PATH/lib64:$LD_LIBRARY_PATH
export PYTHONPATH=$ASCEND_HOME_PATH/python/site-packages:$PYTHONPATH
source $ASCEND_HOME_PATH/bin/set_env.sh

配置生效后,建议运行一个简单的 PyTorch 脚本测试 NPU 是否可见。代码中尝试调用 torch.npu.is_available(),若返回 True 且能打印出设备名称,则说明底层环境已就绪。这一步看似简单,却能提前规避 90% 因环境路径混乱导致的“找不到算子”错误。

③ vLLM-Ascend 源码编译与安装

目前 vLLM 对昇腾的支持主要通过特定的分支或补丁实现,直接使用 pip install vllm 通常会安装仅支持 CUDA 的版本,因此源码编译是必经之路。首先从官方仓库克隆代码,并切换到适配 Ascend 的特定分支(如 ascend_main 或根据文档指示的 commit ID)。

进入目录后,需要设置编译环境变量,告诉构建系统使用 NPU 后端。关键变量包括 VLLM_USE_ASCEND=1。随后执行安装命令,建议使用 -e 模式进行可编辑安装,方便后续调试修改。

git clone -b ascend_main https://github.com/vllm-project/vllm.git
cd vllm

# 设置编译标志
export VLLM_USE_ASCEND=1
export MAX_JOBS=8  # 根据 CPU 核心数调整并发编译数

# 执行安装
pip install -e .

编译过程耗时较长,期间会调用 CMake 生成构建文件并编译 C++ 扩展。如果遇到报错,重点检查日志中关于 acl 库的链接情况。有时需要手动指定 CMAKE_ARGS 来指向 CANN 的头文件和库文件路径。编译成功后,可以在 Python 中导入 vllm 模块并打印其版本信息,若无报错则代表安装完成。

④ 模型加载参数与启动命令详解

环境就绪后,接下来是核心的模型加载与服务启动。vLLM-Ascend 的启动命令结构与原生版类似,但在参数细节上有所区别。首先是 --model 参数,指定 Hugging Face 上的模型 ID 或本地路径。对于昇腾平台,--device 参数必须显式设置为 ascend,否则引擎可能尝试初始化不存在的 CUDA 上下文。

显存管理是另一个重点。--gpu-memory-utilization 参数用于控制预分配给 KV Cache 的显存比例。在 Ascend 设备上,由于内存管理机制的差异,建议将该值设置在 0.8 到 0.9 之间,预留部分内存给系统开销,避免 OOM(显存溢出)。此外,--max-model-len 决定了上下文窗口的最大长度,需根据实际业务需求和显存容量权衡,过大的值会导致单次推理延迟显著增加。

启动示例命令如下:

python -m vllm.entrypoints.api_server \
    --model Qwen/Qwen2-7B-Instruct \
    --device ascend \
    --host 0.0.0.0 \
    --port 8000 \
    --gpu-memory-utilization 0.85 \
    --max-model-len 4096 \
    --dtype half

这里 --dtype half 指定使用 FP16 精度,这在昇腾 910 系列芯片上能获得最佳的算力利用率。如果模型权重为 BF16,也可相应调整,但需确认当前 CANN 版本对 BF16 的支持情况。

⑤ 使用 Python API 发起推理请求

服务启动后,最直接的使用方式是通过 vLLM 提供的异步 Python API 进行集成。这种方式适合嵌入到现有的业务逻辑中,无需经过 HTTP 协议转换,延迟更低。首先需要导入 AsyncLLMEngineSamplingParams 类,并配置引擎参数,使其与启动命令行中的参数保持一致。

在代码中,构建一个提示词列表,然后调用 generate 方法。值得注意的是,昇腾后端的生成过程是同步阻塞还是异步非阻塞,取决于具体的实现版本,通常建议使用 asyncio 包裹以获得最佳并发性能。

import asyncio
from vllm import LLM, SamplingParams

def run_inference():
    # 初始化引擎,注意 device 设置
    llm = LLM(
        model="Qwen/Qwen2-7B-Instruct",
        device="ascend",
        gpu_memory_utilization=0.85,
        max_model_len=4096
    )

    prompts = ["你好,请介绍一下昇腾处理器的特点。"]
    sampling_params = SamplingParams(
        temperature=0.7,
        top_p=0.9,
        max_tokens=512
    )

    outputs = llm.generate(prompts, sampling_params)

    for output in outputs:
        print(f"Prompt: {output.prompt!r}, Generated text: {output.outputs[0].text!r}")

if __name__ == "__main__":
    run_inference()

这段代码展示了最小化的推理流程。在实际生产中,可以将 LLM 实例化为全局单例,避免重复加载模型带来的时间开销。同时,通过调整 temperaturetop_p 参数,可以灵活控制生成文本的随机性和多样性。

⑥ 通过 OpenAI 兼容接口进行测试

为了方便前端应用或其他语言客户端调用,vLLM 内置了完全兼容 OpenAI API 格式的 HTTP 服务。当使用前述的 api_server 入口启动服务后,可以直接使用标准的 requests 库或 curl 命令进行测试。这种方式的优点是解耦了客户端与服务端,便于横向扩展。

测试时,只需将请求地址指向部署服务的 IP 和端口,并在 JSON Body 中遵循 OpenAI 的 Chat Completion 格式。以下是一个使用 curl 的测试示例:

curl http://localhost:8000/v1/chat/completions \
    -H "Content-Type: application/json" \
    -d '{
        "model": "Qwen/Qwen2-7B-Instruct",
        "messages": [
            {"role": "system", "content": "你是一个有用的助手。"},
            {"role": "user", "content": "如何用 Python 读取 CSV 文件?"}
        ],
        "max_tokens": 200,
        "temperature": 0.5
    }'

返回的结果结构与 OpenAI 官方接口完全一致,包含 choicesusage 等字段,方便现有基于 GPT 开发的应用无缝迁移。如果在局域网内部署,还可以配合 Nginx 进行反向代理,添加鉴权机制,进一步提升服务的安全性。

⑦ 显存占用异常与算子报错排查

在昇腾平台上运行大模型,偶尔会遇到显存占用居高不下或特定算子不支持的报错。显存异常通常表现为进程未退出但显存未释放,这往往是因为上一次推理任务异常终止,导致 NPU 上下文未正常销毁。解决方法是找到残留的进程 ID 并强制杀死,或者重启机器重置 NPU 状态。使用 npu-smi info 可以实时监控各卡的显存使用情况,定位是哪个进程占用了资源。

算子报错则更为复杂,常见错误日志会提示 “Op not implemented” 或 “Kernel launch failed”。这通常是因为模型中使用了某些昇腾 CANN 尚未完全支持的算子,或者算子的数据类型(如 int8、bf16)与当前配置不匹配。排查时,首先检查模型结构,尝试关闭一些高级特性(如 Flash Attention 的特定变体),或者在加载时启用 --enforce-eager 模式,虽然会牺牲部分性能,但能绕过图编译阶段的优化错误。此外,保持 CANN toolkit 更新至最新版本,往往能解决大量已知算子缺失问题。

⑧ 多卡并行策略与性能调优技巧

面对参数量巨大的模型,单卡显存往往不足以承载,这时需要利用多卡并行策略。vLLM-Ascend 支持张量并行(Tensor Parallelism, TP),通过将模型权重切分到多个 NPU 上共同计算。启动时只需添加 --tensor-parallel-size 参数,指定使用的卡数即可,例如 --tensor-parallel-size 4 表示使用 4 张卡。

然而,简单的多卡叠加并不一定能带来线性的性能提升。通信开销是主要的瓶颈。在昇腾集群中,确保各卡之间通过高速互联总线(如 HCCS)连接至关重要。如果跨节点部署,还需配置合理的 RDMA 网络。调优方面,可以尝试调整 block-size 参数,改变 PagedAttention 管理的显存块大小,以适应不同的序列长度分布。较小的 block size 能减少显存碎片,但可能增加管理开销;较大的 block size 则相反,需要根据实际负载进行微调。

另外,批处理大小(batch size)的动态调整也是提升吞吐量的关键。vLLM 的调度器会自动管理并发请求,但在高负载下,适当限制 --max-num-batched-tokens 可以防止单个长序列请求阻塞整个队列,从而保证整体服务的响应延迟维持在可控范围内。通过不断压测和监控各项指标,最终找到显存利用率与推理速度之间的最佳平衡点。

Logo

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构

更多推荐