分布式推理与 GPU 集群调度:张量并行、流水并行与调度器实战

当单卡放不下一个 70B 模型,或单卡无法满足线上吞吐时,分布式推理就从『可选优化』变成『必选项』。本文系统讲解张量并行、流水线并行与专家并行三大策略的通信代价与适用场景,介绍连续批处理在集群中的落地,并深入 KServe 与 Ray Serve 两类 GPU 调度器的架构与配置。一、为什么需要分布式推理

当单卡放不下一个 70B 模型,或单卡无法满足线上吞吐时,分布式推理就从"可选优化"变成"必选项"。本文系统讲解张量并行、流水线并行与专家并行三大策略的通信代价与适用场景,介绍连续批处理在集群中的落地,并深入 KServe 与 Ray Serve 两类 GPU 调度器的架构与配置。

一、为什么需要分布式推理

1.1 单卡装不下的显存账本

推理显存需求 = 模型权重 + KV Cache + 激活值。以 Llama-2-70B(FP16)为例:

模型权重:70e9 × 2 字节 ≈ 140 GiB
KV Cache:4096 长度、8 kv heads、80 层 ≈ 单请求约 20 MiB(可忽略)

一张 A100 80G 只能放下一半权重。即便用 https://plumephp.com/ai-llm-quantization/ 中的 INT4 量化把权重压到 ~35 GiB,还需要留出 KV Cache 与计算余量,仍显局促。单卡方案在模型规模面前天花板太低。

模型FP16 权重INT4 权重最小可用显存
Llama-2-7B14 GiB3.5 GiB24 GiB
Llama-2-13B26 GiB6.5 GiB40 GiB
Llama-2-70B140 GiB35 GiB4×80G 或 2×H100
Mixtral-8x7B112 GiB28 GiB8×80G(EP 场景)

1.2 分布式推理 vs 分布式训练

维度分布式训练分布式推理
目标提升吞吐、缩短训练时间单请求延迟低 + 集群吞吐高
数据流前向 + 反向,梯度通信仅有前向
批处理大批量、静态形状连续批处理、动态形状
通信频率每个训练 step 一次每请求 / 每 decode step
热点关注吞吐(MFU)延迟(TTFT/ITL)+ 吞吐

分布式推理的特殊之处在于:它是延迟敏感的、请求驱动的。不能简单照搬训练的 AllReduce 通信模式,而要考虑一次请求生命周期内的通信开销占比。

二、三种并行策略

2.1 张量并行(Tensor Parallel,TP)

原理:把单个 Transformer 层的权重矩阵按行/列切分到多张卡上。例如 Attention 的 Q/K/V 投影按列切分,输出投影按行切分。每张卡各自计算一部分,通过 AllReduce 汇总。

通信量:每个 Transformer 层两次 AllReduce(Attention 输出 + MLP 输出),通信量与 hidden size 成正比,与 batch 无关。因此 TP 对单请求延迟友好,但通信成本高,一般建议 TP ≤ 8。

# vLLM 张量并行:单卡放不下,用 4 卡
python -m vllm.entrypoints.openai.api_server \
    --model meta-llama/Llama-2-70B-chat \
    --tensor-parallel-size 4 \
    --gpu-memory-utilization 0.90

2.2 流水线并行(Pipeline Parallel,PP)

原理:把模型按层切段,每张卡负责连续的一段层。数据像流水线一样依次流过各段。通信量极小(仅层间激活传递),但存在"气泡"(流水线首尾填充的空闲时间)。

推理场景:PP 天然适合预填充与解码分离。理想状态下 Prefill 阶段跑段 1,同时 Decode 阶段跑段 2,交错执行减少气泡。

# TensorRT-LLM:TP=2, PP=2(2 张卡张量并行,另外 2 张卡接后半段)
trtllm-build --model_config config.pbtxt \
    --tensor_parallel_size 2 \
    --pipeline_parallel_size 2 \
    --max_batch_size 128 \
    --use_fused_mlp

2.3 专家并行(Expert Parallel,EP)

原理:MoE 模型的 FFN 由多个专家(Expert)组成,每个 token 只激活少数专家。EP 把不同专家放在不同卡上,token 通过 All2All 通信路由到对应专家所在卡。

关键点:EP 的通信量 = 路由到专家层的 token 数。因为 MoE 中每个 token 可能触发多次 All2All,EP 对单请求延迟影响比 TP 更显著,通常与 TP 组合使用(TP×EP)。

# vLLM MoE 模型:专家并行 4 + 张量并行 2
python -m vllm.entrypoints.openai.api_server \
    --model mistralai/Mixtral-8x7B-Instruct \
    --tensor-parallel-size 2 \
    --expert-parallel-size 4

2.4 并行策略对比

维度张量并行 TP流水线并行 PP专家并行 EP
切分对象层内权重矩阵层序列MoE 专家
通信频率每层 2 次 AllReduce仅层间(低频)每 token 多次 All2All
通信量与 batch 关系无关相关(随 batch 增大)强相关
单请求延迟影响小(低延迟友好)中(气泡)大(多次路由)
典型规模≤82~164~64
适用单卡放不下、追求低延迟超大模型、prefill/decode 分离MoE 模型(Mixtral、DeepSeek)

工程上推荐组合策略:TP × PP 用于稠密大模型,TP × EP 用于 MoE 模型。具体配置逻辑与 https://plumephp.com/ai-tensorrt-llm/ 中的部署方案一脉相承。

2.5 通信原语与互连拓扑

并行策略的通信效率受制于底层互连。NCCL(NVIDIA Collective Communications Library)是 GPU 间集合通信的事实标准,负责执行 AllReduce、AllGather、All2All 等原语。

原语方向用途通信量
AllReduce多卡 → 汇总 → 广播回TP 中 Attention/MLP 输出合并2 × hidden × batch
AllGather各卡广播自己的分片TP 中 QKV 前的输入聚合(N-1)/N × 数据量
All2All每卡发给其他所有卡EP 中 token 专家路由与路由分布相关
P2P Send/Recv相邻两卡PP 层间激活传递单次激活切片

NVIDIA 平台的两级互连:

NVLink(卡内多卡互联,900 GB/s H100)
   └─────────────── IB / RoCE(跨节点,200~400 Gb/s)

拓扑感知调度是 GPU 集群调度的进阶能力:调度器优先把同一 TP 组的卡放在同一节点、同一 NVSwitch 域,以最大程度利用 NVLink 而非慢得多的跨节点网卡。

# 检查 NVLink 拓扑
nvidia-smi topo -m

# 输出示例(同一节点内 TP 组)
#       GPU0  GPU1  GPU2  GPU3
# GPU0   NV    NV    NV    NV
# GPU1   NV    NV    NV    NV
# ...

一个经验法则:TP=8 的组建议放在同一节点;跨节点 TP 通信会因网卡带宽受限,显著拉高 decode 单步延迟。PP/EP 对跨节点相对宽容,但仍建议尽量减少跳数。

三、连续批处理与多实例部署

3.1 连续批处理在集群中的角色

https://plumephp.com/ai-vllm-system/ 中介绍的连续批处理(Continuous Batching)解决的是"单引擎内如何高效服务多请求"。放到集群层面,调度器还需要决定:

  1. 哪个请求去哪个副本:基于副本的负载与 token 余量
  2. 每个副本跑什么模型:模型分片(多副本 × 多并行度)
  3. 副本如何扩容缩容:按队列深度 / GPU 利用率触发
                        ┌──────────── 请求队列 ────────────┐
                        │                                  │
  LB + 路由 ────────────>  vLLM 副本 A (TP=4, 70B)          │
                        │  vLLM 副本 B (TP=4, 70B)          │
                        │  vLLM 副本 C (TP=8, 8x7B MoE)     │
                        └──────────────────────────────────┘

3.2 GPU 多实例与多副本

  • MIG(Multi-Instance GPU):物理 GPU 切成多个隔离实例,适合"小模型多副本"场景,但 MIG 无法参与跨实例张量并行
  • 多副本 + 队列:同一模型部署 N 个 vLLM 副本,前端负载均衡分发;每个副本独立连续批处理,集群吞吐 ≈ 单副本吞吐 × 副本数(线性扩缩)
# 每个副本的 vLLM 服务
replicas: 4
spec:
  containers:
    - name: vllm
      command: ["python", "-m", "vllm.entrypoints.openai.api_server"]
      args: ["--model", "Qwen/Qwen2-72B-Instruct", "--tensor-parallel-size", "4"]
      resources:
        limits:
          nvidia.com/gpu: 4

3.3 显存分片与副本数规划

给定一个拥有 N 张 GPU 的集群,两种常见编排方式:

  • 大副本(多卡 1 副本):模型并行度高,单副本吞吐高,但副本少 → 故障影响面大、弹性粒度粗
  • 小副本(少卡多副本):副本数多,路由与伸缩粒度细,但并行度低时每副本吞吐下降

规划公式(粗略):

可部署副本数 ≈ 集群总 GPU 数 ÷ 单副本 TP 数

示例:32 张 A100,70B FP16 模型 TP=4
     → 最多 8 个副本,每副本提供约 70B/4=35 GiB/卡 的 KV 余量

实践建议:用 TP 数把单副本显存余量控制在 20%~35%(留给 KV Cache 与激活值),再按线上峰值吞吐估算副本数,最后叠加 1~2 个冗余副本。

四、GPU 集群调度器

4.1 KServe:Kubernetes 上的模型服务

KServe(原 KFServing)是基于 Kubernetes 的标准化推理平台,核心是 InferenceService 自定义资源(CRD)。它把模型服务建模为 Deployment + 自动扩缩(HPA/KPA)+ 灰度发布(Canary)。

apiVersion: serving.kserve.io/v1beta1
kind: InferenceService
metadata:
  name: llama-70b
spec:
  predictor:
    model:
      modelFormat:
        name: vllm
      storageUri: s3://models/llama-70b/
      args:
        - --tensor-parallel-size=4
        - --gpu-memory-utilization=0.9
      resources:
        limits:
          nvidia.com/gpu: 4
  scaleSettings:
    minReplicas: 2
    maxReplicas: 8
    target: 50   # 基于 RPS 的自动扩缩目标

优势:与 K8s 生态(Prometheus、HPA、Ingress)无缝整合,多模型管理、金丝雀发布开箱即用。劣势:需要 GPU 节点池与 Kubeflow 全家桶,运维心智较重。

4.2 Ray Serve:Python 原生的推理调度

Ray Serve 是 Ray 生态提供的模型服务框架,把"模型副本"抽象为 Python 里的 Deployment,配合 Ray 的分布式运行时在集群中调度。对 GPU 的调度通过 ray_actor 的 num_gpus 声明完成。

from ray import serve
import vllm

@serve.deployment(
    num_replicas=2,
    ray_actor_options={"num_gpus": 4},  # 每个副本 4 张卡
    autoscaling_config={
        "min_replicas": 1, "max_replicas": 8,
        "target_num_ongoing_requests_per_replica": 16,
    },
)
class Llama70B:
    def __init__(self, model_path: str):
        self.llm = vllm.LLM(
            model=model_path,
            tensor_parallel_size=4,
            gpu_memory_utilization=0.90,
        )

    async def __call__(self, request: dict):
        result = self.llm.generate(request["prompt"],
                                   sampling_params=request["params"])
        return result

app = Llama70B.bind(model_path="s3://models/llama-70b")
serve.run(app)

优势:GPU 资源由 Ray 调度器统一管理,模型副本自动伸缩、异常自动重建,与 Python 生态(训练、数据管线)天然衔接。劣势:需要独立部署 Ray 集群,与 K8s 的网络/存储要自己打通。

4.3 KServe vs Ray Serve 对比

维度KServeRay Serve
底层Kubernetes + CRDRay 分布式运行时
模型格式存储路径(HuggingFace/S3)Python 对象 + 模型路径
GPU 调度K8s 节点池 + 显存配额ray_actor num_gpus
自动伸缩KPA/HPA(RPS 或并发)按 ongoing requests / 队列
灰度发布内置 Canary需自建(deployment strategy 有限)
多模型多 InferenceServiceserve 图 + 路由策略
运维复杂度中高(Kubeflow 生态)中(Ray 集群运维)
典型场景企业级标准化平台Python 团队、训练推理一体化

选型建议:已有成熟 K8s 平台的团队选 KServe;模型团队(尤其是从训练平滑过渡到推理)选 Ray Serve。两者都可以在前方再加一层网关实现统一路由。

五、推理集群架构设计

5.1 分层架构

┌─────────────────────────────────────────────────────┐
│ 接入层:Nginx / Envoy / 自研网关(鉴权、限流、计费)     │
├─────────────────────────────────────────────────────┤
│ 路由层:按模型维度路由到对应 InferenceService / Serve  │
├─────────────────────────────────────────────────────┤
│ 调度层:KServe 或 Ray Serve(弹性伸缩、GPU 分配)       │
├─────────────────────────────────────────────────────┤
│ 执行层:vLLM / TensorRT-LLM 副本(TP/PP/EP 并行)      │
└─────────────────────────────────────────────────────┘

5.2 弹性伸缩与冷启动

GPU 推理冷启动是集群性能的隐形杀手:模型权重加载(70B 从 S3 拉取 + 加载)+ 引擎初始化可能需要数十秒到数分钟。

冷启动环节耗时量级优化手段
权重下载10~60s节点本地 NVMe 缓存 / 预拉取镜像
权重加载到显存5~30ssafetensors mmap、并行加载
CUDA Graph / 引擎编译1~30s预热队列 + 后台常驻冷副本
首请求显存分配<1svLLM 启动时预分配

因此生产集群通常维护 1 个常驻冷副本 + 若干热副本,用"预热队列"让扩容出的副本在服务流量前完成模型加载。

5.3 路由、限流与容错

  • 路由键:按模型名 + 推理后端版本路由,避免新旧模型混跑
  • 限流:网关层按 API Key 的 QPS/token 配额限流,防止单请求洪峰拖垮整个副本
  • 容错:副本健康检查失败后自动摘除;请求超时重试时避免放大(Jitter Retry)
  • 队列背压:当所有副本排满时,网关返回 429 而非无限排队

这些治理能力与 https://plumephp.com/ai-llm-inference-architecture/ 中的企业级中台设计互相补充,一个侧重集群内部调度,一个侧重外部服务治理。

5.4 多租户与 GPU 配额

当多个团队共享同一个 GPU 集群时,需要配额治理,避免"一个团队跑满全部卡"。

  • Namespace 级配额:K8s ResourceQuota 限制某团队可申请的 GPU 总数
  • 优先级与抢占:生产任务优先级高于实验任务,低优先级任务在资源紧张时被抢占回收
  • 显存隔离:nvidia.com/gpu 以整卡为单位分配;MIG 可提供 1/7、1/4 等细粒度切片,适合小模型租户
apiVersion: v1
kind: ResourceQuota
metadata:
  name: gpu-quota
  namespace: team-ml
spec:
  hard:
    requests.nvidia.com/gpu: "16"
    limits.nvidia.com/gpu: "16"

合理的配额策略能让一个 GPU 集群同时服务多个模型、多个团队,而不至于因某个突发流量拖垮全局。

六、总结

知识点核心要点
为什么分布式单卡显存天花板 + 吞吐需求
张量并行 TP层内切分,AllReduce 密集,低延迟友好
流水线并行 PP层间切分,通信少,存在气泡
专家并行 EPMoE 路由,All2All 密集,需与 TP 组合
连续批处理集群吞吐 = 单副本吞吐 × 副本数
GPU 调度器KServe(K8s 生态)/ Ray Serve(Python 原生)
冷启动治理预热队列 + 常驻冷副本 + 本地权重缓存

分布式推理的工程要点是"把模型切得下去、把请求调度得动"。切分策略决定单副本延迟下限,调度器决定集群吞吐上限。建议先在一台多卡机上用 vLLM 的 --tensor-parallel-size 打通 70B 推理,再引入 Ray Serve 做多副本编排,最后按需迁移到 KServe 纳入企业 K8s 平台。集群层面的容量与成本如何测算,可以参考 https://plumephp.com/ai-inference-benchmark/ 一文。

继续阅读

探索更多技术文章

浏览归档,发现更多关于系统设计、工具链和工程实践的内容。

全部文章 返回首页

「ai」更多文章

  1. 时序预测实战:从 ARIMA 到时序基础模型
  2. 模型压缩:量化、剪枝、蒸馏与部署优化实战
  3. 量化感知训练(QAT)与量化微调:伪量化、STE 与 QLoRA 实战