对分布式推理架构有经验。包括跨机器 /跨节点部署、负载均衡、模型分片。

对于 Llama-3-70B 这样的模型,所需的约 140GB 显存,是根据其参数量和 FP16 精度下的存储需求计算得出的。

核心计算公式

计算模型权重(Weights)所需的显存大小公式非常简单:

1. 确定参数量 (Parameters)

Llama-3-70B:该模型的参数量为 700 亿 (70 Billion)。
Parameters=70×109 ext{Parameters} = 70 imes 10^9Parameters=70×109

2. 确定每参数字节数 (Bytes per Parameter)

FP16 精度 (半精度浮点数):FP16 意味着每个参数使用 16 位(Bit)来存储。
16 Bits=2 Bytes16 ext{ Bits} = 2 ext{ Bytes}16 Bits=2 BytesBytes per Parameter (FP16)=2 Bytes ext{Bytes per Parameter (FP16)} = 2 ext{ Bytes}Bytes per Parameter (FP16)=2 Bytes

3. 计算显存容量 (VRAM Calculation)

将数据代入公式:

分子(总字节数):70,000,000,000×2=140,000,000,00070,000,000,000 imes 2 = 140,000,000,00070,000,000,000×2=140,000,000,000 字节换算成 GB:

而实际应用中提到约 140GB,是因为在模型推理时,除了存储模型权重外,还需要额外的显存来存储以下数据:

激活值 (Activations): 模型在推理过程中,每层计算产生的中间结果。这部分数据量取决于批次大小 (Batch Size)序列长度 (Sequence Length)K/V Cache (键值缓存): 用于存储 Attention 机制中的键(Key)和值(Value)向量,特别是进行长序列推理时,K/V Cache 会占用相当大的空间。推理框架开销: PyTorch、TensorFlow 或 VLLM 等推理框架自身运行所需的少量显存。

因此,130.2 GB仅模型权重的理论最低值,而 140 GB 则是实际运行时(权重 + 缓存/激活值)的一个保守估计值。

如果想进一步节省显存,可以考虑使用 INT4 或 INT8 这样的量化 (Quantization) 技术。例如,如果使用 INT4 精度,理论上显存需求会进一步减半。


这是一个非常典型的中国大陆算力现状下的实战场景。目前国内企业的算力资源往往是“新旧混用、旗舰与特供版混用”的状态(存量的 V100/P100,少量的 A100,以及新购入的 A800/H20)。

作为一名顶级部署工程师,你的价值不仅在于会用新卡,更在于如何在一个异构(Heterogeneous)集群中,把这些参差不齐的显卡性能榨干

以下我为你构建一个基于真实落地场景的项目经验描述,你可以直接参考这个逻辑来准备面试或优化你的简历。


项目名称:面向企业级 RAG(检索增强生成)场景的千亿模型高并发推理集群搭建

1. 项目背景 (Situation)

业务场景:公司内部上线企业知识库助手(类似 GPT-4 的文档问答),基于 Qwen1.5-72B-Chat(通义千问 72B)模型。

流量特征:典型的 RAG 场景,输入 Prompt 极长(平均 4k token 上下文),并发波动大。

硬件现状(痛点)

资源池极度碎片化:拥有 2 台 8卡 A800 (80G)、2 台 8卡 H20 (96G)、若干存量 V100 (32G) 和 P100 (16G)。核心挑战:单卡显存不足以承载 72B 模型;不同显卡计算能力差异巨大,简单的轮询会导致请求在慢卡上堆积,拖垮整体 SLA。

2. 核心架构设计 (Task & Action)

我负责了该推理集群的整体架构设计与部署,采用了 K8s + Ray Serve + vLLM 的技术栈,核心工作围绕你提到的三个点展开:


A. 模型分片策略 (Model Sharding) —— 因“卡”制宜

针对 72B 模型(FP16 权重约 144GB,加上 KV Cache 实际需求远超此数),我制定了差异化的分片策略:

A800 节点(主力军)—— 张量并行 (Tensor Parallelism, TP)

策略TP=4实施:在一台 8 卡 A800 机器上,部署 2 个 vLLM 实例,每个实例占用 4 张卡。理由:A800 NVLink 带宽极高,TP 通信成本低。TP=4 时,总显存 320GB,扣除模型权重后,有大量空间留给 KV Cache,极其适合 RAG 场景的大 Context 处理,能支持极大的 Batch Size。

H20 节点(特供版优化)—— 显存换吞吐

策略TP=8实施:H20 的 FP16 算力只有 A100 的一半左右,但显存大(96GB)。我将 8 张卡聚合为一个超大实例(TP=8)。理由:虽然 H20 算得慢,但 768GB 的总显存是巨大的优势。我将其专门配置为处理**超长文本(Long Context, >16k token)**的请求,利用大显存避免 KV Cache 溢出(OOM),虽然延迟稍高,但吞吐量极稳。

V100/P100 节点(老兵新用)—— 服务解耦

策略流水线并行 (Pipeline Parallelism) 或 模型降级

实施

V100:由于不支持 BF16,强行跑 72B 容易溢出且精度有损。我将其用于部署 Embedding 模型 (bge-m3)Rerank 模型。这两类模型参数小(<1B),V100 单卡即可跑满,释放了珍贵的 A800 资源给 LLM。P100:部署较小的 Qwen-14B-Int4 量化版,作为“降级服务”。当主集群流量过载时,将非核心用户的请求路由到 P100 集群,保证服务不挂。


B. 跨机器/跨节点部署 (Distributed Deployment) —— 统一算力平面

由于显卡分布在不同物理机上,我使用 Ray Cluster 构建了一个统一的计算平面:

Head Node 管理:在 CPU 节点部署 Ray Head,负责全局资源注册。

Worker Node 接入:所有 GPU 机器作为 Worker 接入 Ray 集群。在启动 vLLM 时,通过
ray.remote(num_gpus=4)
声明资源需求。

实际问题解决

环境隔离:不同显卡 CUDA 版本要求不同(H20 需要较新 CUDA,V100 较旧)。我基于 Docker 封装了不同的镜像,通过 K8s 的 NodeSelector 调度不同的 Pod 到对应机器,但通过 Ray 的 GCS (Global Control Store) 统一管理服务。


C. 负载均衡 (Load Balancing) —— 智能路由

这是整个项目中提效最明显的部分。普通的 Nginx 轮询无法应对异构显卡。我开发了自定义的 Router(基于 Ray Serve)

基于“卡性能”的加权路由

通过 Benchmark 测得 A800 实例的吞吐是 H20 的 1.8 倍。在 Ray Serve 的
Deployment
配置中,通过
max_concurrent_queries
限制 H20 的并发数,或者在 Router 层按 6:4 的比例分发流量,防止 H20 积压请求。

基于 Prefix Caching 的亲和性路由 (Affinity Routing)

原理:RAG 场景下,用户经常基于同一份文档提问。vLLM 支持复用 KV Cache。实现:我在 Router 层计算 Prompt 前缀的 Hash 值。如果发现
Hash(Document_A)
之前的请求发给了 节点-1,那么下一个关于
Document_A
的请求强制路由到 节点-1效果:这使得 Prefill(首字生成)阶段的时间几乎归零,因为缓存命中了。

分离式路由 (Detached Routing)

将 Embedding 请求 100% 路由到 V100 节点。将 LLM 生成请求 路由到 A800/H20 节点。


3. 项目成果 (Result)

资源利用率:成功将闲置的 V100/P100 利用起来,节省了采购新卡的成本(约节省 50万+)。性能提升:通过 Prefix Caching 和 A800 的 TP=4 优化,首字延迟 (TTFT) 从平均 400ms 降低至 150ms吞吐量:在混合集群下,实现了单卡平均 35 tokens/s 的生成速度,支撑了日均 10万+ 次的内部调用。稳定性:利用 Ray 的自动容错机制,当某台物理机宕机时,流量自动在 3秒内 切换到备用节点,实现了 99.9% 的可用性。


面试/简历中的话术提炼(Key Takeaways)

如果你在面试中被问到这部分,请着重强调以下几点,这会让你显得非常专业:

“我不止会跑通模型,我懂硬件特性”:强调你根据 H20 显存大但算力弱的特点,将其分配给 Long Context 任务,而不是简单的所有卡平摊流量。“我懂通信开销”:强调你坚持机内做 TP,跨机绝不做 TP(除非万不得已用 PP),因为国内机房通常没有 InfiniBand,跨机通信是瓶颈。“我看重 Cache 命中率”:提到 Prefix Caching(前缀缓存)路由算法 的结合,这是目前大模型推理优化的前沿方向(vLLM 核心特性)。


1. Rerank 模型是干啥用的?

简单一句话:它是 RAG(检索增强生成)流程里的“精挑细选”环节,用来提升回答准确率的。

背景(为什么要用它?)
当用户问“公司去年的报销政策是什么?”时,你的系统会去向量数据库(Vector DB)里检索。

第一步(粗排/Embedding 检索):数据库会飞快地扔给你 50 条它觉得相关的文档。但这 50 条里,可能只有第 1 条和第 45 条是真的相关的,其他的只是关键词沾边。痛点:如果你把这 50 条全扔给大模型(LLM),第一是太长(费钱、费显存),第二是噪声太多,模型会看晕,开始胡说八道(幻觉)。

Rerank 的作用
Rerank 模型(比如 BGE-Reranker)是一个专门用来打分的小模型。它会把这 50 条文档,一条条拿来跟用户的问题进行深度比对,算出相关性分数,然后重新排序。

结果:Rerank 之后,那两条真正相关的文档会被排到 Top 2。最后:我们只把 Top 5 扔给 LLM,既省钱又准。

硬件关联
Rerank 模型通常不大(几百兆到 2GB),但计算稍微有点密集。用你手里的 V100 跑 Rerank 是绝配,完全不需要占用宝贵的 A800/H20。


2. 什么是 Ray Cluster?它和 K8s、Docker 是什么关系?

这是一个非常高频的面试题。我们可以把搭建一个分布式计算平台比作盖一栋现代化的大楼

Docker:预制板/房间模块

定义:容器化技术。作用:把你的代码、Python 环境、依赖包打包成一个“盒子”。关系:它是最小的运行单元。无论你在哪台机器上跑,只要有 Docker,环境就是一样的。

Kubernetes (K8s):大楼的物业/施工队

定义:容器编排系统。

作用

它负责管理那一堆 Docker 容器。它决定把哪个容器放到哪台物理机(Node)上。如果某台机器起火了(宕机),K8s 会自动把上面的容器在别的地方重新启动。

局限性:K8s 不懂 Python 代码里的逻辑,它只管“容器活着还是死了”,它不管“这个矩阵乘法需要把数据从 GPU1 发给 GPU2”。

Ray Cluster:大楼里的智能办公系统

定义:Python 的分布式计算框架。

作用

运行在 K8s 之上(通常每个 K8s Pod 里跑一个 Ray 节点)。它懂代码。它允许你写 Python 代码来控制 GPU。核心能力:当你写
ray.remote(num_gpus=1)
时,Ray 知道哪台机器显存空着,能毫秒级地把你的 Python 函数调度过去执行,并且还能让不同机器上的 Python 对象互相通信(共享内存)。

总结三者关系

Docker 打包了 vLLM 和 Ray 的环境。K8s 负责把这些 Docker 容器撒到你的 A800、H20、V100 物理机上,让集群跑起来。Ray 连接了这些容器内部的 Python 进程,组成了一个统一的计算大脑,负责具体的模型分片、张量传输和推理任务调度。


3. Benchmark 是什么?

简单一句话:跑分 / 压力测试 / 基准测试。

在你的项目中
既然你有 A800 和 H20 两种主力卡,你不能凭感觉说“A800 比较快”。你需要具体的数字。

怎么做?
你会运行一个专门的脚本(比如 vLLM 自带的
benchmark_throughput.py
),模拟 100 个用户同时发请求,记录以下指标:

TTFT (Time To First Token):用户发问后,多久看到第一个字?(越低越好)TPOT (Time Per Output Token):生成时,每秒出多少个字?(越高越好)QPS (Queries Per Second):一秒能处理多少个请求?

为什么要跑 Benchmark?

测完你会发现:A800 的 TPOT 是 50 tokens/s,而 H20 只有 30 tokens/s决策依据:依据这个跑分结果,你在配置负载均衡时,就会给 A800 分配 60% 的流量,给 H20 分配 40% 的流量,防止 H20 被压垮。这就是 Benchmark 的价值。


4. Ray Serve 是什么?

简单一句话:Ray 这个“计算大脑”对外的“前台接待员”。

背景
Ray Core 负责在后台搞计算(算矩阵、算模型),但用户是通过 HTTP(浏览器/Postman)发请求的。Ray Core 不懂 HTTP。

Ray Serve 的作用
它是构建在 Ray 之上的一个库,专门用于模型服务化(Model Serving)

它帮你做了什么?

HTTP 接口:它自动帮你把 Python 函数变成
http://127.0.0.1:8000/chat
这样的接口。动态扩缩容 (Autoscaling):如果晚上 8 点突然来了 1 万人,Ray Serve 监测到请求排队了,会自动告诉 Ray Core:“嘿,再给我复制 5 个模型实例出来!”等人少了,它又会自动销毁实例省资源。流水线编排:你可以用代码定义流程:
Request -> Ray Serve -> (V100 做 Embedding) -> (A800 做推理) -> Response


总结一下这套“连招”

在你的那个真实项目中,流程是这样的:

Benchmark:你先对 A800 和 H20 进行跑分,摸清了它们的底细。

Docker:你把 vLLM 镜像打包好。

K8s:你让 K8s 把这些镜像部署到所有物理机上。

Ray Cluster:容器启动后,Ray 把所有显卡连成了一个大集群。

Ray Serve:你写了一个 Python 类,作为接待员。

用户请求来了 -> Ray Serve 接待。需要检索 -> 丢给 V100 跑 Embedding,再跑 Rerank 挑出重点。需要生成回答 -> 丢给 A800 跑大模型。最后返回结果。


问题一:不同机器上怎么共享内存?(Distributed Shared Memory)

你说得对,物理上,A 机器的内存条插在 A 上,B 机器肯定不能直接把手伸进 A 的内存里拿数据。

Ray 所谓的“跨机器共享内存”,学名叫做 分布式对象存储(Distributed Object Store)。在 Ray 的底层实现中,有一个核心组件叫做 Plasma

它的工作原理是“局部共享 + 按需复制”。

1. 假如没有 Ray(笨办法)

假设机器 A 算出了一个巨大的矩阵(1GB),机器 B 上的 5 个进程都需要用它。

做法:机器 A 通过网络把这 1GB 发给机器 B 的进程 1,再发给进程 2,再发给进程 3…后果:机器 B 的内存里存了 5 份一样的矩阵(浪费 5GB 内存),而且网络传输了 5 次。

2. Ray 的做法(聪明办法)

Ray 在每台机器上都维护了一个本地对象存储(Object Store,即共享内存区)

步骤一(生产):机器 A 的 Worker 算出矩阵,存入机器 A 的 Object Store(内存中)。得到一个
ObjectRef
(类似于取件码)。

步骤二(传输):机器 B 需要这个矩阵。Ray 的后台进程(Raylet)发现机器 B 的 Object Store 里没有,它会自动联系机器 A,通过网络只传一份过来,存入机器 B 的 Object Store。

步骤三(零拷贝读取 – Zero Copy)

这是最关键的一步!机器 B 上跑着 5 个 Python 进程(Worker)。这 5 个进程不需要把那个矩阵 copy 到自己的 Python 堆内存里。它们通过 Apache Arrow 技术,直接读取 Object Store 里的那同一份内存地址。

结果:机器 B 无论跑多少个进程,这 1GB 数据只占 1GB 内存,而不是 5GB。

3. 总结

所谓的“跨机共享内存”,其实是:Ray 帮你自动处理了网络传输,并在每台机器的本地实现了“一次写入,多次读取”的零拷贝共享。

这对你的项目意味着什么?
你的权重数据(Model Weights)如果在节点间传输,Ray 会极大地节省内存。特别是当你用 TP(张量并行)时,虽然通信频繁,但 Ray 的底层传输协议(往往基于 gRPC 或更底层的传输)能帮你屏蔽掉复杂的 Socket 编程。


问题二:Ray Serve 和 Ray Core 是 Ray Cluster 下面的吗?

这是层级关系(Hierarchy)的问题。我们可以把 Ray 看作一个三层汉堡

第一层:Ray Cluster(基础设施层 / 物理层)

是什么:这是肉体内容:就是你那堆 A800、H20、V100 机器。状态:当你用 K8s 把这些机器连起来,并在每台机器上启动了 Ray 的守护进程(Raylet),它们就组成了一个 Ray Cluster。包含:Head Node(大脑) + Worker Nodes(手脚)。

第二层:Ray Core(内核层 / 能力层)

是什么:这是神经系统

内容:这是 Ray 的底层 API。

主要概念

Task:远程函数(
@ray.remote def func()
)。Actor:远程类(
@ray.remote class Model()
),这对于大模型很重要,因为模型加载进显存后不能动,得一直活着,这就是 Actor。Object:刚才说的共享内存里的数据。

关系:Ray Core 运行在 Ray Cluster 上。

第三层:Ray Serve(应用框架层 / 业务层)

是什么:这是制服/工牌

内容:这是构建在 Ray Core 之上的一个专门用于模型推理的库

为什么需要它

Ray Core 只管“算”。它不懂 HTTP,不懂“路由”,不懂“扩缩容”。Ray Serve 封装了 Ray Core 的 Actor,加上了 HTTP 服务器(Proxy),加上了自动扩缩容的逻辑。

关系:Ray Serve 依赖 Ray Core。你写的 Ray Serve 代码,底层最终会被翻译成 Ray Core 的 Actor 和 Task,然后在 Ray Cluster 上执行。


一张图理清关系


用户请求 (HTTP Request)
      ↓
+-------------------------------------------------------+
|  应用层: Ray Serve                                    |
|  (负责:接收 HTTP,路由请求,管理模型副本 Deployment)   |
+-------------------------------------------------------+
      ↓ (调用)
+-------------------------------------------------------+
|  内核层: Ray Core                                     |
|  (负责:调度 Task,管理 Actor,跨机 Object 传输)        |
+-------------------------------------------------------+
      ↓ (运行于)
+-------------------------------------------------------+
|  物理层: Ray Cluster (运行在 K8s 之上)                 |
|  (机器: A800 Node, H20 Node, V100 Node)               |
+-------------------------------------------------------+

你的简历/面试必杀技:

当面试官问你架构时,你可以这样降维打击:

“在这个项目中,我基于 K8s 搭建了 Ray Cluster 作为底座,利用 Ray Core 的 Actor 机制管理异构 GPU 资源(因为模型需要常驻显存)。

而在业务接入层,我选用了 Ray Serve。为什么?因为 Ray Serve 能很好地解耦 HTTP 处理和模型推理,它的Pipeline 机制让我能轻松地把 V100 上的 Rerank 服务和 A800 上的 LLM 服务串联起来,同时利用 Ray 底层的 Object Store 实现高效的数据流转。”

这段话把三者的关系和你的实际用法结合得天衣无缝。

好的,当然可以!我们用一个**“专属服务员”**的比喻来简单明了地解释 Ray Actor 机制:


🎭 Ray Actor 机制:通俗易懂的解释

想象一下您的 Ray Cluster 是一个巨大的分布式厨房(K8s 集群),里面有很多厨师(CPU)和烤箱(GPU)。

1. 什么是 Actor?

在传统的分布式计算中,任务就像是一次性的外卖订单:厨师做完菜(推理任务),锅碗瓢盆(显存中的模型)立刻就收走了。下次来新订单,得重新找锅、洗锅、热锅。

Ray Actor 就像一个“专属服务员”:

独占资源: 当你创建 Actor 时(比如
MyModelServer.remote()
),Ray 会给它分配一个专属的工作台和烤箱(特定的 GPU 显存和计算资源)状态常驻: 这个服务员一上岗,就把您的大模型(“核心工具”)永久地放在这个专属工作台上(常驻显存)。他不会收走模型。

2. 它解决了什么问题?

解决的核心痛点是:启动慢和资源混乱。

痛点 解决方式(Actor 机制)
模型加载慢 服务员(Actor)的“核心工具”(模型)永远在桌上,无需重复加载
异构资源管理 你可以指定这个服务员只用 “A100 烤箱”,另一个服务员只用 “H100 烤箱”,清晰隔离,互不打扰。
分布式服务 你的所有请求都直接发送给这个**“专属服务员”**,它直接帮你用桌上的模型进行推理,Ray 帮你处理了所有复杂的派单和网络通信。

总结

Actor 机制就是创建了有状态(Stateful)独占资源、且可远程调用的分布式服务实例。

在您的项目中,它意味着:每一个 LLM 模型实例都有一个独占的 GPU 显存空间,并且随时待命,以实现最低的推理延迟。

© 版权声明

相关文章

暂无评论

none
暂无评论...