作者:Matthias Reso, Ankith Gunapal, Simon Mo, Li Ning, Hamid Shojanazeri

vLLM 引擎是当前执行大型语言模型 (LLM) 的顶级高性能方式之一。它提供了 vllm serve 命令,作为在单机上部署模型的简便选项。虽然这很方便,但在生产环境中大规模服务这些 LLM 需要一些高级功能。

flow diagram

TorchServe 提供了这些基本的生产功能(例如自定义指标和模型版本控制),并通过其灵活的自定义 handler 设计,非常容易集成检索增强生成 (RAG) 或 Llama Guard 等安全功能。因此,将 vLLM 引擎与 TorchServe 结合起来,创建一个完整的 LLM 生产服务解决方案是顺理成章的。

在深入探讨集成细节之前,我们将演示如何使用 TorchServe 的 vLLM docker 镜像部署 Llama-3.1-70B-Instruct 模型。

在 TorchServe + vLLM 上快速开始使用 Llama 3.1

要开始,我们需要通过检出 TorchServe 仓库并从主文件夹执行以下命令来构建 新的 TS LLM Docker 容器镜像

docker build --pull . -f docker/Dockerfile.vllm -t ts/vllm

该容器使用我们新的 LLM 启动脚本 ts.llm_launcher,该脚本接受 Hugging Face 模型 URI 或本地文件夹,并在后端运行 vLLM 引擎的情况下启动本地 TorchServe 实例。要在本地服务模型,您可以使用以下命令创建容器实例

#export token=<HUGGINGFACE_HUB_TOKEN>
docker run --rm -ti --shm-size 10g --gpus all -e HUGGING_FACE_HUB_TOKEN=$token -p 
8080:8080 -v data:/data ts/vllm --model_id meta-llama/Meta-Llama-3.1-70B-Instruct --disable_token_auth

您可以使用此 curl 命令在本地测试端点

curl -X POST -d '{"model":"meta-llama/Meta-Llama-3.1-70B-Instruct", "prompt":"Hello, my name is", "max_tokens": 200}' --header "Content-Type: application/json" "http://localhost:8080/predictions/model/1.0/v1/completions"

该 docker 将模型权重存储在本地文件夹“data”中,该文件夹在容器内部被挂载为 /data。要服务您自定义的本地权重,只需将它们复制到 data 文件夹中,并将 model_id 指向 /data/<您的权重>。

在内部,该容器使用我们新的 ts.llm_launcher 脚本来启动 TorchServe 并部署模型。该启动器将 LLM 与 TorchServe 的部署简化为单命令行,并且也可以在容器外部用作实验和测试的有效工具。要在 docker 外部使用该启动器,请按照 TorchServe 安装步骤进行操作,然后执行以下命令启动一个 8B Llama 模型

# after installing TorchServe and vLLM run
python -m ts.llm_launcher --model_id meta-llama/Meta-Llama-3.1-8B-Instruct  --disable_token_auth

如果有多个 GPU 可用,启动器将自动声明所有可见设备并应用张量并行(参见 CUDA_VISIBLE_DEVICES 指定要使用的 GPU)。

虽然这非常方便,但重要的是要注意它并未涵盖 TorchServe 提供的所有功能。对于希望利用更高级功能的用户,需要创建一个模型存档。虽然这个过程比执行单命令更复杂一些,但它具有自定义 handler 和版本控制的优势。前者允许在预处理步骤中实现 RAG,后者允许您在更大规模部署之前测试不同版本的 handler 和模型。

在提供创建和部署模型存档的详细步骤之前,让我们深入了解 vLLM 引擎集成的细节。

TorchServe 的 vLLM 引擎集成

作为最先进的服务框架之一,vLLM 提供了大量高级功能,包括 PagedAttention、持续批处理、通过 CUDA graphs 实现快速模型执行,以及支持各种量化方法,如 GPTQ、AWQ、INT4、INT8 和 FP8。它还提供了对 LoRA 等重要参数高效适配器方法的集成,并支持包括 Llama 和 Mistral 在内的多种模型架构。vLLM 由 vLLM 团队和一个活跃的开源社区维护。

为了方便快速部署,它提供了一种基于 FastAPI 的服务模式,通过 HTTP 服务 LLM。为了更紧密、更灵活的集成,该项目还提供了 vllm.LLMEngine,它提供了持续处理请求的接口。我们利用了 异步变体 将其集成到 TorchServe 中。

TorchServe 是一个易于使用的开源解决方案,用于在生产环境中服务 PyTorch 模型。作为经过生产验证的服务解决方案,TorchServe 提供了许多对于大规模部署 PyTorch 模型非常有益的优势和功能。通过将其与 vLLM 引擎的推理性能相结合,这些优势现在也可用于大规模部署 LLM。

Torchserve highlights and integrations

为了最大化硬件利用率,通常将来自多个用户的请求批量处理在一起是一个好做法。历史上,TorchServe 只提供同步模式来收集来自各种用户的请求。在该模式下,TorchServe 会等待预定义的时间(例如,batch_delay=200ms)或直到接收到足够的请求(例如,batch_size=8)。当其中一个事件触发时,批处理的数据被转发到后端,模型应用于该批次数据,然后模型输出通过前端返回给用户。这对于传统视觉模型尤其有效,因为每个请求的输出通常同时完成。

对于生成式用例,尤其是文本生成,请求同时就绪的假设不再成立,因为响应的长度会有所不同。尽管 TorchServe 支持持续批处理(动态添加和移除请求的能力),但此模式仅支持静态最大批处理大小。随着 PagedAttention 的引入,即使最大批处理大小的假设也变得更加灵活,因为 vLLM 可以以高度适应性的方式组合不同长度的请求,以优化内存利用率。

为了实现最优的内存利用率,即填充内存中的未使用空隙(想象俄罗斯方块),vLLM 需要完全控制在任何给定时间处理哪些请求的决策。为了提供这种灵活性,我们不得不重新评估 TorchServe 如何处理用户请求。我们引入了 异步模式(见下图),而不是之前的同步处理模式,在该模式下,传入的请求被直接转发到后端,使其可供 vLLM 使用。后端将请求输入到 vllm.AsyncEngine,该引擎现在可以从所有可用请求中进行选择。如果启用了流式传输模式并且请求的第一个 token 可用,后端将立即发送结果,并继续发送 token 直到生成最后一个 token。

flow diagram

我们对 VLLMHandler 的实现使用户能够通过配置文件快速部署任何与 vLLM 兼容的模型,同时仍然通过自定义 handler 提供相同的灵活性和可定制性。用户可以通过继承 VLLMHandler 并覆盖相应的类方法来添加例如自定义的 预处理后处理 步骤。

我们还支持单节点多 GPU 分布式推理,其中我们将 vLLM 配置为使用模型的张量并行分片,以增加小型模型的容量或支持不适合单个 GPU 的大型模型,例如 70B Llama 变体。以前,TorchServe 仅支持使用 torchrun 进行分布式推理,其中会启动多个后端 worker 进程来分片模型。vLLM 在内部管理这些进程的创建,因此我们 为 TorchServe 引入了新的“custom” parallelType,该类型启动单个后端 worker 进程并提供分配的 GPU 列表。后端进程可以在必要时启动自己的子进程。

为了方便将 TorchServe + vLLM 集成到基于 docker 的部署中,我们提供了一个单独的 Dockerfile,该 Dockerfile 基于 TorchServe 的 GPU docker 镜像,并将 vLLM 添加为依赖项。我们选择将两者分开,以避免增加非 LLM 部署的 docker 镜像大小。

接下来,我们将演示在具有四个 GPU 的机器上使用 TorchServe + vLLM 部署 Llama 3.1 70B 模型所需的步骤。

循序渐进指南

对于本循序渐进指南,我们假设 TorchServe 的安装已成功完成。目前,vLLM 不是 TorchServe 的强制依赖项,因此我们使用 pip 安装该包

$ pip install -U vllm==0.6.1.post2

在接下来的步骤中,我们将(可选地)下载模型权重、解释配置、创建模型存档、部署和测试它

1.(可选)下载模型权重

此步骤是可选的,因为 vLLM 也可以在模型服务器启动时处理权重的下载。然而,预先下载模型权重并在 TorchServe 实例之间共享缓存文件,对于存储使用和模型 worker 的启动时间都有好处。如果您选择下载权重,请使用 huggingface-cli 并执行

# make sure you have logged into huggingface with huggingface-cli login before
# and have your access request for the Llama 3.1 model weights approved

huggingface-cli download meta-llama/Meta-Llama-3.1-70B-Instruct --exclude original/*

这会将文件下载到 $HF_HOME 下,如果您想将文件放在其他位置,可以修改该变量。请确保您在运行 TorchServe 的任何地方更新此变量,并确保它有权访问该文件夹。

2. 配置模型

接下来,我们创建一个 YAML 配置文件,其中包含模型部署所需的所有参数。配置文件的第一部分指定前端应如何启动后端 worker,该 worker 最终将在 handler 中运行模型。第二部分包含后端 handler 的参数,例如要加载的模型,以及 vLLM 本身的各种参数。有关 vLLM 引擎可能配置的更多信息,请参阅此链接

echo '
# TorchServe frontend parameters
minWorkers: 1            
maxWorkers: 1            # Set the number of worker to create a single model instance
startupTimeout: 1200     # (in seconds) Give the worker time to load the model weights
deviceType: "gpu" 
asyncCommunication: true # This ensures we can cummunicate asynchronously with the worker
parallelType: "custom"   # This lets TS create a single backend prosses assigning 4 GPUs
parallelLevel: 4

# Handler parameters
handler:
    # model_path can be a model identifier for Hugging Face hub or a local path
    model_path: "meta-llama/Meta-Llama-3.1-70B-Instruct"
    vllm_engine_config:  # vLLM configuration which gets fed into AsyncVLLMEngine
        max_num_seqs: 16
        max_model_len: 512
        tensor_parallel_size: 4
        served_model_name:
            - "meta-llama/Meta-Llama-3.1-70B-Instruct"
            - "llama3"
'> model_config.yaml

3. 创建模型文件夹

创建模型配置文件 (model_config.yaml) 后,我们现在将创建一个模型存档,其中包含配置和附加元数据,例如版本信息。由于模型权重很大,我们不会将它们包含在存档中。相反,handler 将通过模型配置中指定的 model_path 访问权重。请注意,在此示例中,我们选择使用“no-archive”格式,该格式会创建一个包含所有必要文件的模型文件夹。这使我们能够轻松修改配置文件进行实验,没有任何障碍。之后,我们也可以选择 mar 或 tgz 格式来创建更易于传输的制品。

mkdir model_store
torch-model-archiver --model-name vllm --version 1.0 --handler vllm_handler --config-file model_config.yaml --archive-format no-archive --export-path model_store/

4. 部署模型

下一步是启动 TorchServe 实例并加载模型。请注意,我们已为本地测试禁用了 token 认证。强烈建议在公开部署任何模型时实现某种形式的认证。

要启动 TorchServe 实例并加载模型,请运行以下命令

torchserve --start --ncs  --model-store model_store --models vllm --disable-token-auth

您可以通过日志语句监视模型加载的进度。模型加载完成后,您可以继续测试部署。

5. 测试部署

vLLM 集成使用与 OpenAI API 兼容的格式,因此我们可以使用专门的工具或 curl 来实现。我们在此使用的 JSON 数据包括模型标识符以及提示文本。其他选项及其默认值可以在 vLLMEngine 文档中找到。

echo '{
  "model": "llama3",
  "prompt": "A robot may not injure a human being",
  "stream": 0
}' | curl --header "Content-Type: application/json"   --request POST --data-binary @-   http://localhost:8080/predictions/vllm/1.0/v1/completions

请求的输出如下所示

{
  "id": "cmpl-cd29f1d8aa0b48aebcbff4b559a0c783",
  "object": "text_completion",
  "created": 1727211972,
  "model": "meta-llama/Meta-Llama-3.1-70B-Instruct",
  "choices": [
    {
      "index": 0,
      "text": " or, through inaction, allow a human being to come to harm.\nA",
      "logprobs": null,
      "finish_reason": "length",
      "stop_reason": null,
      "prompt_logprobs": null
    }
  ],
  "usage": {
    "prompt_tokens": 10,
    "total_tokens": 26,
    "completion_tokens": 16
  }

当 streaming 为 False 时,TorchServe 将收集完整答案,并在最后一个 token 生成后一次性发送。如果我们翻转 stream 参数,我们将分段接收数据,每条消息包含一个 token。

结论

在这篇博文中,我们探讨了 vLLM 推理引擎与 TorchServe 的新的原生集成。我们演示了如何使用 ts.llm_launcher 脚本在本地部署 Llama 3.1 70B 模型,以及如何创建模型存档以在任何 TorchServe 实例上进行部署。此外,我们还讨论了如何在 Docker 容器中构建和运行解决方案,以便在 Kubernetes 或 EKS 上进行部署。在未来的工作中,我们计划启用 vLLM 和 TorchServe 的多节点推理,并提供预构建的 Docker 镜像以简化部署过程。

我们要感谢 Mark Saroufim 和 vLLM 团队在这篇博文准备过程中提供的宝贵支持。