围绕 vLLM 与 Ray 的分布式容器化部署,整理 Kubernetes 编排、Ray Serve、张量并行、流水线并行、VRAM 分配和生产运维策略。
本文由《本地大模型部署与调优》PDF 整理而来;原 PDF 无嵌入图片,正文按博客阅读方式重新排版,并补充自制架构图帮助理解。
系列导航:本地推理与 vLLM 优化 · 分布式部署与生产化
第五部分:基于vLLM与Ray的分布式容器化部署
执行摘要
本报告为一篇专家级综合指南,旨在阐述如何利用vLLM推理引擎和Ray分布式计算框架部署大语言模型(LLM)。报告特别针对用户提出的特定场景:一个由两台节点组成的、采用容器化部署的异构NVIDIA GPU环境(一台配备80GB显存的A800,另一台配备48GB显存的L20)。本文首先从Ray和vLLM的基础原理入手,建立对其核心架构的深刻理解;随后深入
剖析分布式推理的内在机制,提供一个用于精确计算显存(VRAM)需求的量化框架;接着,报告将重点分析并解决GPU异构性带来的关键挑战;最后,以可操作的最佳实践和详尽的部署步骤收尾。报告识别出的核心挑战在于,vLLM的张量并行(Tensor Parallelism)功能要求使用同构GPU。因此,本报告对流水线并行(Pipeline Parallelism)和多实例架构这两种可行的替代方案进行了评估,为用户提供了一个清晰的决策框架。
第一章:采用分布式架构进行横向扩展
当单个节点的计算资源无法满足日益增长的流量需求,或者需要部署的LLM规模超过了单个节点(即便是多GPU节点)的内存上限时,就必须采用分布式架构进行横向扩展。 Kubernetes和Ray是实现这一目标的两大核心技术,但它们解决的是不同层面的扩展问题。
1.1 使用Kubernetes进行服务编排
Kubernetes 是容器编排领域的事实标准,它为部署、扩展和管理容器化应用提供了强大的平台。
- 核心概念:在Kubernetes上部署vLLM,通常涉及定义以下资源清单(Manifests):
Deployment:负责管理vLLM Pod的副本数量,确保服务的高可用性,并支持滚动 ○
更新 。 37
Service:为一组vLLM Pod提供一个稳定的网络入口和内部负载均衡,使得其他应用 ○
可以通过一个固定的地址访问推理服务 。 38
PersistentVolumeClaim (PVC):用于为模型缓存提供持久化存储,确保在Pod重启 ○
后模型文件不会丢失 。 37
- GPU调度:要在Kubernetes集群中使用GPU,必须安装NVIDIA设备插件(NVIDIA
Device Plugin)。该插件会自动发现节点上的GPU,并将其作为一种可调度的资源暴露给Kubernetes调度器。之后,我们就可以在Pod的资源规约中明确请求GPU资源,例如 resources: limits: nvidia.com/gpu: "1" 。
- vLLM Production Stack:这是一个官方推荐的、用于在Kubernetes上进行生产级部署
的参考架构 。它超越了简单的Deployment/Service模式,引入了更复杂的组件,包括:
请求路由器(Request Router):一个智能的网关,能够根据请求的会话ID或前缀 ○
将请求路由到最有可能命中KV缓存的vLLM实例,从而最大化缓存复用率,提升性能。 可观测性堆栈(Observability Stack):集成了Prometheus和Grafana,用于监控 ○
vLLM服务的各项关键性能指标,为运维和优化提供数据支持。
1.2. 使用Ray Serve实现高吞吐量服务
是一个开源的、用于构建分布式应用的统一计算框架。Ray Serve是Ray生态系统中的一Ray 个组件,专门用于构建可扩展的模型推理服务 。 40
- 架构:Ray的核心能力在于能够将一个Python程序无缝地从单机扩展到多机集群。当
Ray 与 结合时,Ray Serve提供了一套高级API来简化分布式推理的部署和管理 。
vLLM 42
○ LLMServer :负责管理底层的vLLM推理引擎。 ○ LLMRouter :提供一个兼容OpenAI的API入口,并将请求路由到正确的模型服务,特别适用于多模型部署场景 。 42
- 分布式并行策略:对于那些因体积过于庞大而无法装入单个节点的模型(例如175B参数
的模型),Ray是实现模型并行的关键。它原生支持并简化了复杂的并行策略 : 43
○张量并行(Tensor Parallelism):将模型中的单个大层(如注意力层或MLP层)的权重矩阵切分到同一节点内的多个GPU上,协同完成计算。 ○流水线并行(Pipeline Parallelism):将模型的不同层分阶段部署到不同的节点上,形成一个计算流水线。一个请求的数据在前向传播过程中依次流经这些节点。
- 配置驱动的部署:在生产环境中,Ray Serve的部署通常由YAML配置文件来驱动 。这种 42
声明式的方法允许工程师将整个服务应用(包括模型、硬件需求、自动伸缩策略等)的定义进行版本控制,实现了基础设施即代码(Infrastructure as Code)。 Kubernetes和Ray并非相互竞争的技术,而是解决不同维度扩展问题的互补工具。 Kubernetes的核心是基础设施编排,它回答的是“如何可靠地运行和管理N个vLLM服务副本?”的问题。而Ray的核心是分布式计算,它回答的是“如何让一个超大模型的单次推理请求利用M个节点的计算能力?”的问题。在最成熟的架构中,两者往往会结合使用:通过KubeRay Operator等工具,将Ray集群部署在Kubernetes之上。在这种模式下,Kubernetes 负责管理底层的计算资源(如Pod、网络、存储),而Ray则负责管理运行在这些资源之上的分布式应用程序(vLLM的模型并行计算)。因此,架构决策路径变得清晰:对于能够在单个节点(即便是多GPU节点)上运行的模型,采用Kubernetes(如vLLM Production Stack) 进行水平扩展即可满足需求。只有当模型规模大到必须进行跨节点并行计算时,引入Ray的复杂性才具有合理性。 表4:分布式部署框架对比:Kubernetes vs. Ray Serve 维度 Kubernetes ( 原生/vLLM Production Ray Serve Stack)
主要用途 容器编排与服务管理 分布式应用计算框架扩展方式 水平扩展(增加服务副本/Pod) 模型并行(张量并行、流水线并行核心问题 如何管理和扩展多个独立的服务实例 如何将单个大型计算任务分布到多节点
复杂度 中等(需要理解K8s概念) 高(需要理解分布式计算和Ray的程模型) 理想场景 为多个适合单节点部署的模型提供高 部署单个、因体积过大而无法在单可用和负载均衡服务 点上运行的超大型模型
第二章:Ray分布式计算框架的基础原理
2.1. 核心架构:Ray集群的剖析
为了理解分布式执行,必须首先解构Ray集群的基础组件,这些组件协同工作,将多台机器的资源抽象为一个统一的计算池 。 1
头节点 (Head Node) 头节点是集群的中央协调器。它运行着全局控制存储(Global Control Store, GCS),负责管理系统元数据、Actor的位置信息以及节点健康状态。对于启动作业和连接新的工作节点而言,头节点是唯一的联系点 。GCS的健康检查机制至关重要;如果一个工作节点未能按时
发送心跳,GCS会将其标记为死亡,这可能导致集群不稳定,是在配置不当的多节点环境中常见的问题 。 5
工作节点 (Worker Nodes) 工作节点是执行实际计算任务的机器。每个工作节点上都运行一个 raylet 进程,该进程包含一个本地调度器和一个对象存储,负责管理该特定节点上的资源和任务 。这些节点从头节 3
点接收任务并执行计算。 全局控制存储 (Global Control Store, GCS) 是位于头节点上的一个键值存储系统,是整个集群的“大脑”。它通过维护整个分布式系GCS 统的状态来确保容错能力和元数据管理 。GCS的可靠性对于集群的稳定运行至关重要。
2.2. Ray的并行化原语:任务与Actor
提供了两种核心API来实现Python代码的并行化,这两种API在无状态和有状态计算之间Ray 做出了关键区分。 远程任务 (Remote Tasks) 通过在函数上使用 @ray.remote 装饰器,Ray能够将无状态函数异步地调度到工作节点上执行。这些被称为“任务”(Tasks)的函数调用由Ray根据集群的资源可用性进行调度,从而实
现大规模的函数式并行计算 。 1
有状态Actor (Stateful Actors) 是Ray中有状态分布式应用的核心。通过在类上使用 @ray.remote 装饰器,一个Actor实Actor 例被创建并运行在自己专用的工作进程中,它能够在多次方法调用之间保持其内部状态。对同一个Actor实例的所有方法调用都是串行执行的,这保证了状态的一致性 。 7
vLLM正是利用了Ray的Actor模型来创建和管理其分布式模型工作者(workers)。当vLLM 以 tensor_parallel_size=N 初始化时,它会请求Ray实例化N个Actor,每个Actor持有模型权重的一部分。Ray的GCS负责管理这些Actor的生命周期和位置,而vLLM的引擎则协调对它们的调用。这种架构比手动管理多进程或SSH进程更为健壮和可扩展。此外,Actor可以被配置特定的资源需求,例如 num_gpus=1 ,Ray的调度器会据此来分配GPU资源 。 8
2.3. 分布式对象存储:零拷贝数据共享
Ray 通过其独特的对象存储机制来管理数据,并实现任务与Actor之间的高效通信。
对象存储 (Object Store) 集群中的每个节点都有一个由 raylet 管理的共享内存对象存储 。这使得同一节点上的
Ray 3
多个工作进程能够访问相同的数据,而无需进行序列化和反序列化的开销,实现了零拷贝读取。 对象引用 (ObjectRefs) 当一个任务或Actor方法被调用时,它会立即返回一个 ObjectRef 。这本质上是一个指向未来结果的指针(Future或Promise),该结果最终将被存放在对象存储中。实际的数据可以通过调用 ray.get() 来获取 。这种基于Future的异步模型是Ray构建和执行计算图的基础。
2.4. Ray Serve :用于生产级机器学习的可扩展服务层
是Ray生态中专门用于构建和部署在线推理服务的库,它为管理vLLM等复杂应用
Ray Serve
提供了高层抽象。 部署与副本 (Deployments and Replicas) 部署(Deployment)和副本(Replica)是Ray Serve的核心概念。一个 Deployment 是一个可扩展的 Replica 组。每个 Replica 都是一个运行着应用逻辑副本(例如vLLM引擎)的
Ray Actor 。 11
模型组合与路由
Ray Serve支持构建复杂的推理图,其中不同的部署可以通过 DeploymentHandles 相互调用。
一个入口部署(Ingress Deployment)负责处理外部HTTP请求,通常与FastAPI集成,并将请求路由到后端的模型部署 。 11
ray.serve.llm 库提供的 LLMRouter 就是一个为此目的预构建的入口 。 13
自动扩缩容 (Autoscaling)
Ray Serve能够根据请求负载自动增减部署的副本数量,甚至支持缩容至零以节约资源 。值 13
得注意的是,Ray Serve的 LLMServer 并非vLLM核心逻辑的替代品,而是一个高层抽象。 LL MServer 本质上是一个预先打包好的Ray Serve Deployment ,专门用于初始化和运行像vLLM 这样的底层推理引擎。 LLMConfig 类则是一个数据结构,它收集所有必要的参数(如模型ID、张量并行大小、自动扩缩容配置),并将其转换为Ray Serve Deployment 和底层 vllm.L LM 引擎调用所需的正确参数 。因此,用户并非在vLLM和Ray Serve之间做选择,而是使用
Ray Serve的高级API来将vLLM实例作为可扩展的、生产就绪的服务进行管理和部署,这极大
地简化了配置、自动扩缩容和多模型路由的复杂性 。 15
第三章:使用vLLM和Ray构建分布式LLM推理架构
3.1 模型并行策略:张量并行 vs. 流水线并行
当单个GPU无法容纳整个LLM时,需要采用模型并行策略将模型拆分到多个GPU上。vLLM 支持两种主要策略 。 30
3.1.1 张量并行 (Tensor Parallelism, TP)
张量并行是一种层内(intra-layer)并行技术。它将单个Transformer层内部的权重矩阵(例如Q, K, V投影矩阵)沿着某个维度进行切分,并将这些切片分布到多个GPU上。所有参与的GPU在同一时间协同计算同一层的不同部分,这需要频繁且高速的通信来同步计算结果 。 31
TP能有效降低单个GPU的内存占用,但对节点间的网络延迟极为敏感 。 36
3.1.2 流水线并行 (Pipeline Parallelism, PP)
流水线并行是一种层间(inter-layer)并行技术。它将模型的多个连续层作为一个整体(称为一个“阶段”或“stage”)分配给不同的GPU,形成一个计算流水线。数据(即激活值)在通过模型的各层时,会依次从一个GPU流向下一个GPU。这种方式对网络延迟的敏感度远低于TP,但可能会因为流水线“气泡”(pipeline bubbles)而导致GPU空闲,即某些GPU在等待上一阶段的输出时无事可做 。选择TP还是PP,本质上是在延迟和带宽之间进行权衡,而这
个权衡由硬件拓扑结构决定。TP需要频繁但小量的数据传输(用于All-Reduce操作的激活
值),使其对低延迟的互连网络极为敏感 。PP则需要不频繁但大量的数据传输(微批次的
整个激活张量),使其对高带宽的互连网络更为敏感 。在单个节点内部,GPU通过NVLink 37
或PCIe等极低延迟、极高带宽的总线连接,这是TP的理想运行环境 。而跨节点之间通常通 39
过以太网连接,其延迟比NVLink高出几个数量级,这使得TP频繁的All-Reduce通信成为性能瓶颈 。因此,行业标准实践(vLLM也遵循此实践)是在节点内使用TP,在节点间使用
PP。
3.2. 深入张量并行:权重分片与All-Reduce操作
为了更好地理解TP,需要深入其内部机制。
3.2.1 列并行与行并行
权重矩阵的切分方式是有讲究的。以一个MLP模块为例,第一个线性层的权重矩阵通常按列(column)切分,而第二个线性层的权重矩阵则按行(row)切分。这种设计确保了在通信之后,数学计算的结果是等价的 。 31
3.2.2 All-Reduce 操作
是TP中至关重要的集体通信步骤。在每个GPU使用其权重分片完成部分计算后, All-Reduce 系统会执行一次 All-Reduce 操作。该操作将所有GPU上的部分结果进行求和,并将最终的完整结果分发回每个GPU。这确保了在进入下一计算步骤之前,所有GPU的状态都是同步和一致的 。这个操作是TP网络开销的主要来源。
3.3 Ray 作为vLLM的分布式后端
在多节点部署中,vLLM依赖Ray来管理其分布式工作者。
3.3.1 执行器后端 (Executor Backend)
可以配置不同的“执行器后端”来管理其分布式工作者。对于单节点多GPU场景,默认使vLLM 用 的 multiprocessing 。对于跨节点部署,则必须使用 ray 作为后端 。这通过命令
Python 30
行参数 --distributed-executor-backend=ray 进行配置。
3.3.2 通过Ray Actor管理工作者
当vLLM以 tensor_parallel_size=N 和Ray后端启动时,它不会自己创建进程。相反,它会向Ray集群请求启动N个 Ray Actor 进程。每个Actor被分配一个特定的GPU,并加载其对应的模型分片 。vLLM引擎通过向这些Actor发送远程方法调用的方式来协调整个推理过程。
3.3.3 Ray 放置组与调度 (Placement Groups)
Ray的放置组(Placement Groups)功能可以用来原子性地保留一组资源(例如N个GPU)。 PACK 调度策略会尝试将组内的所有工作者放置得尽可能近,理想情况下是在同一个节点上,这对于TP的性能至关重要 。然而,vLLM的TP实现隐式地假设其工作者位于低延
迟互连网络上(即同一节点),但它本身没有机制来强制这种布局。当使用Ray后端时, vLLM将N个工作者Actor的放置任务委托给了Ray调度器. Ray的默认调度器可能会将这些30
Actor分散到集群中任何可用的GPU资源上,这可能导致一个TP组被分割到两台物理机器上。尽管这在技术上是可能的 ,但在用户场景中的高延迟以太网上运行TP,会因All-
Reduce瓶颈导致灾难性的性能下降。
3.4. 分布式推理请求的生命周期
一个请求在vLLM+Ray堆栈中的处理流程如下 : 17
1. API服务器 / AsyncLLM引擎: HTTP请求到达与OpenAI兼容的API服务器。 AsyncLLM 引擎处理请求的词元化(tokenization),并通过进程间通信(IPC)异步地将请求发送到中央的 EngineCore 。 2. EngineCore与调度器: EngineCore 接收到请求。其内部的调度器使用连续批处理算法将请求加入队列,并决定何时处理它。同时,通过 KVCacheManager 为请求分配KV缓存块。 3. ModelExecutor (Ray): 调度器将一个批次的工作传递给 ModelExecutor 。该执行器随后将计算任务广播给所有分布式的 ModelRunner Ray Actor。 4. ModelRunners (Ray Actors): 每个 ModelRunner Actor在其指定的GPU上,使用自己的模型权重分片执行前向传播计算。 5. 同步 (All-Reduce): 各个Actor通过网络执行一次 All-Reduce 操作,以同步它们的计算部分结果。 6. 词元生成: 等级为0(rank 0)的工作者执行最终的采样步骤以生成下一个词元,然后将该词元返回给 EngineCore 。对于后续的每个词元,此过程将重复进行。
第四章:分布式环境下的VRAM分配与管理
4.1. VRAM 计算的量化框架
本章提供了一套具体的、分步的公式,用于计算在分布式部署中VRAM的需求。这套框架将总VRAM需求分解为三个主要部分:模型权重、激活内存和KV缓存,从而实现精确的资源规划。
4.2. 组件一:模型权重内存
这是加载模型参数所需的静态内存。
-
公式: MemoryWeights=TP_Size(Total_Parameters×Bytes_per_Parameter)
-
解释: 模型的总参数量乘以每个参数的数据类型大小(例如,FP16/BF16为2字节,INT8
为1字节)得到总的模型大小 。在使用张量并行( TP_Size )时,这些权重几乎被均匀
地分片到TP组中的N个GPU上 。 假如qwen2.5-32b FP16量化的模型所需的权重内存就
是32x2=64g。
4.3. 组件二:激活内存
这是在前向传播期间用于存储中间计算结果的瞬时内存。
-
公式 (启发式): MemoryActivation_per_GPU≈(MemoryWeights_per_GPU×0.25)
-
解释: 精确计算激活内存非常复杂,它依赖于序列长度、批处理大小和具体的核函数实
现 。然而,在推理场景中(此时无需为反向传播存储激活值),一个常用且有效的启发
式方法是为激活内存预留相当于分片后模型权重内存25%的空间 。这部分内存同样分布 33
在各个TP工作者上。qwen2.5-32b FP16量化的模型所需的激活内存就是16g。
4.4. 组件三:KV缓存
这是动态内存部分,用于存储过去词元的Key-Value状态。在高吞吐量的服务环境中,这部分通常是VRAM的最大消耗者。
- 公式 (每词元): MemoryKV_per_Token
=2×Num_Layers×Num_KV_Heads×Head_Dim×Bytes_per_Parameter
- 公式 (总量): Total_MemoryKV=MemoryKV_per_Token
×Num_Concurrent_Requests×Avg_Sequence_Length
- 解释: 对于序列中的每个词元,系统必须为每一层的每个注意力头存储一个Key向量和一
个Value向量(因此乘以2)。每个向量的维度是 Head_Dim 。这里使用 Num_KV_Heads 是为了兼容分组查询注意力(Grouped-Query Attention, GQA)机制,在这种机制下,多个查询头可以共享同一组Key/Value头 。 45
- TP下的分布: 至关重要的是,KV缓存也同样被分区到各个TP工作者上。每个GPU只需存
储它所负责的那些注意力头的KV对 。因此,每个GPU所需的KV缓存内存 33
为 Total_MemoryKV/TP_Size。 张量并行(TP)为KV缓存提供了一种“VRAM乘数效应”。单个GPU在加载模型权重和预留激活内存后,剩余的VRAM可用于KV缓存 。当使用N个GPU进行TP时,不仅模型权重和激活
内存被N等分,总的KV缓存容量也被划分到这N个GPU上 。这意味着服务的总有效KV缓存 33
容量变成了所有参与GPU上可用KV缓存空间的总和。例如,如果单个GPU剩余17GB用于KV 缓存,一个8-GPU的TP设置不仅是每个GPU有17GB,而是拥有一个 17GB * 8 = 136GB 的集体缓存池,用于存储所有并发请求的KV状态 。这使得系统能够支持的并发用户数或最大上 33
下文长度远超单个GPU的能力。
第五章:应对异构GPU部署的挑战
5.1. vLLM 张量并行的同构要求
这是用户当前设置面临的核心问题。vLLM的分布式推理,特别是张量并行,要求所有参与的GPU在型号和架构上保持一致,以确保正确的操作和最佳性能 。底层的集体通信库(如 48
NCCL)针对同构硬件进行了优化,并常常假定所有设备具有相同的计算能力。TP涉及高度同步的细粒度计算。如果GPU之间在计算速度、内存带宽或架构上存在差异,较快的GPU将在All-Reduce步骤中不断等待较慢的GPU,这将严重破坏性能,甚至可能导致同步错误。
5.2. 用户A800与L20配置分析
为了凸显其异构性,需要对这两款GPU进行技术比较。
- NVIDIA A800: 这是一款基于Ampere架构的数据中心GPU,是A100的变体,配备80GB的
HBM2e内存。它专为高性能计算设计,具有强大的FP16/BF16性能。
- NVIDIA L20: 这是一款基于Ada Lovelace架构的GPU,配备48GB的GDDR6内存。它主要
为推理和图形处理优化,拥有更新一代的Tensor Cores,但其内存子系统特性与A800不同。 尽管没有直接的A800与L20的基准测试数据,但可以通过代理进行比较。L40S(Ada架构, 与L20相似)和RTX 6000 Ada表现出相似的性能 。A100(Ampere架构,与A800相似)则
属于上一代产品。它们的性能特征,尤其是在不同精度(FP16 vs INT8)下的表现会有所不同,这使得它们不适合共同参与张量并行 。 50
5.3. 潜在策略一:使用流水线并行作为替代方案
这是第一个允许单个模型跨越两台机器的可行变通方案。
- 实现: 将模型的一部分层分配给A800,其余的层分配给L20。在vLLM中,这可以通过设
置 --pipeline-parallel-size=2 和 --tensor-parallel-size=1 来实现 。Ray后端将负责在
每台机器上放置一个流水线阶段(作为一个Ray Actor)。
- 优点: 允许将一个大于48GB的单个模型部署到两台机器上,有效地聚合了它们的VRAM。
它对节点间网络延迟的敏感度远低于TP 。 37
- 缺点: 容易产生“流水线气泡”,导致GPU空闲,降低整体利用率。拥有较小VRAM的机器
(L20, 48GB)会成为层分配的瓶颈;不能将一个需要超过48GB VRAM的层块分配给该阶段。在异构GPU上平衡各阶段的计算时间以确保负载均衡是一项不小的挑战 。 37
第六章:张量并行的网络架构与通信协议
6.1. 互连的关键作用:带宽与延迟的影响
网络性能是分布式深度学习的决定性因素,尤其对于张量并行中的All-Reduce操作。在分布式训练和推理中,GPU在计算和通信之间交替。如果通信缓慢,GPU将空闲等待数据,浪费昂贵的计算周期,网络很容易成为主要的性能瓶颈 。延迟(第一个比特的传输时间)影响
频繁的小消息,而带宽(数据传输速率)影响大消息。TP的All-Reduce涉及许多同步步骤, 使其对延迟极为敏感 。 35
6.2. 高性能互连与标准以太网
不同类型的网络硬件对分布式性能有巨大影响。
- 节点内互连 (NVLink, PCIe): 在单台机器内部,GPU通过NVLink或PCIe总线通信。这些
技术提供极高的带宽和超低的延迟,这也是TP在单台多GPU服务器上表现出色的原因 。 57
- 节点间互连 (InfiniBand, RoCE, Ethernet):
InfiniBand: 一种专为HPC和AI设计的专用、低延迟、高带宽网络结构。它使用 ○
RDMA(远程直接内存访问)技术绕过CPU和内核网络堆栈,实现约2µs的延迟 。 40
RoCE (RDMA over Converged Ethernet): 一种允许RDMA在标准以太网上运行的 ○
标准。它提供接近InfiniBand的性能(约5µs延迟),但需要精心配置的“无损”以太网交换设备 。 40
标准TCP/IP以太网: 最常见的网络技术。其延迟要高出几个数量级(约50µs),因为 ○
数据必须在发送和接收端都经过内核的网络堆栈 。 40
6.3. 对用户双节点设置的建议
用户的两台标准Linux机器几乎可以肯定是- 通过标准以太网连接的。这种连接的高延迟将使得TP所需的频繁All-Reduce同步成为一个严重的瓶颈。性能会非常差,甚至可能比在单个GPU上运行还要慢。这再次印证了为什么vLLM官方不支持跨节点TP,以及为什么应该避免这种做法 。然而,这种节点间网络对于流水线并行是完全足够的,因为PP每次推理传递只
涉及一次较大的数据传输,因此更能容忍以太网的较高延迟 。这使得PP成为在必须将单个 37
模型分割到两台机器上时唯一技术上合理的选择。 并行策略的选择不仅仅是软件算法的抽象,它与物理硬件拓扑紧密相连。TP是为单服务器主板的拓扑结构(通过NVLink/PCIe实现低延迟、高带宽)设计的算法。而PP则更适合数据中心机架的拓扑结构(通过以太网实现较高延迟、较低带宽)。用户试图跨两个节点运行TP, 实际上是试图将一个“主板级”的算法应用于一个“机架级”的物理拓扑。算法的通信模式与物理链路特性之间的不匹配是预期性能崩溃的根本原因。因此,选择正确的并行策略不仅取决于模型,还取决于将模型的通信需求映射到集群的物理网络拓扑上。
第七章:分步部署指南与最佳实践
7.1. 先决条件:主机设置
在开始部署之前,确保在两台Linux机器上都已安装以下软件。
-
NVIDIA驱动程序: 为A800和L20安装合适的驱动程序 。 62
-
Docker引擎: 安装最新版本的Docker 。
-
NVIDIA Container Toolkit: 这是允许Docker容器访问主机GPU的关键组件。需要遵循官
方指南进行安装和验证 。 62
7.2. 使用Docker容器配置多节点Ray集群
本节提供启动Ray集群的实用命令行指南。对于此用例,建议直接使用 ray 命令行工具和vLLM的辅助脚本,而不是Docker Compose或Swarm,因为前者更直接、简单。
- 第一步:创建自定义Docker镜像
提供一个示例Dockerfile,它基于vllm/vllm-openai镜像,并添加任何必要的依赖项,以确保两个节点上的环境完全一致 。
- 第二步:启动头节点容器
提供头节点的docker run命令,并解释关键参数: ○ --gpus all 和 --runtime=nvidia : 授予GPU访问权限 。
○ --network host : 至关重要。这会绕过Docker的网络虚拟化,使Ray头节点能够通过主机的IP地址被工作节点访问,确保了Ray节点间的直接通信 。 64
○ --shm-size : 设置一个较大的共享内存大小(例如 64g )以防止Ray对象存储出错。5
- 第三步:启动Ray头节点进程
在头节点容器内运行ray start --head命令,并记下其IP地址和端口。
- 第四步:启动并连接工作节点
在工作节点上运行类似的docker run命令,然后在容器内运行ray start -- address='<head_node_ip>:6379'以将其连接到集群。
7.3. 使用流水线并行启动vLLM服务器
本节提供启动vLLM服务的最终命令,假设用户根据第五章的分析选择了流水线并行策略。
- 命令:
复制代码
vllm serve <model_name> \
--distributed-executor-backend ray \
--pipeline-parallel-size 2 \
--tensor-parallel-size 1
- 解释:
○ --distributed-executor-backend ray : 告知vLLM使用正在运行的Ray集群来调度其工作者。 ○ 指示vLLM创建两个流水线阶段。Ray将自动把这两个 --pipeline-parallel-size 2 : 阶段调度到集群中可用的两个节点/GPU上。
7.4. 最佳实践与监控
- 配置: 建议设置关键的vLLM参数,如 —gpu-memory-utilization (通常设置为 0.9 以留
出空间给CUDA上下文)和 --max-model-len 以有效管理VRAM 。 67
- 模型缓存: 在共享卷或每台主机上预先下载模型,并将其挂载到容器中,以避免在容器启
动时重复下载 。 30
- 监控: 通过访问头节点IP地址的8265端口来检查Ray仪表板,以验证两个节点是否都已成
功加入集群,以及vLLM的Actor是否已正确地放置在GPU上。在主机上使用 nvidia-smi 或 nvtop 来实时监控推理过程中的GPU利用率 。 62
第八章:结论与建议
本报告为在特定的双节点异构GPU(NVIDIA A800和L20)环境中,使用vLLM和Ray进行大语言模型的分布式、容器化部署提供了深入的分析和实践指导。 核心结论如下: 1. 张量并行在异构GPU上不可行: vLLM的张量并行(TP)功能要求所有参与的GPU在架构和性能上保持同构。用户所拥有的A800(Ampere架构)和L20(Ada Lovelace架构)在硬件上存在显著差异,因此无法直接使用TP进行模型分片。尝试在标准以太网连接的两个节点间运行TP,会因 All-Reduce 操作的高昂网络延迟而导致性能灾难。 2. 流水线并行是唯一可行的单模型分布式策略: 对于需要跨越两台机器部署的单个大型模型(即模型大小超过任一单GPU的可用VRAM),流水线并行(PP)是唯一技术上合理的选择。PP对网络延迟的容忍度更高,能够适应节点间的以太网连接。然而,其实施需要仔细平衡各阶段的层数和计算负载,以最小化“流水线气泡”并避免由VRAM较小的L20 GPU造成的瓶颈。
3.独立实例架构是更简单、更灵活的替代方案: 如果待部署的模型能够完整地装入单个GPU (A800的~40GB可用VRAM或L20的48GB VRAM),那么最佳实践是放弃分布式模型并行。取而代之,应在每台机器上部署一个独立的、单节点的vLLM服务实例。这种架构实现简单,避免了分布式通信的复杂性和开销,并能提供更高的总吞吐量和更低的服务延迟。此外,它还提供了更大的操作灵活性,例如可以同时服务两个不同的模型。 最终建议:
- 如果模型大小 > 48GB: 用户应采用**流水线并行(PP)**策略。配置vLLM时设置 —pipel
ine-parallel-size=2 和 --tensor-parallel-size=1 ,并使用Ray作为分布式后端。部署时需特别关注两个阶段的VRAM占用情况,确保分配给L20的层所需的内存不超过其48GB的容量。
- 如果模型大小 <= 40GB: 强烈建议用户采用独立的vLLM实例架构。在两台机器上分别部
署vLLM服务,并在前端设置一个简单的负载均衡器或路由层。这将是实现最高性能、最低复杂度和最大灵活性的最优选择。 无论选择哪种策略,都必须通过Docker容器来确保环境的一致性,并使用 --network host 配置以实现可靠的节点间通信。通过遵循本报告中提供的VRAM计算框架和部署步骤,用户可以为其特定的硬件和模型需求做出明智的架构决策,并成功地部署一个高性能、稳定可靠的LLM推理服务。
第六部分:大语言模型架构和格式
第 1 章: 现代开源大语言模型概览本节将对用户查询中提及的特定模型进行详细的架构概述。其目标是超越表面的参数数量, 建立对其设计哲学、关键创新以及这些架构选择如何影响下游部署考量的深入理解。
1.1 DeepSeek 系列 (V2, V3, R1): 大规模混合专家模型 (MoE) 的先驱
系列模型在开源社区中以其对混合专家(Mixture-of-Experts, MoE)架构的积极DeepSeek 采用而著称,这直接应对了在不产生过高推理成本的情况下扩展模型规模的挑战。
1.1.1 架构深度解析
的核心设计理念是通过稀疏激活实现计算效率。例如,DeepSeek-V2 拥有 2360 DeepSeek 亿(236B)的总参数量,但在处理每个 token 时仅激活其中的 210亿(21B)参数 。这一 1
策略在 DeepSeek-V3 中得到进一步扩展,其总参数量达到 6710亿(671B),而激活参数为 370亿(37B) 。这种设计显著降低了单次前向传播的计算量(FLOPs),使得模型在保
持巨大知识容量的同时,推理成本远低于同等规模的密集型模型。
1.1.2 关键创新
DeepSeek 不仅采用了 MoE 架构,还引入了多项关键创新以优化其性能和效率。其中,多头隐注意力(Multi-head Latent Attention, MLA) 机制通过压缩 KV 缓存,有效地降低了推理过程中的显存占用,相较于 DeepSeek 67B 模型,KV 缓存减少了 93.3% 。此外, 1
DeepSeek-V3 开创性地采用了**无辅助损失(auxiliary-loss-free)**的负载均衡策略 。在传 3
统的 MoE 模型中,通常需要一个辅助损失函数来确保各个专家被均匀地使用,但这可能会对模型性能产生负面影响。DeepSeek 的新策略在不牺牲性能的前提下实现了负载均衡,这对于推理引擎的实现和优化至关重要。
1.1.3 推理专业化 (DeepSeek-R1)
DeepSeek-R1 代表了一种新颖的训练范式。它直接在基础模型上进行大规模强化学习(Reinforcement Learning, RL),而没有经过传统的监督微调(Supervised Fine-Tuning, SFT)作为预备步骤 。这种方法使模型能够自主探索“思维链”(Chain-of-Thought, CoT)等
复杂推理路径,从而在数学、编码和推理任务上展现出卓越的性能。 此外,DeepSeek 团队还展示了一种能力迁移策略,即通过“蒸馏”将 DeepSeek-R1 强大的推理能力迁移到更小、更通用的密集型模型上。他们发布了基于 Llama 和 Qwen 架构的蒸馏版本,这些模型在继承了强大推理能力的同时,保持了原有基础模型的兼容性和易用性 。 4
1.2 Qwen 体系 (Qwen2.5, Qwen3): 多功能性的典范
阿里巴巴的 Qwen(通义千问)系列模型以其广泛的模型尺寸、多样的功能和对前沿技术的快速整合而闻多。
1.2.1 广泛的模型谱系
Qwen 的核心策略之一是提供覆盖从小型到巨型模型的全面选择。Qwen2.5 系列涵盖了从 5 亿(0.5B)到 720亿(72B)的多种尺寸 ,而 Qwen3 系列则进一步扩展,包括从 6亿
(0.6B)的密集模型到 2350亿(235B)参数的 MoE 模型 。这种策略满足了从边缘设备到
数据中心级服务器的各种硬件和应用需求。
1.2.2 功能差异化:“思考模式”
的一个独特创新是其可切换的“思考模式”(thinking mode) 。在处理复杂的逻辑推Qwen3 7
理、数学或编码任务时,可以启用此模式,模型会生成详细的推理步骤( <think>...</think > 块),然后再给出最终答案。而在进行常规对话时,可以切换到更高效的“非思考模式”。 这是一个应用层面的创新,它不改变模型权重,而是通过特定的 prompt 模板或 API 参数来控制生成行为,这对推理框架的适配提出了明确要求。
1.2.3 拓展边界:多模态与长上下文
Qwen 系列在模型能力的前沿领域也进行了深入探索。Qwen2.5-Omni 是一款端到端的多模态模型,能够同时处理文本、图像、音频和视频输入,并生成文本和语音输出 。而 11
Qwen2.5-1M 和 Qwen3 的部分版本则支持高达 100万 token 的超长上下文窗口,这得益于诸如**双块注意力(Dual Chunk Attention, DCA)**和 MInference 等稀疏注意力技术 。这 13
些高级功能对推理栈的内存管理和注意力计算内核提出了独特的挑战和要求。
1.3 Llama 王朝: Meta 的 Llama 3 及其基础性作用
Meta 的 Llama 3 系列不仅是一个强大的模型,更是一个催生了庞大开源生态系统的基础平台。
1.3.1 生态系统基石
提供了从 80亿(8B)到 4050亿(405B)的多种模型尺寸 。其相对宽松的许可证Llama 3 15
(附带月活跃用户超过 7 亿需申请商业许可的条款)极大地促进了开源社区的繁荣 。许多 16
知名的开源模型,包括前述的 DeepSeek-R1 蒸馏版,都是基于 Llama 架构进行微调的。
1.3.2 标准化架构
模型采用了一个经过充分优化和验证的 transformer 架构 。这种架构的稳定性使其成Llama 16
为一个可靠的基准,并确保了与几乎所有主流训练和推理工具的广泛兼容性。其 prompt 格式也经过了明确定义,使用如 <|begin_of_text|> 和 <|eot_id|> 等特殊 token 来界定对话结构,这为开发者提供了清晰的交互规范 。 17
1.3.3 安全与护栏
对负责任的人工智能给予了高度重视,这一点体现在其发布的 Llama Guard 3 等专用Meta 模型上 。Llama Guard 3 基于一个明确的风险分类系统,旨在对输入和输出内容进行安全
审查,防止模型被用于恶意目的。这代表了将安全机制直接嵌入模型生态系统的行业趋势。
1.4 架构综合:密集型与 MoE 范式的比较分析
通过对上述模型的分析,可以清晰地看到当前开源 LLM 架构的两个主要发展方向:以Llama 3 为代表的密集型(Dense)架构和以 DeepSeek V2/V3、Qwen3-235B 为代表的混
合专家(MoE)架构。 核心权衡这两种范式之间的核心权衡在于推理计算效率与架构简单性和参数利用率之间。
- 密集型模型:在每次前向传播中,所有参数都会参与计算。这使得其架构简单、易于优
化,并且能保证所有知识都被“激活”。但缺点是随着模型规模的扩大,推理成本会急剧增加。
- MoE 模型:通过路由机制,每次只激活一小部分专家(参数),从而在总参数量巨大的
情况下,保持较低的推理计算量。这使得 MoE 模型在性能相当的情况下,推理速度通常比同等参数规模的密集模型快得多 。 1
部署影响这种架构差异直接影响了本地部署的决策。MoE 模型虽然激活参数少,但其完整的权重文件通常非常大,对存储和内存(RAM/VRAM)的容量要求更高。此外,MoE 模型的推理过程涉及到专家路由和负载均衡等额外逻辑,这对推理引擎的实现提出了更高的要求,需要专门的优化才能发挥其性能优势。 综合来看,开源 LLM 领域并非简单的“越大越好”的参数竞赛,而是正在向三个关键战略方向分化: 1. 架构效率 (Architectural Efficiency):以 DeepSeek 为代表,专注于通过 MoE 等先进架构在单位计算成本下实现最强性能。其模型卡反复强调“经济的训练”和“高效的推理”,这表明其战略目标是在性能与成本的竞争中取胜 。 1
2. 功能多样性 (Functional Versatility):以 Qwen 为代表,采取“为每个人提供一切”的策略。从适用于边缘设备的小模型到巨型 MoE 模型,再到多模态能力和独特的“思考模式”,Qwen 旨在通过广泛的应用覆盖来占领市场 。 6
3. 生态系统标准化 (Ecosystem Standardization):以 Meta 的 Llama 为代表,通过提供一个稳定、可靠、许可证友好的基础平台,鼓励社区在其之上进行构建和创新,从而建立一个强大的生态系统 。 16
对于负责基础设施的技术负责人而言,理解这些不同的战略至关重要。选择模型不仅是一个技术决策,更是与一种特定的发展哲学对齐。如果首要任务是为特定文本生成任务追求极致的性价比,DeepSeek 是一个强有力的竞争者。如果需要一个能够应对多种不同应用的通用工具箱,Qwen 则极具吸引力。如果需要最广泛的社区支持和一个稳定、可预测的基础, Llama 则是默认的稳妥之选。这种认知可以避免将这些模型视为可互换的商品,从而做出更具战略性的技术选型。 第 2 章: 基础技术:训练框架与模型文件格
本节将深入剖析支撑整个 LLM 生态系统的软件和标准。理解这些基础技术对于诊断问题、确保兼容性以及在工具选型上做出明智决策至关重要。
2.1 训练框架三巨头:PyTorch 的主导地位及 TensorFlow 和 JAX 的角色
2.1.1 PyTorch 作为通用语言
压倒性的证据表明,PyTorch 已成为 LLM 研究和开发领域的主导框架。其动态计算图、 Pythonic 的编程风格以及与 Hugging Face 生态系统的紧密集成,使其成为事实上的行业标准 。研究显示,Hugging Face Hub 上近 92% 的模型是 PyTorch 独占的 。 19 22
2.1.2 TensorFlow 和 JAX
尽管 TensorFlow 在生产环境中,尤其是在遗留系统和移动端部署(TFX, TensorFlow Lite) 方面仍然保持强势 ,但其在尖端 LLM 研究中的使用率已明显落后于 PyTorch 。由19 22
Google 开发的 JAX 则是一颗冉冉升起的新星,以其在加速器(TPU/GPU)上的卓越性能和函数式编程范式而闻名。这使其在高性能计算研究圈(如 Google Brain, DeepMind)中备受欢迎,但在通用的开源模型发布中则较为少见 。 22
2.1.3 对部署的启示
对于 MLOps 工程师而言,一个安全的假设是,他们遇到的几乎所有新的开源模型都将以PyTorch 兼容的格式发布。因此,核心任务不是进行框架间的转换,而是如何将这些PyTorch 的产物高效地用于推理。
2.2 模型仓库剖析:解构基本文件
一个典型的 Hugging Face 模型仓库通常包含以下核心文件,它们共同定义了一个模型的全部信息:
- 模型权重: 文件名通常为 model.safetensors 或 pytorch_model.bin 。这是模型的核心,
包含了经过训练的所有参数。
- 配置文件: config.json 文件定义了模型的架构、维度和超参数,例如层数、隐藏层大
小、注意力头数等 。推理引擎需要读取此文件来构建正确的模型结构。
- 分词器文件: 一组文件,如 tokenizer.json , tokenizer_config.json , special_tokens_ma
p.json 等,定义了如何将原始文本转换为模型能够理解的 token 序列。
- 生成配置文件: generation_config.json 文件指定了模型生成文本时的默认参数,如温度
(temperature)、top_p 等 。 27
2.3 分发黄金标准:用于安全、可互操作模型权重的 Safetensors
2.3.1 为何选择 Safetensors?
格式的出现主要是为了解决 PyTorch 默认 .bin 文件所使用的 pickle 格式存Safetensors 在的严重安全隐患。 pickle 能够执行任意代码,这意味着加载一个来源不明的 .bin 文件可能导致恶意代码执行。Safetensors 通过一种受限的反序列化过程,从根本上杜绝了这一漏洞 。
2.3.2 结构与优势
一个 Safetensors 文件由两部分组成:一个描述所有张量(tensors)元数据(如形状、数据类型)的 JSON 头部,以及紧随其后的原始、连续的张量数据。这种简单的结构带来了两个核心优势: 1. 安全性:如上所述,它消除了代码执行风险。 2. 高性能加载:该格式支持快速的“懒加载”(lazy loading),通常通过内存映射( mmap ) 实现。这意味着操作系统可以将文件直接映射到进程的虚拟地址空间,而无需立即将整个文件读入物理内存。只有在实际访问某部分权重时,相应的数据才会被从磁盘加载。对于动辄数十甚至数百 GB 的大模型而言,这一特性极大地缩短了模型加载时间并降低了启动时的内存峰值 。 29
目前,几乎所有现代开源模型,包括 DeepSeek、Qwen 和 Llama 3,都已将 Safetensors 作为其首选的分发格式 。 1
2.4 推理专用格式:用于量化、跨平台部署的 GGUF
2.4.1 为推理而生
(GPT-Generated Unified Format)是为 llama.cpp 项目量身打造的,其设计目标完GGUF 全专注于高效推理,而非训练或微调 。 29
2.4.2 一体化格式
的一个关键优势是其一体化的文件结构。它将模型权重、架构元数据,甚至通常还包GGUF 括分词器信息,全部打包在一个单一的文件中。这极大地简化了模型的分发和使用,用户下载一个文件即可开始运行,无需再关心其他依赖文件 。 29
2.4.3 量化领域的强者
GGUF 的核心优势在于其对各种量化方案的广泛支持。量化是一种通过降低模型权重精度(例如,从 16 位浮点数降至 4 位整数)来大幅减小模型文件大小和显存占用的技术。 GGUF 提供了极为丰富和灵活的量化选项(如 Q2_K, Q4_K_M, Q8_0 等),使得在消费级硬件上运行大型模型成为可能 。 29
2.5 实践对比:Safetensors 与 GGUF 的战略性选择
需要明确的是,Safetensors 和 GGUF 并非竞争关系,而是处于模型生命周期中不同阶段的两种格式。一个标准的本地化部署工作流如下: 训练/微调 (PyTorch) -> 分发 (Safetensors) -> 转换 (llama.cpp) -> 本地/CPU 推理部署(GGUF)。下表对这两种关键格式进行了详细比较,为技术选型提供清晰的依据。
特性 Safetensors (.safetensors) GGUF (.gguf)
主要用途 安全地分发训练好的模型权重,用于进一步 高性能、量化后的训练、微调,或作为转换的源文件。 CPU 和消费级硬安全性 高。通过将元数据(JSON)与数据分离, 高。二进制格式旨在防止任意代码执行。 加载性能 非常快。支持懒加载和内存映射( mma 非常快。为 mm p )。 载时间和内存占量化支持 有限。支持 PyTorch 原生的量化,但缺乏 广泛且灵活。这GGUF 那样丰富、灵活的方案。 供从 2 位到 8 位可修改性 高。权重可以被轻松加载、修改和保存。元 低。被设计为最数据通常在可编辑的独立 JSON 文件中。 起来并不直接。 生态系统 Hugging Face、 transformers 库以及更广 llama.cpp 、O 泛的 PyTorch 生态系统的标准。 地/CPU/Apple 整个开源 LLM 生态系统实际上运作于一个根本性的“阻抗失配”(impedance mismatch)之上:一边是开发世界(动态、基于 Python、高精度、以 GPU 为中心),另一边是多样化硬件上的部署世界(静态、基于 C++、低精度、对 CPU/消费级 GPU 友好)。我们讨论的工具和格式不仅仅是技术,它们是跨越这一鸿沟的关键桥梁。 llama.cpp 提供的 convert-hf-to-gguf.py 脚本 正是执行这种“翻译”工作的核心工具。它
将 PyTorch/Safetensors 所代表的、为灵活性和研究速度优化的开发产物,转换为 GGUF 所代表的、为效率和在非数据中心硬件上的可及性而优化的部署产物。 因此,对于 MLOps 工程师而言,核心挑战不仅仅是“运行模型”,而是可靠且高效地管理这一翻译过程。如果不能深刻理解这种“阻抗失配”,将会导致一个脆弱且低效的部署策略。对工具的选择不应是“哪个最好”,而应是“在我从开发到部署的这段特定旅程中,我需要哪座桥梁 ”。
2.6 GGUF 转换流程:使用 llama.cpp 的分步指南
为何需要转换这是使用 Ollama 运行非标准模型最关键的技术环节。由于 Ollama 的原生格式是 GGUF,而绝大多数模型以 Safetensors 格式分发,因此必须进行转换。本小节提供了一个详尽的实践指南。 分步流程1. 环境准备: 安装 git-lfs 并设置一个独立的 Python 环境。克隆 llama.cpp 的官方GitHub 仓库 。
2. 安装依赖: 在 llama.cpp 目录下,运行 pip install -r requirements.txt 来安装所有必要的 Python 库 。 35
3. 下载 Hugging Face 模型: 使用 git clone 或 huggingface-hub Python 库下载目标模型的完整仓库,确保包含了 Safetensors 权重文件和所有配置文件 。 34
4. 运行转换脚本: 执行 convert-hf-to-gguf.py 脚本。一个推荐的最佳实践是,首先将模型转换为高精度的 GGUF 格式(如 FP16),作为后续量化的基础。命令示例: python con vert-hf-to-gguf.py /path/to/hf/model --outfile model-f16.gguf --outtype f16 。
5. 量化 GGUF 文件: 使用 llama.cpp 编译出的 quantize 可执行文件,将上一步生成的FP16 GGUF 文件转换为更小的量化版本。命令示例: ./quantize model-f16.gguf model-Q 4_K_M.gguf Q4_K_M 。 59
Q4_K_M 是一个在性能和质量之间取得良好平衡的常用量化级别。
常见陷阱在转换过程中,用户可能会遇到一些常见问题,例如:系统中缺少 C++ 编译工具链(在Windows 上尤其常见) ;Python 依赖冲突;以及由于
llama.cpp 项目的快速迭代,一些旧的教程仍在引用已被弃用的 convert.py 脚本,而现在应使用功能更全面的 convert-hf-to-gguf.py 。 63
2.7 依赖链映射:可视化指南
为了清晰地展示整个技术栈的内在联系,下图描绘了从模型选型到最终部署的完整生命周期。这个流程图揭示了每一步决策如何影响后续环节,强调了各组件之间的紧密耦合关系。 流程图:从模型架构到本地部署的完整生命周期模型架构选择 (例如 MoE vs. Dense)
↓ 训练框架 (几乎总是 PyTorch) ↓ 模型分发格式 (标准: Safetensors) ↓ **关键转换步骤 (llama.cpp 工具链)** ↓ 推理优化格式 (标准: GGUF) ↓ 部署引擎选择 ├───> Ollama (易用性, 跨平台) └───> vLLM (高吞吐量, 生产服务) 这个可视化指南强调,从 Hugging Face 获取的 Safetensors 模型到能够在本地高效运行的GGUF 模型,中间的转换步骤是不可或缺的核心环节。
2.8 面向未来的本地 LLM 基础设施最佳实践
为了构建一个能够适应快速技术迭代的稳健基础设施,建议采纳以下最佳实践:
- 采纳混合部署模式: 同时部署 vLLM 和 Ollama。vLLM 用于对外提供或内部共享的、需要
高性能和高可用的生产级 API 端点。Ollama 则用于赋能开发人员和数据科学家,在他们的本地机器(包括 MacBooks)上进行快速实验和原型验证,这可以有效减少对宝贵的共享 GPU 资源的占用。
- 自动化 GGUF 转换流水线: 将从 Safetensors 到 GGUF 的转换过程视为核心的 MLOps 流
程,并将其自动化。这个过程应该是一个版本可控、可重复的脚本,并集成到 CI/CD 流水线中。当上游模型更新或需要引入新模型时,该流水线可以自动、可靠地生成适用于Ollama 部署的 GGUF 产物。
- 以 Safetensors 作为“事实之源” (Source of Truth): 将所有从 Hugging Face 下载的原始
模型以 Safetensors 格式进行存储和版本控制。GGUF 文件应被视为从这个“源”派生出的构建产物。这种做法保证了您可以随时根据需要,从同一个可信源重新生成具有不同量化级别的 GGUF 文件,确保了部署的一致性和可追溯性。
- 监控与性能分析: 强调对 vLLM 和 Ollama 部署进行持续监控的必要性。对于 vLLM,应密
切跟踪 GPU 利用率、吞吐量(每秒处理的请求数)和延迟(首个 token 时间和 token 间时间),以便对批处理大小、内存利用率等参数进行调优。对于 Ollama,应了解其在不同开发者硬件上的性能特征,以便为团队设定切合实际的性能预期。
- 与生态系统保持同步: LLM 领域的发展速度极快。模型、工具(如 llama.cpp , vLLM)
和最佳实践每周都在更新。建议建立一个定期审查关键依赖项更新的流程,以便及时利用最新的性能优化、新增的模型支持和功能特性,确保您的基础设施不会因技术陈旧而落后。
第七部分:vLLM与Ray的GPU显存分配策略
第 1 章: LLM推理中的GPU显存剖析在深入探讨vLLM复杂的显存分配策略之前,必须首先建立一个清晰的基准,以理解大型语言模型(LLM)在推理过程中GPU显存(VRAM)的构成。对显存消耗的各个组成部分进行精确解构,是理解vLLM架构设计初衷及其解决核心问题的关键。LLM推理时的显存占用主要由三个动态变化的组件构成:模型权重、键值缓存(KV Cache)和中间激活值。
1.1. VRAM消耗:模型权重、KV缓存与激活值
1.1.1 模型权重 (Model Weights)
模型权重是显存消耗中最直接、最静态的部分。它代表了将模型参数(例如,从 .safetensor s 文件中)加载到GPU上所需的内存。对于给定的模型和数据精度,这部分开销是固定的、 一次性的。模型权重所占用的显存由模型的参数量和其存储精度(如FP16、INT8等)共同决定。以一个70亿参数的Llama模型为例,当使用FP16(半精度浮点数,每个参数占2字节) 加载时,其权重本身就需要大约14GB的显存。对于参数量达到数百亿甚至数千亿的更大模型,仅模型权重就可能超出单个顶级GPU的显存容量,这是驱动多GPU部署(如张量并行) 的首要原因。
1.1.2 键值缓存 (KV Cache)
键值缓存是LLM推理中最为关键、也最为复杂的显存消耗者。在自回归生成任务中,模型每生成一个新词元(token),都需要利用注意力机制(Attention Mechanism)回顾并处理此
前所有词元的上下文信息。为了避免在生成每个新词元时都对整个序列进行重复计算,系统会将序列中每个词元的“键(Key)”和“值(Value)”张量缓存起来。这个缓存就是KV缓存。 KV缓存的特性使其成为性能瓶颈的核心: 1. 动态增长:KV缓存的大小与序列长度成正比。对于一个长度为N的序列,其缓存大小近似为O(N)。在朴素的实现中,其内存占用甚至可能与序列长度的平方相关。 2. 请求独占:在批处理(batching)推理中,每个独立的请求(sequence)都需要有自己独立的KV缓存。因此,总的KV缓存大小是所有并发请求的KV缓存之和。 3. 内存密集:在处理长序列或高并发请求的场景下,所有请求的KV缓存总和常常会超过模型权重本身占用的显存,成为最主要的显存消耗来源。
1.1.3 中间激活值 (Activations)
激活值是在模型前向传播过程中产生的临时张量。它们是网络层与层之间计算的中间结果。 其大小取决于模型架构、批处理大小(batch size)和序列长度。虽然激活值在计算过程中会占用相当大的显存,但它们的生命周期通常是短暂的,仅限于单次前向传播过程,并由深度学习框架(如PyTorch)的内存管理器进行分配和释放。vLLM架构的主要优化焦点在于持久化(每个请求生命周期内)的KV缓存,而非这些瞬态的激活值。
1.2. 静态与动态显存的二分法:为何KV缓存是关键瓶颈
理解LLM推理显存挑战的核心在于认识到其静态与动态组件之间的根本差异。模型权重是静态的、可预测的,而KV缓存是动态的、与每个请求相关联的,其大小和生命周期都高度不可预测。 传统推理系统(如Hugging Face Transformers的标准实现)在处理KV缓存时,通常采用一种简单但低效的策略:为每个进入系统的请求预先分配一个足够大的、连续的内存块,以容纳该请求可能达到的最大序列长度的KV缓存。这种“静态预分配”策略直接导致了两个严重的内存管理问题: 1. 内部碎片化 (Internal Fragmentation):由于请求的实际序列长度几乎总是小于预分配的最大长度,导致每个已分配的内存块内部都存在大量未被使用的闲置空间。例如,为一个最大长度为2048的请求预留了空间,但它实际只生成了100个词元,那么超过95%的预留KV缓存空间就被浪费了。 2. 外部碎片化 (External Fragmentation):随着请求的进入和完成,显存中会产生许多小的、不连续的空闲内存块。即使这些小块的总和足以容纳一个新的请求,但由于找不到一个足够大的连续空闲块,新请求也无法被处理。 这两种碎片化问题共同作用,导致GPU显存的利用率极低。在实际生产环境中,这种浪费可以非常惊人,显存的有效利用率可能低至20%,意味着高达80%的宝贵GPU显存被无效占用或闲置。这不仅限制了系统能够同时处理的并发请求数量,从而严重影响吞吐量,也直接推高了服务的硬件成本。
正是这种由于对动态资源(KV缓存)采用静态管理方法(连续预分配)所导致的根本性矛盾,构成了LLM推理服务的核心性能瓶颈。vLLM的诞生,其架构的核心创新—— PagedAttention,正是为了从根本上解决这一问题。它认识到,推理的挑战并非简单地将所有东西塞进VRAM,而是要为性质截然不同的静态和动态显存组件设计不同的、高效的管理策略。
第二章:张量并行
2.1. 模型分片的原理
张量并行是一种模型并行(Model Parallelism)技术,其核心思想是将神经网络中单个、巨大的层(如Transformer中的自注意力层或前馈网络层)的权重矩阵进行切分,并将这些切分后的“分片”(shards)分布到多个GPU上。 具体来说,对于一个矩阵乘法操作 Y=XA,其中A是一个巨大的权重矩阵。如果我们将A按列切分为$[A_1, A_2]$,并将它们分别放置在GPU 1和GPU 2上,那么每个GPU可以独立计算部分结果:$Y_1 = XA_1$ 和 $Y_2 = XA_2$。最后,通过将$Y_1和Y_2拼接起来,就可以得到完整的结果Y =$。类似地,对于按行切分的情况,则需要在计算后对部分结果进行求和(All-Reduce操作)。 通过这种方式,每个参与张量并行的GPU都只持有整个模型权重的一部分。完整的模型在逻辑上存在,但在物理上,它被分布式地存储在整个张量并行组(TP group)的集体显存中。
2.2. GPU 上的分配
现在,我们可以精确地回答用户提出的场景:一个需要80GB显存的模型部署在拥有4块40GB显卡的单台服务器上。 当vLLM被配置为 tensor_parallel_size=4 时,它会将模型的权重(主要是那些大型的线性层和嵌入层)均匀地切分成4个分片。因此,这个80GB的模型会被分割成4个大小约20GB的分片。在加载阶段,vLLM的每个工作进程(worker)会控制一个GPU,并只加载属于自己的那一份模型分片。最终的显存分配情况如下:
-
GPU 0: 加载约20GB的模型权重分片0。
-
GPU 1: 加载约20GB的模型权重分片1。
-
GPU 2: 加载约20GB的模型权重分片2。
-
GPU 3: 加载约20GB的模型权重分片3。
张量并行的目标是协同利用多个GPU的聚合显存和聚合计算能力,因此它会尽可能地将负载均匀分布到所有指定的设备上。在每个GPU上,加载完20GB的模型权重后,剩余的大约20GB显存(40GB - 20GB)并不会被闲置。这部分空间将用于其他两个关键的显存消耗者: 瞬态的中间激活值,以及更重要的、由vLLM PagedAttention机制管理的KV缓存块池。
2.3. 集体通信(NCCL)的角色
仅仅将模型分片是不够的。在前向传播过程中,当计算流经一个被切分的层时,各个GPU上的部分计算结果必须通过某种方式进行组合,以形成该层的完整输出,并作为下一层的输入。这个跨GPU的数据交换和同步过程,就是通过集体通信(Collective Communication) 来完成的。 在NVIDIA GPU环境中,这一功能通常由NVIDIA集体通信库(NCCL)来高效实现。NCCL提供了一系列优化的通信原语(primitives),如 All-Reduce 、 All-Gather 、 Reduce-Scatter 等。例如,在一个按行切分的线性层之后,每个GPU都得到了一个部分和,此时就需要一个All-Reduce 操作,将所有GPU上的部分和相加,并将最终的总和分发回每个GPU,以确保后续计算的一致性。 这种通信会引入额外的开销。其性能高度依赖于GPU之间的互联带宽。在单节点内部,如果GPU之间通过高速的NVLink连接,通信延迟较低,张量并行的效率较高。如果GPU之间仅通过PCIe总线连接,带宽较低,延迟较高,则会成为性能瓶颈。因此,张量并行本质上是在用通信开销换取更大的模型容量和计算并行度。
2.4. vLLM的实现机制
vLLM 为用户优雅地封装了张量并行的复杂性。当用户在初始化 LLMEngine 时指定一个大于1
的 tensor_parallel_size ,vLLM的内部工作流程如下: 1. 创建工作进程组:vLLM会创建一组“工作进程”(workers),数量与 tensor_parallel_siz e 相等。每个工作进程负责控制一个GPU。 2. 加载模型分片:每个工作进程根据自己的排名(rank)加载对应的模型权重分片。 3. 建立通信:这些工作进程会协同初始化一个NCCL通信组,建立起它们之间的通信链路。 4. 分布式执行:当一个推理请求到来时,输入数据会被广播到所有工作进程。每个工作进程在其持有的模型分片上执行计算。在每个被切分的层之后,它们会调用NCCL原语进行必要的数据同步。从用户的角度来看,他们只是与一个单一的、逻辑上的 LLMEngine 交互, 而底层的分布式计算对他们是透明的。 总而言之,张量并行的首要目标是解决显存容量问题,使得能够加载和运行那些远超单卡显存的大模型。虽然它也带来了计算上的并行化,但这通常伴随着不可避免的通信延迟。这种内在的权衡——用延迟换容量——是AI基础设施工程师在设计和优化大模型推理服务时必须考虑的核心因素。选择合适的 tensor_parallel_size ,不仅仅是看模型能否装下,还要评估其对服务延迟的影响。
第三章:流水线并行
除了张量并行(TP)这种在层内“水平”切分模型的方式,vLLM还支持另一种被称为流水线并行(Pipeline Parallelism, PP)的分布式策略,它在层间“垂直”地切分模型 。理解这两种策
略的差异与协同是进行高级部署优化的关键。
3.1. 流水线并行的概念:垂直分割模型
与张量并行将每一层的权重矩阵分割到多个GPU不同,流水线并行将模型的整个层序列分割成多个连续的块(称为“阶段”或“stages”),并将每个阶段分配给一个独立的GPU(或一组GPU) 。 1
其工作方式类似于一条工厂流水线 : 2
1. 分段:模型被划分为多个阶段。例如,一个有48层的模型,在使用 pipeline_parallel_siz e=2 时,可能会将第1-24层分配给阶段1,第25-48层分配给阶段2。 2. 顺序处理:输入数据首先由阶段1的GPU处理。处理完成后,其输出(即中间激活值)被传递给阶段2的GPU 。 1
3. 数据流:数据在各个阶段之间单向流动,直到最后一个阶段产生最终输出。
与张量并行相比,流水线并行的通信模式有本质区别:
- 通信频率:PP的通信频率较低。数据仅在每个阶段处理完整个微批次(micro-batch)后
才需要在阶段之间传输一次 。 1
- 通信内容:TP在层内交换的是部分计算结果,而PP在阶段间交换的是完整的中间激活张
量,数据量通常更大。
3.2. 单节点场景分析:TP vs. TP+PP 混合模式
现在,我们来分析用户提出的具体单节点场景:一台拥有4个40GB GPU(总显存160GB) 的服务器,用于部署一个需要大量显存的模型。 场景 A: tensor_parallel_size=4 , pipeline_parallel_size=1 (纯张量并行)
- 分配方式:这是我们在第三节中详细讨论过的纯TP模式。整个模型的所有层都被“水平”切
分,每个切片分布在4个GPU上。
- 显存占用:每个GPU加载整个模型权重的1/4。例如,对于一个80GB的模型,每个GPU
加载20GB的权重。所有4个GPU共同构成一个统一的计算单元,协同完成每一层的计算。剩余的显存(每个GPU约20GB)被所有GPU共享,用于统一的KV缓存池。 场景 B: tensor_parallel_size=2 , pipeline_parallel_size=2 (混合并行)
- 分配方式:这种配置创建了一个两阶段的流水线,其中每个阶段本身由一个2-way的张量
并行组构成。
○ 阶段 1 (GPUs 0, 1):这两块GPU负责模型的前半部分层(例如,1到L/2层)。这两块GPU内部使用张量并行,即这部分层的权重被均匀切分并加载到GPU 0和GPU 1 上。 ○ 阶段 2 (GPUs 2, 3):这两块GPU负责模型的后半部分层(例如,L/2+1到L层)。同样,这部分层的权重被均匀切分并加载到GPU 2和GPU 3上。
- 显存占用:
○ 模型权重:对于一个80GB的模型,前40GB的层被分配到阶段1,后40GB的层被分配到阶段2。在阶段1内部,这40GB的权重被TP切分,因此GPU 0和GPU 1各加载20GB。同理,GPU 2和GPU 3也各加载20GB。从单个GPU的权重占用来看,与场景A相同,但它们持有的模型层是完全不同的。 ○ KV缓存:KV缓存的物理存储位置与生成它的模型层所在的GPU绑定。因此,前半部分层的KV缓存块将仅在GPU 0和GPU 1的显存中分配。后半部分层的KV缓存块将仅在GPU 2和GPU 3的显存中分配。vLLM的调度器仍然管理一个逻辑上统一的块池, 但物理块的分布不再是全局均匀的。
3.3. 延迟与吞吐量的权衡:如何选择最佳策略
选择TP、PP还是两者的组合,本质上是在延迟和吞吐量之间进行权衡 。 2
- 张量并行 (TP)
优势: 低延迟。由于所有GPU同时参与计算,可以有效利用聚合的内存带宽,从而快 ○
速处理单个请求 。这使其成为对延迟敏感的实时应用的理想选择 。 1 2
劣势: 高通信开销。TP需要在每个被切分的层后进行频繁的同步操作(如All- ○
Reduce),这要求GPU之间有极高速的互联(如NVLink) 。 4
- 流水线并行 (PP)
优势: 高吞吐量。通过将多个微批次在流水线中重叠执行,可以保持所有GPU持续工 ○
作,适合批处理等对总处理能力要求高的场景 。其通信频率较低,对互联带宽的要 2
求低于TP 。 1
劣势: 高延迟。请求必须按顺序通过所有阶段,总延迟约等于各阶段延迟之和。此 ○
外,流水线启动和排空时会产生“气泡”(pipeline bubbles),即部分GPU处于空闲状态,这在自回归推理中尤其难以避免,从而降低了效率 。 4
3.4. 最佳实践与建议
基于上述权衡,我们可以为不同的部署环境提供清晰的建议:
- 单节点多卡部署:
首选策略: 纯张量并行 ( tensor_parallel_size=N , pipeline_parallel_size=1 )。在单 ○
节点内部,GPU通常通过高速的NVLink或PCIe连接,这非常适合TP的高频通信需求 。纯TP通常能提供最低的请求延迟和最高的效率 。 4 2
○ 例外情况: 仅在特殊情况下考虑使用PP,例如模型层数无法被 tensor_parallel_size 整除,但可以被 pipeline_parallel_size 划分时 。
- 跨节点多卡部署:
○ 首选策略: 混合并行,即在节点内使用张量并行,在节点间使用流水线并行 。这是 1
vLLM官方推荐的常见做法 。 8
○ 配置方法: 设置 tensor_parallel_size 为每个节点内的GPU数量,设置 pipeline_pa rallel_size 为节点的数量 。
○ 原因: 这种策略最大化地匹配了硬件特性。节点内的TP利用了高速的内部互联,而节点间的PP则适应了相对较慢的外部网络(如以太网或InfiniBand),因为它通信频率较低 。例如,对于一个拥有2个节点、每节点8块GPU的集群,最佳配置通常是 ten
sor_parallel_size=8 和 pipeline_parallel_size=2 。
第四章:跨节点vLLM与Ray
在解决了单节点内多GPU的协同问题后,我们转向用户的第二个核心疑问:当模型规模进一步扩大,需要跨越多台物理服务器时,显存和计算是如何分配的?例如,一个80GB的模型部署在两台各有2块GPU的机器上。这引出了vLLM生态中的另一个关键组件——Ray,一个通用的分布式计算框架。
4.1. Ray作为编排层:Actor、资源与调度
要理解vLLM如何实现跨节点部署,首先需要理解Ray的核心抽象。Ray并非为LLM推理专门设计,而是一个旨在简化任何分布式应用开发的框架。其核心概念是Ray Actor。 一个Ray Actor是一个有状态的、可远程调用的Python对象(或进程)。你可以将一个普通的Python类通过 @ray.remote 装饰器转换为一个Actor类。当实例化这个类时,Ray会在集群的某个节点上启动一个独立的进程来运行这个Actor实例。 Ray的强大之处在于其内置的、资源感知的调度器。Ray集群中的每个节点( ray start --he ad 或 ray start --address=... )都会向头节点(head node)汇报自己的资源情况,如CPU核心数、GPU数量、内存大小等。当你创建一个Actor并指定其资源需求时,例如 .opti ons(num_gpus=1) ,Ray调度器会自动在整个集群中寻找一个拥有可用资源的节点,并将该Actor进程放置在该节点上运行。 这个机制是vLLM实现跨节点扩展的基础。vLLM将自身的分布式组件打包成Ray Actor,然后将资源调度和进程管理的复杂工作完全委托给Ray。
4.1.1 Ray 集群基础:节点、工作进程和逻辑资源
一个 Ray 集群由一个头节点和若干工作节点组成。头节点上运行着全局控制存储(Global Control Store, GCS),它维护着整个集群的元数据和状态 。每个节点上都运行着一个名
为 Raylet 的进程,负责管理该节点的本地资源和调度。Ray 通过逻辑资源(如 num_cpu s , num_gpus )的概念来抽象物理硬件,调度器基于这些逻辑资源的请求来分配任务 。 15
4.1.2 超越默认调度:为何简单策略无法满足分布式 AI 需求
Ray的默认调度策略( DEFAULT )在设计上旨在为大量独立的任务或 Actor 实现负载均衡和数据本地性的平衡 。它会为每个独立的任务请求选择一个最合适的节点。然而,对于 vLLM
的分布式部署而言,这种调度策略存在一个致命缺陷。一个 tp=2, pp=2 的模型实例并非由4 个独立的进程组成,而是一个需要 4 个 GPU 进程同时启动、协同工作的紧耦合整体。默认调度器无法提供“组调度”(Gang Scheduling)或原子性的资源分配保证。它可能会成功地为模型调度了 3 个 GPU 工作进程,但在为第 4 个进程寻找资源时失败,这将导致整个应用陷入死锁状态,并造成已分配资源的闲置。
4.1.3 关键组件:用于组调度的 Ray Placement Groups
为了解决上述问题,Ray 引入了一个强大的功能:Placement Groups (PGs)。PG 允许用户以原子方式跨多个节点预留一组资源 。这是实现像 vLLM 这样复杂的分布式应用能够可靠
启动的核心机制。
- 资源包 (Bundles): 一个 PG 由一个或多个“资源包”组成。每个资源包是一个资源集合(例
如, {"GPU": 2} ),并且必须能够被放置在单个节点内 。 16
- 安置策略 (Placement Strategies): PG 提供了对资源包在集群中拓扑分布的精确控制。
主要有四种策略 : 16
STRICT_PACK : 强制所有资源包必须放置在同一个节点上。 ○
PACK : 尽力将所有资源包打包到尽可能少的节点上(这是 PG 的默认策略)。 ○
STRICT_SPREAD : 强制每个资源包必须放置在不同的节点上。 ○
SPREAD : 尽力将每个资源包分散到不同的节点上。 ○
vLLM 的分布式执行正是利用了 PlacementGroupSchedulingStrategy 。vLLM 将其并行化需
求(TP 和 PP 的大小)转化为一个具体的 PG 请求,然后交由 Ray 的调度器来满足。这确保了模型需要的所有 GPU 资源要么被一次性全部成功预留,要么整个启动过程失败,从而避免了部分成功导致的资源死锁。
4.2. 实例化分布式vLLM引擎:Ray Actor中的 LLMEngine
与Ray的集成非常紧密。当在Ray集群环境中初始化vLLM进行分布式推理时,vLLM并vLLM 不会像单机模式那样简单地创建本地子进程。相反,它利用了Ray的Actor模型。
vLLM定义了一个 RayWorker 类,这个类封装了单个GPU上运行模型分片所需的所有逻辑。 在分布式初始化过程中,vLLM会根据 tensor_parallel_size 的值,创建相应数量的 RayWorke r 实例,并将每一个实例都作为一个独立的Ray Actor来启动。
例如,设置 tensor_parallel_size=4 ,vLLM会向Ray调度器请求创建4个 RayWorker Actor, 并且每个Actor都附带 num_gpus=1 的资源请求。 LLMEngine 本身可能运行在另一个中央Actor 中,或者在驱动脚本的主进程中,它负责协调和与这些分布式的 RayWorker Actor进行通信。
4.3. Ray如何跨节点放置张量并行工作者
现在我们可以清晰地回答用户的跨节点场景:一个80GB的模型,需要4个GPU(即 tensor_p arallel_size=4 ),部署在两台各有2块40GB GPU的服务器上。
1. Actor创建请求:vLLM的 LLMEngine 向Ray系统提交一个请求:创建4个 RayWorke r Actor,每个Actor都需要1个GPU。 2. Ray调度决策:Ray的头节点调度器收到了这个请求。它扫描集群的资源视图,发现节点1 有2个可用的GPU,节点2也有2个可用的GPU。 3. Actor放置:调度器做出决策,将2个 RayWorker Actor放置在节点1上,另外2个 RayWorke r Actor放置在节点2上。 4. 模型加载:每个 RayWorker Actor启动后,会像在单机模式下一样,加载属于自己rank的模型分片。因此,80GB的模型权重被均匀地分布在所有4个GPU上,每个GPU约20GB。 最终,节点1上总共加载了约40GB的模型权重,节点2上也加载了约40GB。 KV缓存的管理逻辑保持不变。vLLM的中央调度器(无论它运行在哪里)维护一个全局的块池视图。这个池中的物理块实际上分布在两个节点的所有4个GPU上。当调度器为一个请求分配KV缓存块时,它并不关心这个块的物理位置是在哪个节点上,它只负责逻辑上的分配, 并将块的句柄(handle)告知执行计算的 RayWorker 。
4.4. 追踪数据路径:跨节点通信
在跨节点张量并行设置中,第三节中讨论的集体通信现在必须跨越物理网络。当模型前向传播到一个被切分的层时,位于不同节点上的 RayWorker Actor需要通过网络交换数据来完成All-Reduce 等操作。
Ray在这里扮演了辅助角色。它帮助vLLM的工作进程发现彼此的网络地址和端口,从而建立起一个跨越节点边界的NCCL通信组。然而,物理现实是无法逾越的:
-
节点内通信:通常通过高速、低延迟的NVLink或PCIe进行,带宽可达数百GB/s。
-
节点间通信:通过标准的以太网或InfiniBand进行,即使是高速网络,其延迟也比节点内
互联高出一个数量级以上,带宽也相对较低。
因此,跨节点张量并行会引入显著的性能开销。这是进行大规模模型部署时必须面对的关键权衡。虽然它能够聚合更多节点的显存来运行巨型模型,但每次前向传播中包含的多次网络通信会显著增加单次请求的延迟。 这种vLLM与Ray之间的清晰分工,体现了优秀的系统设计思想。vLLM专注于其核心领域: 在GPU层面进行极致的内存管理(PagedAttention)和模型执行逻辑(张量并行)。而Ray 则扮演着“分布式操作系统”的角色,负责通用的分布式挑战:进程的放置、生命周期管理、 容错以及跨进程通信的建立。工程师在排查问题时,可以根据问题的性质来判断根源:如果是模型无法加载或CUDA错误,那很可能是vLLM层面的问题;如果是工作进程无法启动或节点间通信超时,那大概率是Ray的配置或网络环境问题。理解这一架构边界,对于高效的运维至关重要。
4.5 分布式后端的必要性:超越单节点限制
尽管单个服务器节点可以配备多张 GPU,但随着模型参数量突破千亿甚至万亿级别,单节点的总显存依然可能无法容纳整个模型及其运行时的 KV 缓存 。为了解决这一根本性限制, 7
vLLM 提供了对分布式执行的支持。
vLLM 支持两种分布式执行后端:Python 原生的 multiprocessing 和 ray 。需要特别指 9
出的是, multiprocessing 后端的能力严格局限于单节点内的多 GPU 场景。一旦模型规模大到需要跨越多台物理机进行部署,Ray 就不再是一个可选项,而是唯一的、必需的后端支持 。这一 10
架构决策凸显了 Ray 在 vLLM 分布式生态中的核心地位。
4.6 激活多节点能力: —distributed-executor-backend=ray 参数
要启用 Ray 作为 vLLM 的分布式后端,需要在启动 vLLM 服务时明确指定 --distributed-ex ecutor-backend=ray 参数 。
然而,仅仅设置此参数是不够的。一个关键的前提条件是:在启动 vLLM 服务之前,必须已经有一个正在运行的 Ray 集群。这个集群由一个头节点(Head Node)和一个或多个工作节点(Worker Nodes)组成。通常,这个集群可以通过 vLLM 提供的辅助脚本(如 examples/ online_serving/run_cluster.sh )或在 Kubernetes 环境中通过 KubeRay Operator 来创建和管理 。这个操作流程明确了 vLLM 服务是作为客户端应用,向一个已存在的 Ray 集群提交
计算任务,而不是自己从零开始构建集群。
第五章:从理论到实践
前几节分别解构了vLLM的内存管理核心(PagedAttention)、模型分片策略(Tensor Parallelism)以及分布式编排框架(Ray)。本节将把这些理论知识串联起来,通过对用户提
出的两个具体场景进行端到端的步骤拆解,形成一个统一、清晰的分配策略视图。 场景一(单节点,多GPU):80GB模型部署于1台拥有4x40GB GPU的5.1. 服务器在此场景中,我们的目标是在一台服务器内利用4块GPU协同运行一个80GB的大模型。
- 第一步:初始化 (设置 tensor_parallel_size=4 )
用户启动vLLM服务,并在 LLMEngine 的初始化参数中指定 tensor_parallel_size= ○
4 。 vLLM主进程随即在本地创建4个工作进程(worker processes),并将每个进程绑定 ○
到一块独立的GPU上(GPU 0, 1, 2, 3)。 这4个工作进程通过共享内存或本地套接字进行协调,共同初始化一个NCCL通信 ○
组。由于它们在同一台物理机上,NCCL会优先利用最高速的互联技术,如NVLink, 来建立通信链路。
- 第二步:模型权重加载
vLLM将80GB的模型权重在CPU内存中逻辑地切分为4个约20GB的分片。 ○
每个工作进程根据自己的排名(rank),负责将对应的分片从CPU内存加载到其绑定 ○
的GPU显存中。 加载完成后,显存的静态占用情况为:每块40GB的GPU上,约有20GB被模型权重 ○
占据。整个服务器上的80GB模型权重被均匀地分布在4块GPU上。
- 第三步:KV缓存池分配
LLMEngine 的中央调度器开始初始化PagedAttention所需的内存池。 ○
它会查询每块GPU的剩余显存。以GPU 0为例,总显存40GB,减去已用的20GB权 ○
重,还剩约20GB。 调度器会根据 gpu_memory_utilization 参数(例如,默认值为0.90)来决定KV缓存池 ○
的大小。该参数指示vLLM可以使用GPU总显存的多大比例。 因此,在GPU 0上,为KV缓存池分配的显存大小约为 (40GB * 0.90) - 20GB = 16G ○
B 。 vLLM会在每块GPU上都分配这样一个约16GB的巨大内存块,并将其预先格式化为 ○
数千个小的、固定大小的物理块(physical blocks),准备用于动态分配。
- 第四步:请求处理
一批推理请求到达服务。 ○
vLLM调度器根据连续批处理算法,为每个请求从全局的、分布在4个GPU上的块池 ○
中分配初始的KV缓存块,并为每个请求创建和填充其块表(block table)。 前向传播开始。输入数据被分发给所有4个工作进程。 ○
○ 当计算流经一个被张量并行的层时(例如一个大的MLP层),每个工作进程在其GPU上使用自己的权重分片进行部分计算。计算完成后,它们会通过NVLink执行一次NCCL集体通信操作(如 All-Reduce ),以同步和合并部分结果,确保所有GPU 得到一致的、完整的层输出。 ○ 随着每个请求生成新的词元,调度器会不断从块池中为其分配新的块,并更新其块表。当请求完成时,其占用的所有块被回收至池中,可供新请求使用。
场景二(多节点,多GPU):80GB模型部署于2台各拥有2x40GB GPU 6.2. 的服务器 (PACK策略) 此场景展示了vLLM如何借助Ray扩展到多台机器,以聚合跨节点的GPU资源。
- 第一步:在Ray集群上初始化 (设置 tensor_parallel_size=4 )
前提是已经部署了一个Ray集群,包含这两台服务器作为工作节点。 ○
用户启动vLLM服务(通常是通过Ray Serve或直接的Ray脚本),同样指定 tensor_p ○
arallel_size=4 。 vLLM的 LLMEngine (它本身可能运行在一个Ray Actor中)不再创建本地进程,而是 ○
向Ray调度器请求创建4个 RayWorker Actor,每个Actor都需要1个GPU资源( .opti ons(num_gpus=1) )。
- 第二步:Ray的调度与放置
Ray的中央调度器接收到创建4个GPU Actor的请求。 ○
它检查集群资源,发现节点1有2个空闲GPU,节点2也有2个空闲GPU。 ○
调度器决定将2个 RayWorker Actor放置在节点1上,另外2个 RayWorker Actor放置 ○
在节点2上。Ray负责在相应的机器上启动这些Python进程。
- 第三步:模型权重加载与跨节点NCCL设置
模型加载过程与单节点场景类似,但现在是分布式的。位于不同机器上的4个 RayWor ○
ker Actor,每个都会加载自己对应的约20GB模型分片。 最终,节点1上的两块GPU共持有约40GB的模型权重,节点2也是如此。 ○
Ray会协助这些分布在不同机器上的Actor发现彼此的网络地址和端口。随后,这些 ○
Actor共同初始化一个单一的NCCL通信组,该通信组的成员跨越了两台服务器。节点内的通信对(例如节点1上的两个Actor)将通过PCIe或NVLink进行,而节点间的通信对将通过服务器间的网络(如以太网)进行。
- 第四步:KV缓存池分配
这一步的逻辑与单节点场景完全相同。vLLM的中央调度器管理一个逻辑上统一的KV ○
缓存块池。
○ 物理上,这个池由分布在两台服务器上所有4块GPU的可用显存构成。每块GPU上同样会根据 gpu_memory_utilization 参数预留出一大块内存作为本地的块池部分。 ○ 调度器在分配块时,对块的物理位置(在哪台服务器上)不敏感,它只关心块是否可用。
- 第五步:请求处理
○ 处理流程与单节点场景基本一致,但有一个关键性能差异。 ○ 当执行到被张量并行的层时,NCCL的集体通信操作现在包含了跨节点网络传输。例如,一次 All-Reduce 需要节点1上的两个GPU与节点2上的两个GPU通过网络交换数据。 ○ 这种跨节点通信的延迟远高于节点内的NVLink通信,因此,相比于单节点4GPU的部署方式,这种跨两节点4GPU的部署方式虽然实现了相同的模型承载能力,但通常会导致更高的单次请求处理延迟。
6.3. 角色分工的清晰界定
通过以上两个场景的分析,我们可以总结出vLLM、张量并行和Ray三者之间清晰的职责划分:
- 张量并行 (Tensor Parallelism):这是一种模型分片策略。它回答了核心问题:“一个庞大
的、静态的模型权重矩阵是如何被分割并分布到多个计算单元上的?” 它的范畴是模型结构和数学运算。
- vLLM的PagedAttention:这是一个动态显存管理系统。它回答了核心问题:“对于成百
上千个长度和生命周期都不可预测的并发请求,它们的动态KV缓存是如何被高效、无碎片地存储和管理的?” 它的范畴是GPU显存的高效利用。
- Ray:这是一个分布式执行框架。它回答了核心问题:“那些实现了张量并行和
PagedAttention逻辑的计算进程,应该在集群的哪些物理位置上运行,以及它们启动后如何找到彼此并建立通信?” 它的范畴是计算资源的调度和进程的生命周期管理。 这三者协同工作,构成了一个从底层硬件到上层应用逻辑的完整、分层的解决方案,使得大规模语言模型的分布式推理服务成为可能。
第七章:场景分析 多节点部署一个 TP=2, PP=2 的模型 ( SP
READ 策略) 现在,我们将前述所有概念应用于您的具体场景,一步步追踪从 vLLM 命令到最终物理资源上运行的进程的全过程。
4.1 步骤一:vLLM 解析命令
当您执行 vllm serve... --tensor-parallel-size 2 --pipeline-parallel-size 2 命令时,
vLLM 的启动器首先会解析这些参数。它从中理解到,需要启动一个由 2 个流水线阶段组成
的模型,并且每个阶段内部的张量并行度为 2。由此计算出总共需要 2×2=4 个 GPU 工作进程。 注意:实际调用逻辑梳理一下,从vllm/engine/llm_engine.py中判断engine_config.parallel_config.distributed_executor_backend配置的后端,可以配置包括
ray、mp、uni、external_launcher。但是实际上主要用的是ray 和 mp。并且在
vllm/config.py中说明如下:如果没有指定distributed_executor_backend,则基于 tp * pp
<= gpu num;则默认使用mp,否则需要使用ray。
4.2 步骤二:构建 Ray Placement Group 请求
接下来,vLLM 的 Ray 后端会将这个逻辑上的并行需求,转化为一个具体的 Ray Placement Group 规范。这个翻译过程遵循以下逻辑:
- 流水线阶段映射为资源包: pipeline-parallel-size=2 参数意味着模型需要两个独立
的、可以分布在不同机器上的执行单元。因此,PG 将包含两个资源包(Bundles)。
- 张量并行度定义资源包大小: tensor-parallel-size=2 参数定义了每个流水线阶段所需
的资源。因此,每个资源包的具体内容将是 {"GPU": 2} 。
- 跨节点部署决定安置策略: 为了实现跨节点的流水线并行,这两个资源包必须被放置在
不同的物理机上。因此,vLLM 会为 PG 请求一个 SPREAD 或 STRICT_SPREAD 策略。 综上所述,vLLM 向 Ray GCS 提交的请求在逻辑上等同于:
ray.util.placement_group(bundles=[{"GPU": 2}, {"GPU": 2}], strategy="SPREAD")
4.3 步骤三:Ray 调度器执行安置
的 GCS 收到这个 PG 创建请求后,调度器开始在集群中寻找满足条件的资源。 Ray 调度器会扫描您集群中的 4 个节点。它的目标是找到两个不同的节点(由 SPREAD 策略决定),并且每个节点都至少有 2 个可用的 GPU 资源。鉴于您的集群配置是 4 个节点,每个节点有 4 张 GPU,且初始状态下资源均可用,调度器可以轻松地满足这个请求。它会选择任意两个节点,我们称之为节点 A 和节点 B。
4.4 步骤四:资源预留与工作进程放置
一旦调度器找到了满足条件的节点,PG 就会被成功创建,这 4 个 GPU 资源(节点 A 上的2 个和节点 B 上的 2 个)会被原子性地预留下来。在 PG 被销毁之前,这些资源不能被任何其他 Ray 任务使用。
- 在节点 A 上: 第一个资源包 {“GPU”: 2} 被安置于此。vLLM 随后在该节点上被预留的
两张 GPU 上启动流水线阶段 0 (PP Rank 0) 的两个 GPU 工作进程。这两个进程将协同执行张量并行计算。
- 在节点 B 上: 第二个资源包 {“GPU”: 2} 被安置于此。vLLM 接着在该节点上被预留的
两张 GPU 上启动流水线阶段 1 (PP Rank 1) 的两个 GPU 工作进程。这两个进程同样执行张量并行计算。
4.5 最终答案
直接回答您的问题:vLLM 服务不会在单个节点上运行。 它的部署拓扑将会是:
-
分布在您 4 节点集群中的两个不同节点上。
-
每个被选中的节点将使用其 4 张可用 GPU 中的 2 张。
-
集群中另外两个节点将完全不参与这个 vLLM 实例的计算,并保持空闲状态,可用于其
他任务。 这个从 vLLM 并行参数到 Ray Placement Group 的映射关系,是理解和预测任何 vLLM 分布式部署行为的核心。