<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:dc="http://purl.org/dc/elements/1.1/"><channel><title>Jimmy Song – 工作负载实践</title><link>https://jimmysong.io/zh/book/ai-infra/workloads/</link><description>Recent content in 工作负载实践 on Jimmy Song</description><generator>Hugo -- gohugo.io</generator><language>zh</language><managingEditor>Jimmy Song</managingEditor><webMaster>Jimmy Song</webMaster><follow_challenge><feedId>51621818828612637</feedId><userId>59800919738273792</userId></follow_challenge><lastBuildDate>Sat, 10 Jan 2026 10:45:31 +0800</lastBuildDate><atom:link href="https://jimmysong.io/zh/book/ai-infra/workloads/index.xml" rel="self" type="application/rss+xml"/><item><title>训练、推理与分离式系统：工作负载的系统观</title><link>https://jimmysong.io/zh/book/ai-infra/workloads/workload-physics/</link><pubDate>Sun, 30 Aug 2026 00:00:00 +0000</pubDate><author>Jimmy Song</author><guid>https://jimmysong.io/zh/book/ai-infra/workloads/workload-physics/</guid><description>训练是同步问题，任何一个环节都能成为主导；推理是尾延迟问题，高利用率与违约的 SLO 可以并存；把平台拆成模型、服务、加速器、数据、控制五个平面之后，Kubernetes 才开始像 AI 应用平台。</description><content:encoded>
&lt;blockquote&gt;
&lt;p&gt;更多 GPU 缩短计算时间的前提，是通信、输入和同步跟得上；工作负载的形状决定平台的形状。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="训练是一个同步问题"&gt;训练是一个同步问题&lt;/h2&gt;
&lt;p&gt;训练不只是前向传播，它是一个不断循环的链条：输入准备、前向计算、反向计算、梯度同步、优化器更新、检查点、评估，&lt;strong&gt;任何一个环节都可能成为主导&lt;/strong&gt;。这正是&lt;a href="../../fundamentals/gpu-interconnect-collectives/"&gt;互连与集合通信&lt;/a&gt;中扩展效率方程的应用场景：先测单卡、再测单机、最后测多机，才知道瓶颈在哪一环。&lt;/p&gt;
&lt;p&gt;检查点不是事后想法。如果一个作业要跑一周，恢复契约就是硬件决策的一部分。&lt;/p&gt;
&lt;h2 id="推理是一个尾延迟问题"&gt;推理是一个尾延迟问题&lt;/h2&gt;
&lt;p&gt;推理服务关心排队、批处理、token 生成、上下文长度、预热、模型加载和尾延迟。GPU 可能利用率很高，而用户可感知的延迟已经违反 SLO，仅按平均利用率自动伸缩，可能反应太迟或太早。&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;信号&lt;/th&gt;
&lt;th&gt;回答什么&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;队列深度&lt;/td&gt;
&lt;td&gt;请求在等什么&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;首 token 时间（TTFT）&lt;/td&gt;
&lt;td&gt;用户等多久看到第一个字&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;token 间延迟（ITL）&lt;/td&gt;
&lt;td&gt;生成过程是否流畅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;活跃序列数&lt;/td&gt;
&lt;td&gt;批处理压力&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;KV 缓存压力&lt;/td&gt;
&lt;td&gt;上下文容量还剩多少&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;模型加载时间&lt;/td&gt;
&lt;td&gt;扩容/故障恢复的速度&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;figcaption class="text-center mb-3"&gt;
表 1: 推理服务的正确信号
&lt;/figcaption&gt;
&lt;p&gt;这些信号的物理根因展开见&lt;a href="../llm-inference-physics/"&gt;LLM 推理的物理层&lt;/a&gt;，指标口径见&lt;a href="../../observability/gpu-metrics-semantics/"&gt;GPU 指标语义&lt;/a&gt;。&lt;/p&gt;
&lt;h2 id="模型数据与控制平面"&gt;模型、数据与控制平面&lt;/h2&gt;
&lt;p&gt;一个生产 AI 平台把模型工件和缓存与请求服务的 Pod 分开：它定义模型如何到达、如何校验、引擎在哪里构建、权重如何缓存、版本如何滚动、故障节点如何预热。这正是 Kubernetes 开始像 AI 应用平台而非通用容器调度器的地方。&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;平面&lt;/th&gt;
&lt;th&gt;主要关切&lt;/th&gt;
&lt;th&gt;有用信号&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;模型&lt;/td&gt;
&lt;td&gt;权重、画像、引擎、版本&lt;/td&gt;
&lt;td&gt;缓存命中、加载时间、摘要&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;服务&lt;/td&gt;
&lt;td&gt;流量、批处理、副本、延迟&lt;/td&gt;
&lt;td&gt;TTFT、ITL、队列深度、吞吐&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;加速器&lt;/td&gt;
&lt;td&gt;GPU 分配、内存、拓扑&lt;/td&gt;
&lt;td&gt;利用率、余量、错误&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;数据&lt;/td&gt;
&lt;td&gt;数据集搬运与局部性&lt;/td&gt;
&lt;td&gt;读带宽、缓存命中、I/O 等待&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;控制&lt;/td&gt;
&lt;td&gt;发布、策略、自动伸缩、恢复&lt;/td&gt;
&lt;td&gt;调谐与准入状态&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;figcaption class="text-center mb-3"&gt;
表 2: 生产 AI 平台的五个平面
&lt;/figcaption&gt;
&lt;p&gt;这张表与&lt;a href="../../production/production-architecture/"&gt;生产架构&lt;/a&gt;的平面划分互补：那里讲集群治理平面，这里讲工作负载侧平面。&lt;/p&gt;
&lt;h2 id="多节点推理"&gt;多节点推理&lt;/h2&gt;
&lt;p&gt;超大模型可能被切分到多块 GPU 或多个节点。系统因此继承训练同样的互连与拓扑问题，但 SLO 是请求路径而非批次时间。&lt;strong&gt;一个吞吐上看起来不错的设计，如果同步或网络抖动出现在关键路径上，仍可能有不可接受的尾延迟&lt;/strong&gt;。预填充/解码分离（PD 分离）正是这一约束下的产物，见&lt;a href="../../control-plane/kthena/"&gt;Kthena&lt;/a&gt;与&lt;a href="../llm-inference-physics/"&gt;LLM 推理物理层&lt;/a&gt;的讨论。&lt;/p&gt;
&lt;h2 id="总结"&gt;总结&lt;/h2&gt;
&lt;p&gt;训练问“每步同步花了多少”，推理问“第 99 个百分位的请求等了多久”，多节点系统问“关键路径上有几次跳变”。用这三问审视工作负载，再选平台组件，顺序不能反。本章是&lt;a href="../"&gt;工作负载实践&lt;/a&gt;部分的序章：&lt;a href="../vllm/"&gt;vLLM&lt;/a&gt;、&lt;a href="../pytorch/"&gt;PyTorch&lt;/a&gt;、&lt;a href="../ray-kuberay-topology/"&gt;Ray/KubeRay 与拓扑约束&lt;/a&gt;分别把这三种系统观落到具体引擎。&lt;/p&gt;</content:encoded></item><item><title>LLM 推理的物理层：预填充、解码与 KV 容量账</title><link>https://jimmysong.io/zh/book/ai-infra/workloads/llm-inference-physics/</link><pubDate>Fri, 28 Aug 2026 00:00:00 +0000</pubDate><author>Jimmy Song</author><guid>https://jimmysong.io/zh/book/ai-infra/workloads/llm-inference-physics/</guid><description>在讨论推理平台的资源治理之前，先算清物理账：预填充与解码为何受不同的屋顶线约束、KV cache 的容量公式与并发上限、连续批处理与分页 KV 的设计思想、分块预填充对尾延迟的意义，以及推理服务与共享/配额层的配合公式。</description><content:encoded>
&lt;blockquote&gt;
&lt;p&gt;推理平台的所有配置争论（并发、批大小、显存水位、共享粒度）最终都归结为两笔物理账：一笔算力账，一笔显存账。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;a href="../vllm/"&gt;vLLM&lt;/a&gt;一章从资源治理视角讨论吞吐与尾延迟；本章补上它的物理地基：&lt;strong&gt;为什么&lt;/strong&gt;解码受带宽屋顶线约束、&lt;strong&gt;为什么&lt;/strong&gt; KV cache 是容量货币、&lt;strong&gt;为什么&lt;/strong&gt;连续批处理和分页 KV 是必然设计。这些账同样决定&lt;a href="../../control-plane/kthena/"&gt;Kthena&lt;/a&gt;一类的推理调度器在优化什么。Roofline 模型的推导见&lt;a href="../../fundamentals/gpu-microarchitecture-roofline/"&gt;微架构&lt;/a&gt;，显存机制见&lt;a href="../../fundamentals/gpu-memory-management/"&gt;显存管理&lt;/a&gt;。&lt;/p&gt;
&lt;h2 id="两阶段同一模型两种性格"&gt;两阶段：同一模型，两种性格&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;预填充（prefill）&lt;/strong&gt;：一次性并行处理整条 prompt，生成第一个 token。大矩阵乘法，&lt;strong&gt;受算力屋顶线约束&lt;/strong&gt;，是 GPU 的舒适区。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;解码（decode）&lt;/strong&gt;：此后每生成一个 token，都要带着全部历史（KV cache）再算一遍前向。每步计算量小，但&lt;strong&gt;每步都要把全部模型权重从显存读一遍&lt;/strong&gt;，受带宽屋顶线约束。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;由此得到推理平台最实用的一行公式：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;单路解码速度上限 ≈ 显存带宽 ÷ 权重量
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;H100 (3.35 TB/s) 跑 70B BF16 (140 GB) ≈ 24 token/s（物理规律，不是软件差）&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;它解释了三件事：量化（FP8 权重减半）直接让解码翻倍；换带宽更高的卡（H200）比换算力更高的卡对推理更有效；以及&lt;strong&gt;批量推理的本质&lt;/strong&gt;是把“读一遍权重”摊到多个请求上，把算术强度从 1 提高到 B。吞吐随批量大致线性上升，直到撞上算力屋顶线或显存容量。&lt;/p&gt;
&lt;h2 id="kv-cache容量货币"&gt;KV cache：容量货币&lt;/h2&gt;
&lt;p&gt;自回归生成需要缓存每个 token 的 Key/Value 向量：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;每 token KV 字节数 = 2(K与V) × 层数 × KV头数 × 头维度 × 精度字节
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Llama-3-70B (80层, 8 KV头, 头维128, BF16) = 320 KB/token
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;→ 一个 4k 上下文的请求 ≈ 1.25 GB 显存&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;GQA（分组查询注意力）是现代模型 KV 变小的原因：Llama-3-70B 用 8 个 KV 头对应 64 个查询头，KV 比 MHA 等效结构小 8 倍。&lt;strong&gt;架构选择直接等于容量选择。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;推理容量公式（平台做容量规划时的第一行代码）：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;可并发请求数 ≈ (显存总量 × 利用率 − 权重 − 激活/开销) ÷ (每token KV × 平均上下文)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;示例（H100 80G，利用率 0.9，4k 平均上下文）：8B 模型单卡约 100+ 并发；70B 需要 TP=8 才有合理并发；32B 级模型在单卡只剩个位数并发，这就是“中等模型为什么也要 TP”的算术。&lt;/p&gt;
&lt;h2 id="连续批处理与分页-kv两次必然的进化"&gt;连续批处理与分页 KV：两次必然的进化&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;静态批&lt;/strong&gt;（攒一批一起进出）的浪费是显然的：长短请求互相等，新请求必须等整批结束。**连续批处理（continuous batching）**在每个解码步重新组批：完成的请求下车、排队的新请求上车。这是 vLLM/TRT-LLM/SGLang 的共同底座。&lt;/p&gt;
&lt;p&gt;连续批处理把压力转移到内存管理：早期引擎按“最大长度”为每个请求预留连续显存，浪费 60–80%。&lt;strong&gt;分页 KV&lt;/strong&gt;（PagedAttention）把操作系统的分页机制搬进显存：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;操作系统概念&lt;/th&gt;
&lt;th&gt;分页 KV 对应&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;物理页（4KB）&lt;/td&gt;
&lt;td&gt;KV 块（默认 16 token）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;页表&lt;/td&gt;
&lt;td&gt;每请求的 block table&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;写时复制（COW）&lt;/td&gt;
&lt;td&gt;采样 n&amp;gt;1 / beam 分叉时共享块&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;共享库页缓存&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;前缀缓存&lt;/strong&gt;：相同 system prompt 的请求共享已算好的块&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;swap&lt;/td&gt;
&lt;td&gt;显存不足时把块换到主机内存&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;figcaption class="text-center mb-3"&gt;
表 1: 分页 KV 与操作系统分页的对应
&lt;/figcaption&gt;
&lt;p&gt;其中&lt;strong&gt;前缀缓存&lt;/strong&gt;对平台价值最大：RAG/Agent 类“长固定前缀 + 短输出”的流量，块级节省可达 90% 以上，同样显存服务多一个数量级的并发，且 TTFT 大幅下降。前缀命中率应当与 KV 水位一起纳入容量模型与&lt;a href="../../observability/benchmarks-acceptance-capacity/"&gt;验收指标&lt;/a&gt;。&lt;/p&gt;
&lt;h2 id="分块预填充吞吐与尾延迟的调节阀"&gt;分块预填充：吞吐与尾延迟的调节阀&lt;/h2&gt;
&lt;p&gt;一条 16k prompt 的预填充是一个百毫秒级的大 kernel；若引擎“先做完预填充再解码”，期间&lt;strong&gt;所有正在解码的请求都停摆&lt;/strong&gt;，TTFT 与 ITL（token 间延迟）天然冲突。**分块预填充（chunked prefill）**把长 prompt 切块掺进解码批逐步消化，用少量 TTFT 换取 ITL 的稳定。更激进的方案是预填充/解码分离部署（如 Kthena 一类调度器的 PD 分离模式），把冲突移到集群层，代价是 KV 迁移与路由复杂度。&lt;/p&gt;
&lt;h2 id="与共享配额层的配合一道必考题"&gt;与共享/配额层的配合：一道必考题&lt;/h2&gt;
&lt;p&gt;推理引擎按 &lt;code&gt;gpu_memory_utilization × 物理显存&lt;/code&gt; 计算自己的 KV 池。在软切分配额（HAMi 的 &lt;code&gt;nvidia.com/gpumem&lt;/code&gt;）下，若 Pod 配额 40GB 而机器 80GB、utilization 拍 0.9，引擎会按 72GB 规划显存池，配额拦截层在池初始化时注入 OOM，&lt;strong&gt;Pod 进入启动崩溃循环&lt;/strong&gt;。正确做法是让两个数字来自同一个变量：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;gpumem 配额 ≈ 权重/TP + 期望KV池 + 激活/图/上下文底噪(约2-3GB)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;gpu_memory_utilization ≈ gpumem ÷ 物理显存 ← 从配额反推, 而不是拍 0.9&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;同理，core 份额（&lt;code&gt;nvidia.com/gpucores&lt;/code&gt;）对解码型负载的限流效果天然偏弱：它管的是执行时间，而解码的瓶颈在带宽（见&lt;a href="../../fundamentals/gpu-microarchitecture-roofline/"&gt;微架构&lt;/a&gt;的 Roofline 模型）。&lt;strong&gt;推理租户的配额要以显存为主、算力为辅&lt;/strong&gt;，且同卡邻居的负载类型比份额数字更影响尾延迟（见&lt;a href="../vllm/"&gt;vLLM&lt;/a&gt;的抖动分析与&lt;a href="../../observability/gpu-metrics-semantics/"&gt;指标语义&lt;/a&gt;）。&lt;/p&gt;
&lt;h2 id="常见误区"&gt;常见误区&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;误区&lt;/th&gt;
&lt;th&gt;真相&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;推理 GPU 只用了 5% 算力，太浪费&lt;/td&gt;
&lt;td&gt;解码受带宽屋顶线约束，5% 算力可能是物理最优；看 DRAM 指标&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;并发越大吞吐越高&lt;/td&gt;
&lt;td&gt;过拐点后只伤延迟，且显存容量先耗尽&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;前缀缓存是个小优化&lt;/td&gt;
&lt;td&gt;长固定前缀流量下是数量级的容量与 TTFT 收益&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;量化伤质量所以不用&lt;/td&gt;
&lt;td&gt;权重量化在评测上普遍近无损，且是带宽优化的第一手段&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;vLLM 已经做多租户了，平台不用管&lt;/td&gt;
&lt;td&gt;引擎内是请求级公平；副本层的配额、伸缩、隔离仍是平台职责&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;分离部署一定更好&lt;/td&gt;
&lt;td&gt;引入 KV 迁移与路由复杂度；规模或 SLO 不极端时分块预填充常常足够&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;figcaption class="text-center mb-3"&gt;
表 2: 推理物理层的常见误区
&lt;/figcaption&gt;
&lt;h2 id="总结"&gt;总结&lt;/h2&gt;
&lt;p&gt;推理的物理层可以压缩成三句话：&lt;strong&gt;解码是带宽问题&lt;/strong&gt;（单路上限 = 带宽 ÷ 权重量，量化与高带宽卡是正解）；&lt;strong&gt;KV cache 是容量货币&lt;/strong&gt;（并发、模型选型、TP 宽度都按 KV 账算）；&lt;strong&gt;批处理与分页是结构解&lt;/strong&gt;（连续批把算术强度提上去，分页把浪费降下来，前缀缓存再省一个量级）。下一章&lt;a href="../vllm/"&gt;vLLM&lt;/a&gt;把这些物理事实映射为资源治理旋钮；&lt;a href="../../observability/gpu-metrics-semantics/"&gt;可观测&lt;/a&gt;告诉你用哪三个数字确认系统运行在预期区间。&lt;/p&gt;
&lt;h2 id="参考资料"&gt;参考资料&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="../vllm/"&gt;vLLM: 推理吞吐与尾延迟的资源真相&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="../../fundamentals/gpu-microarchitecture-roofline/"&gt;GPU 微架构与 Roofline 模型&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="../../fundamentals/gpu-memory-management/"&gt;GPU 显存管理：分配器、碎片与 OOM 排查树&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://arxiv.org/abs/2309.06180" target="_blank" rel="noopener"&gt;vLLM: Efficient Memory Management for LLM Serving (PagedAttention, SOSP'23)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.usenix.org/conference/osdi22/presentation/yu" target="_blank" rel="noopener"&gt;Orca: A Distributed Serving System for Transformer-Based Generative Models (OSDI'22)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title>vLLM：推理吞吐与尾延迟的资源真相</title><link>https://jimmysong.io/zh/book/ai-infra/workloads/vllm/</link><pubDate>Tue, 30 Dec 2025 05:03:12 +0000</pubDate><author>Jimmy Song</author><guid>https://jimmysong.io/zh/book/ai-infra/workloads/vllm/</guid><description>从并发、KV cache 与显存形态解释推理为何放大共享问题，并给出可治理的部署与验收思路。</description><content:encoded>
&lt;blockquote&gt;
&lt;p&gt;推理平台的真正挑战，不在于极限吞吐，而在于如何用资源契约守住尾延迟的底线。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;在线推理平台最容易掉进一个“看起来很合理、结果很不稳定”的陷阱：把吞吐（tokens/s、QPS）拉满之后，尾延迟（p95/p99）反而开始失控；更糟的是，这种失控常常不是线性恶化，而是突然抖动、周期性爆炸、或者在某些请求形态/租户组合下才出现。&lt;/p&gt;
&lt;p&gt;vLLM 之所以值得单独一章讨论，是因为它把“吞吐与尾延迟冲突”的核心机制暴露得非常清楚：并发与批处理把 GPU 利用率推高的同时，也把显存（KV cache，Key-Value Cache）与调度队列变成了系统的“弹性瓶颈”。当共享/隔离策略变化时，这个瓶颈会被放大或被抑制，最终决定平台的可预测性。&lt;/p&gt;
&lt;p&gt;本章将从 vLLM 的并发与显存行为出发，回答三个工程问题：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;为什么吞吐优化天然会冲击尾延迟？冲击发生在哪些资源环节？&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;平台应该如何定义“资源请求”与“验收指标”，让容量与体验可讨论、可评审？&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;哪些配置组合最容易引发系统性抖动（jitter），以及如何提前规避？&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="吞吐与尾延迟不是权衡口号而是队列与显存的物理后果"&gt;吞吐与尾延迟：不是权衡口号，而是队列与显存的物理后果&lt;/h2&gt;
&lt;p&gt;在在线推理场景中，提升“吞吐”通常有三种主要手段：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;提高并发&lt;/strong&gt;：即同时活跃的请求数量（更多 sequences / requests）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;加大批处理&lt;/strong&gt;：将更多 token 计算合并进一次或更少次数的 GPU kernel 调用（continuous batching，连续批处理）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;提高占用率&lt;/strong&gt;：尽量让 GPU 计算单元持续忙碌，减少空洞和上下文切换。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这些手段的共同副作用是：&lt;strong&gt;请求进入 GPU 的等待时间上升&lt;/strong&gt;，且等待时间的方差变大（队列效应）。尾延迟通常在以下两种情况下爆炸：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;短请求被长请求“拖住”&lt;/strong&gt;：混合工作负载下，批处理与调度更倾向于追求整体吞吐，导致短请求在队列里等待“拼批”或被长请求占用算力窗口。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;显存接近水位线&lt;/strong&gt;：KV cache 越逼近显存上限，越容易触发保守策略（拒绝、回退、offload、或者更频繁的调度切片），从而产生非线性的延迟抖动。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;因此，在推理平台中，吞吐与尾延迟的矛盾，往往不是“算法优化 vs 用户体验”的抽象冲突，而是两个非常具体的资源事实：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;调度队列的排队时间&lt;/strong&gt;（谁先算、谁后算、如何拼批）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;KV cache 的驻留与碎片&lt;/strong&gt;（显存水位、可用块、回收/分配成本）&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;vLLM 的工程价值在于：它把这两件事做成了“可控旋钮”，也就意味着：你把旋钮拧到极致时，系统会以尾延迟或稳定性作为代价支付账单。&lt;/p&gt;
&lt;h2 id="vllm-的关键资源模型并发kv-cache-与水位线管理"&gt;vLLM 的关键资源模型：并发、KV cache 与“水位线管理”&lt;/h2&gt;
&lt;p&gt;理解 vLLM 的资源本质，抓住以下两点即可：&lt;/p&gt;
&lt;h3 id="并发不是免费每个活跃序列都要占-kv-cache"&gt;并发不是免费：每个活跃序列都要占 KV cache&lt;/h3&gt;
&lt;p&gt;对于大多数 Transformer 推理而言，&lt;strong&gt;KV cache（Key-Value Cache）&lt;/strong&gt; 是决定显存曲线的主项。直观来看：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;并发越高 → 同时活跃的序列越多 → KV cache 占用越大&lt;/li&gt;
&lt;li&gt;上下文越长 / max_model_len 越大 → 单序列 KV cache 上限越高&lt;/li&gt;
&lt;li&gt;输出越长 → 序列更久才结束 → KV cache 驻留时间更长&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这意味着平台在讨论“QPS 能跑多少”之前，必须先回答一个更硬的问题：&lt;strong&gt;在给定显存与模型配置下，你能稳定承载多少“活跃 token”&lt;/strong&gt;。吞吐的上限通常不是算力，而是显存与调度策略共同限定的“并发安全区”。&lt;/p&gt;
&lt;h3 id="连续批处理提升吞吐但会把尾延迟变成队列治理问题"&gt;连续批处理提升吞吐，但会把尾延迟变成“队列治理问题”&lt;/h3&gt;
&lt;p&gt;vLLM 的 continuous batching（连续批处理）会不断把新 token 合入批次，从而提高 GPU 利用率。但连续批处理天然引入一个事实：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;更大的批次通常意味着更高的平均吞吐&lt;/li&gt;
&lt;li&gt;但也意味着单个请求更可能等待更久才能进入下一次计算窗口&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;因此，尾延迟治理的本质是“不要让队列变成黑箱”。你需要明确哪些请求优先、如何限制长尾请求对系统的占用、以及何时应该牺牲一点吞吐换取稳定性。&lt;/p&gt;
&lt;p&gt;下图展示 vLLM 的资源模型以及吞吐与尾延迟的物理后果。左侧展示吞吐优化的三种手段（提高并发、加大批处理），代价是尾延迟风险（队列等待时间上升、显存接近水位线）。中间蓝色区域展示 KV cache 作为显存硬边界的三条规则：并发越高 KV cache 占用越大、上下文越长单序列上限越高、输出越长驻留时间越长。黄色区域展示 continuous batching 将尾延迟变成队列治理问题。右侧虚线框总结两个资源瓶颈：调度队列排队时间、KV cache 驻留与碎片。底部核心洞察：vLLM 把队列与显存做成了可控旋钮，拧到极致时以尾延迟或稳定性支付账单。&lt;/p&gt;
&lt;figure class="mx-auto text-center"&gt;
&lt;img src="https://assets.jimmysong.io/images/book/gpu-infra/vllm/vllm-resource-model.svg" data-img="https://assets.jimmysong.io/images/book/gpu-infra/vllm/vllm-resource-model.svg" alt="图 1: vLLM 的资源模型：并发、KV cache 与吞吐尾延迟的物理后果" data-caption="图 1: vLLM 的资源模型：并发、KV cache 与吞吐尾延迟的物理后果"
width="1586"
height="1323"
loading="lazy" decoding="async" class="image-loading"
onload="this.classList.remove('image-loading'); this.classList.add('image-loaded');"
onerror="handleImageError(this); this.classList.remove('image-loading');"&gt;
&lt;figcaption&gt;图 1: vLLM 的资源模型：并发、KV cache 与吞吐尾延迟的物理后果&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2 id="平台应该怎么定义资源请求从gpu-张数走向可运营的推理配额"&gt;平台应该怎么定义资源请求：从“GPU 张数”走向“可运营的推理配额”&lt;/h2&gt;
&lt;p&gt;Kubernetes 的原生资源模型让我们习惯于“请求 1 张 GPU”，但在线推理的真实瓶颈并不等价于“1 张卡”。如果平台只提供“GPU=1”这一维度，团队会通过提高并发/批处理去榨干吞吐，最终把尾延迟与抖动成本转嫁给平台。&lt;/p&gt;
&lt;p&gt;更可运营的做法是：把推理资源请求拆成三层配额语义（不一定都暴露给用户，但平台需要内部具备）：&lt;/p&gt;
&lt;p&gt;下图展示三层资源配额语义。第一层（蓝色区域）：硬资源（GPU + 显存安全余量），强调显存 headroom 不是浪费而是尾延迟稳定性的保险。第二层（黄色区域）：软配额（并发与上下限），包括最大并发序列数和最大上下文/输出长度，如果只控制 QPS 不控制上下文会得到平时很好偶尔爆炸的系统。第三层（绿色区域）：体验配额（SLO 绑定的可用吞吐 - Goodput），定义为在满足指定尾延迟 SLO 的前提下系统能持续输出的有效吞吐，如果 tokens/s 上去但 Goodput 没上去说明只是把抖动转移给用户。底部核心理念：从 GPU 张数走向可运营的推理配额，让资源请求与验收具备可讨论可评审的基础。&lt;/p&gt;
&lt;figure class="mx-auto text-center"&gt;
&lt;img src="https://assets.jimmysong.io/images/book/gpu-infra/vllm/three-tier-quota.svg" data-img="https://assets.jimmysong.io/images/book/gpu-infra/vllm/three-tier-quota.svg" alt="图 2: 三层资源配额语义：从 GPU 张数走向可运营的推理配额" data-caption="图 2: 三层资源配额语义：从 GPU 张数走向可运营的推理配额"
width="1587"
height="1483"
loading="lazy" decoding="async" class="image-loading"
onload="this.classList.remove('image-loading'); this.classList.add('image-loaded');"
onerror="handleImageError(this); this.classList.remove('image-loading');"&gt;
&lt;figcaption&gt;图 2: 三层资源配额语义：从 GPU 张数走向可运营的推理配额&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h3 id="硬资源gpu--显存安全余量headroom"&gt;硬资源：GPU + 显存安全余量（headroom）&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;GPU&lt;/strong&gt;：依然以整卡（或 MIG slice）作为硬隔离单元最可靠。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;显存 headroom&lt;/strong&gt;：不要把显存用到“刚好满”。平台需要设定一个保守水位线，例如只允许 vLLM 使用显存的某个比例，其余留作碎片、峰值波动、驱动/通信开销与异常回退空间。这不是“浪费”，而是尾延迟稳定性的保险。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="软配额并发与上下限admission-control"&gt;软配额：并发与上下限（Admission Control）&lt;/h3&gt;
&lt;p&gt;平台建议明确以下两个“准入阈值”：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;最大并发序列数（max concurrent sequences）&lt;/strong&gt;：超过就排队/拒绝，而不是让系统进入不可预测区。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;最大上下文长度与最大输出长度（prompt/output cap）&lt;/strong&gt;：它们决定 KV cache 的最坏情况上界。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;如果你只控制 QPS，不控制上下文与输出长度，你会得到一个“平时很好、偶尔爆炸”的系统。&lt;/p&gt;
&lt;h3 id="体验配额slo-绑定的可用吞吐goodput"&gt;体验配额：SLO 绑定的“可用吞吐”（Goodput）&lt;/h3&gt;
&lt;p&gt;吞吐指标最好不要只看 tokens/s，而要引入 Goodput：&lt;strong&gt;在满足指定尾延迟 SLO 的前提下，系统能持续输出的有效吞吐。&lt;/strong&gt; 这会迫使所有优化回到同一个目标：既要快、也要稳。&lt;/p&gt;
&lt;h2 id="验收指标怎么定别只测平均要把尾延迟拆开测"&gt;验收指标怎么定：别只测“平均”，要把尾延迟拆开测&lt;/h2&gt;
&lt;p&gt;为了让平台具备可运营性，在线推理的验收建议至少包含四组指标，且每组都要给出 p50/p95/p99（或至少 p95/p99）：&lt;/p&gt;
&lt;p&gt;在介绍各项指标前，先说明其作用和意义。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;TTFT（Time To First Token，首 token 延迟）&lt;/strong&gt;：直接反映“队列与调度”是否健康。吞吐拉满时，TTFT 往往最先恶化。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;TPOT（Time Per Output Token，输出 token 间隔）&lt;/strong&gt;：反映生成阶段的稳定性。共享环境中，TPOT 抖动通常意味着算力争用、带宽争用或调度切片不稳定。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Throughput（tokens/s 与 Goodput）&lt;/strong&gt;：
&lt;ul&gt;
&lt;li&gt;tokens/s 反映吞吐能力&lt;/li&gt;
&lt;li&gt;Goodput 反映“在 SLO 下的吞吐”。如果 tokens/s 上去了但 Goodput 没上去，说明你只是把抖动转移给用户。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;可靠性&lt;/strong&gt;：拒绝率、OOM/回退率、重试率。尾延迟失控往往伴随拒绝、OOM、或回退策略触发。验收必须把这些作为“失败”而不是“另一个状态”。&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;实操建议：验收用例必须覆盖至少三种负载形态：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;短 prompt + 短输出（典型聊天）&lt;/li&gt;
&lt;li&gt;长 prompt + 短输出（RAG/长上下文检索）&lt;/li&gt;
&lt;li&gt;短 prompt + 长输出（长文生成）&lt;/li&gt;
&lt;li&gt;以及“混合形态”作为压测常态。&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;p&gt;下图展示验收指标体系。顶部四个关键指标：TTFT（首 token 延迟）直接反映队列与调度健康、吞吐拉满时最先恶化；TPOT（输出 token 间隔）反映生成阶段稳定性、抖动意味着算力带宽争用；Throughput 包含 tokens/s 和 Goodput（SLO 下的吞吐），tokens/s 上但 Goodput 不上说明只是把抖动转移给用户；Reliability（可靠性）包括拒绝率、OOM/回退率、重试率，尾延迟失控往往伴随这些事件。中间展示四种验收负载形态：短 prompt+ 短输出（聊天）、长 prompt+ 短输出（RAG）、短 prompt+ 长输出（长文生成）、混合形态（压测常态）。底部绿色区域说明为什么要看 p50/p95/p99。底部核心原则：吞吐优化的收益是平均意义上的，尾延迟是最坏情况的，如果验收不看 p99 会把问题留给生产。&lt;/p&gt;
&lt;figure class="mx-auto text-center"&gt;
&lt;img src="https://assets.jimmysong.io/images/book/gpu-infra/vllm/acceptance-metrics.svg" data-img="https://assets.jimmysong.io/images/book/gpu-infra/vllm/acceptance-metrics.svg" alt="图 3: 验收指标体系：别只测平均，要把尾延迟拆开测" data-caption="图 3: 验收指标体系：别只测平均，要把尾延迟拆开测"
width="1583"
height="1243"
loading="lazy" decoding="async" class="image-loading"
onload="this.classList.remove('image-loading'); this.classList.add('image-loaded');"
onerror="handleImageError(this); this.classList.remove('image-loading');"&gt;
&lt;figcaption&gt;图 3: 验收指标体系：别只测平均，要把尾延迟拆开测&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2 id="最容易引发系统性抖动的配置组合以及为什么"&gt;最容易引发系统性抖动的配置组合（以及为什么）&lt;/h2&gt;
&lt;p&gt;下面列出一些在 vLLM + Kubernetes 场景中最常见的“看似合理、实则高风险”的组合。它们的共同特点是：&lt;strong&gt;把系统推到了“显存水位线 + 队列效应”的非线性区间&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;在介绍每种配置前，先说明其风险和表现。&lt;/p&gt;
&lt;h3 id="把显存利用率设得过满--提高并发"&gt;把显存利用率设得过满 + 提高并发&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;典型表现：平时很稳，一旦某些请求变长或并发上冲，就出现 TTFT 飙升、TPOT 抖动、甚至 OOM。&lt;/li&gt;
&lt;li&gt;原因：KV cache 分配与碎片会在高水位时变得脆弱，任何波动都会触发“分配失败/回退/拒绝”链式反应。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;规避策略&lt;/strong&gt;：设定显存 headroom，宁可牺牲一点峰值吞吐，也要保证“高峰不崩”。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="混合长短请求但不做队列隔离或优先级"&gt;混合长短请求但不做队列隔离或优先级&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;典型表现：短请求的 p99 被长请求拖到不可用；用户感知为“突然卡住”。&lt;/li&gt;
&lt;li&gt;原因：continuous batching 在追求整体吞吐时，可能让短请求等待更久以换取更大的批次，长请求又延长了序列驻留时间。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;规避策略&lt;/strong&gt;：至少做到“按模型/租户/请求类型”分队列；必要时分开部署两套参数（高吞吐池 vs 低延迟池）。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="上下文长度上限过大且不做准入"&gt;上下文长度上限过大且不做准入&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;典型表现：少量超长上下文请求就能让整个服务抖动。&lt;/li&gt;
&lt;li&gt;原因：超长 prompt 直接把 KV cache 推到最坏情况上界，导致其他请求在显存/调度上被挤压。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;规避策略&lt;/strong&gt;：对外暴露明确的 context cap；超长上下文走单独服务池或异步通道。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="多租户共享同一张卡但隔离手段不足"&gt;多租户共享同一张卡，但隔离手段不足&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;典型表现：单个租户的突刺会影响所有租户；TPOT 抖动明显。&lt;/li&gt;
&lt;li&gt;原因：算力、显存带宽、PCIe/CPU/网络都可能成为干扰路径。没有足够隔离时，推理服务会出现“看不见的邻居”。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;规避策略&lt;/strong&gt;：
&lt;ul&gt;
&lt;li&gt;强隔离场景优先 MIG（Multi-Instance GPU，MIG）或至少整卡独占&lt;/li&gt;
&lt;li&gt;共享场景必须配合严格准入（并发、上下文、输出）和监控/计量，否则就是“谁先抢到算谁的”&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="盲目追求更大-batch--更高并发而不绑定-slo"&gt;盲目追求更大 batch / 更高并发，而不绑定 SLO&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;典型表现：压测报告很好看，但线上体验很差。&lt;/li&gt;
&lt;li&gt;原因：吞吐优化的收益是平均意义上的，尾延迟是最坏情况的；如果验收不看 p99，你会把问题留给生产。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;规避策略&lt;/strong&gt;：把 Goodput 作为核心 KPI：SLO 达标前提下的吞吐，才是平台应该交付的吞吐。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="一套可落地的推理资源契约让团队与平台可对齐"&gt;一套可落地的“推理资源契约”：让团队与平台可对齐&lt;/h2&gt;
&lt;p&gt;为了让推理平台可运营，建议把“资源请求与验收”固化成一份契约（可以写进平台文档或服务模板），至少包含：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;模型与服务形态&lt;/strong&gt;：模型版本、精度、并行策略（如有）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;用户侧限制&lt;/strong&gt;：最大上下文、最大输出、并发配额、速率限制&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;平台侧保障&lt;/strong&gt;：GPU/MIG 规格、显存 headroom、隔离策略、调度队列策略&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;SLO 指标&lt;/strong&gt;：TTFT/TPOT 的 p95/p99 目标，允许的拒绝率/错误率&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;验收用例&lt;/strong&gt;：短/长/混合负载的固定脚本与基线阈值&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;容量结论&lt;/strong&gt;：在 SLO 下的 Goodput（可用吞吐）与可承载并发区间&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;这样做的收益是：当某个团队说“我们要更高 QPS”，平台可以回答“在保持 p99 TTFT=… 的前提下，你可以提高并发到 X；如果你要更高，需要牺牲尾延迟或迁移到高吞吐池”，而不是陷入“感觉应该可以”的争论。&lt;/p&gt;
&lt;h2 id="总结"&gt;总结&lt;/h2&gt;
&lt;p&gt;vLLM 把在线推理的资源真相讲得很直白：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;吞吐来自并发与批处理，但代价是队列等待与尾延迟风险。&lt;/li&gt;
&lt;li&gt;显存（KV cache）是并发的硬边界，水位线决定稳定性。&lt;/li&gt;
&lt;li&gt;共享与隔离策略会放大或抑制这种边界：隔离不足时，抖动是系统性问题，不是“调参没调好”。&lt;/li&gt;
&lt;li&gt;平台要交付的不是“峰值 tokens/s”，而是“在 SLO 下的可用吞吐（Goodput）”。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;在后续实验章节中，我们会把这些结论落到可复现实验：如何用不同的共享/隔离方案（整卡、MIG、共享数据平面等）去跑同一组 vLLM 负载，并用 TTFT/TPOT/p99 与 Goodput 给出可验证的取舍曲线。这样，你的 GPU 平台能力才能从“能跑”升级为“可预测、可验收、可治理”。&lt;/p&gt;</content:encoded></item><item><title>PyTorch：训练与微调的调度语义与抢占影响</title><link>https://jimmysong.io/zh/book/ai-infra/workloads/pytorch/</link><pubDate>Tue, 30 Dec 2025 05:02:56 +0000</pubDate><author>Jimmy Song</author><guid>https://jimmysong.io/zh/book/ai-infra/workloads/pytorch/</guid><description>训练更依赖稳定吞吐与通信拓扑，讨论抢占、弹性与 checkpoint 如何影响调度器与数据平面策略。</description><content:encoded>
&lt;blockquote&gt;
&lt;p&gt;训练平台的本质挑战不是“GPU 够不够”，而是如何把调度语义变成可治理的资源服务。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;训练/微调与在线推理看似都“吃 GPU”，但它们对平台的压力点完全不同。推理更像排队系统（到达率、并发、尾延迟），训练更像生产流水线（持续吞吐、同步通信、拓扑敏感）。因此，推理平台做得好不等于训练平台就能跑得稳。训练要稳定、要效率，核心不在“能启动多少卡”，而在“能否持续产出有效步数”。&lt;/p&gt;
&lt;p&gt;本章聚焦三个问题：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;训练/微调的资源曲线是什么？为什么它比推理更敏感、也更容易被共享、抢占、碎片化击穿？&lt;/li&gt;
&lt;li&gt;训练作业需要哪些调度语义？例如最小可用、成组调度、拓扑亲和、可抢占、可挂起。&lt;/li&gt;
&lt;li&gt;资源紧张时怎么维持整体效率？抢占与弹性策略如何设计，才能让平台“忙而不乱、抢而不崩”。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="训练微调的资源曲线稳定吞吐优先而非瞬时峰值"&gt;训练/微调的资源曲线：稳定吞吐优先，而非瞬时峰值&lt;/h2&gt;
&lt;p&gt;训练与推理的性能瓶颈表现不同。在线推理常见“QPS 与 P99 的冲突”，而训练/微调则是“有效吞吐（有效 tokens/steps）与系统摩擦（通信/抖动/重启）的冲突”。&lt;/p&gt;
&lt;p&gt;训练的关键资源特征如下：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;持续性&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;训练通常是小时/天级任务，价值来自连续推进的 step。任何中断都会引入无效成本（回滚、重建 cache、重新 warmup）。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;同步通信与拓扑敏感&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;分布式训练（如 DDP、FSDP、ZeRO）依赖高频 collective（AllReduce/AllGather）。这类通信对节点内 NVLink、节点间 RDMA、网络拥塞极其敏感：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;同样的 GPU 数量，跨机分散往往显著慢于同机集中。&lt;/li&gt;
&lt;li&gt;“坏邻居”带来的网络抖动会直接映射为 step time 抖动。&lt;/li&gt;
&lt;li&gt;一张慢卡可能拖慢整个同步组（典型的“拖尾”）。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;资源单位更刚性&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;推理可以在共享方案下“切小块”跑起来；训练往往需要固定数量的 GPU + 固定拓扑约束才有意义。可以理解为：要么成团满足，要么就不该启动。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;显存与 IO 的相互制约&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;微调（尤其 LoRA/QLoRA）表面上更轻，但仍可能被显存碎片、数据加载瓶颈、checkpoint IO 限制。训练的“资源曲线”不是单维 GPU utilization，而是 GPU/CPU/内存/网络/存储的耦合曲线。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;结论：&lt;/strong&gt; 对训练而言，“让作业快点开始”不如“让作业开始后别掉速、别中断”。因此调度系统要支持：成组准入、拓扑约束、抢占策略、可恢复机制。&lt;/p&gt;
&lt;h2 id="训练工作负载分层你调度的是语义不是-pod"&gt;训练工作负载分层：你调度的是“语义”，不是 Pod&lt;/h2&gt;
&lt;p&gt;治理训练/微调时，建议按“调度语义”分层，而不是按框架或模型分层：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;A 类：单机微调/小规模训练（1～N GPU，单节点）&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;目标：快速周转、低运维成本。&lt;/li&gt;
&lt;li&gt;典型：LoRA、短跑实验、评估任务。&lt;/li&gt;
&lt;li&gt;调度重点：排队公平、基本配额、低抢占损失。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;B 类：分布式训练（多节点 DDP/FSDP，强同步）&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;目标：稳定 step time，避免跨域/跨机不确定性。&lt;/li&gt;
&lt;li&gt;调度重点：最小可用（min-available）、成组调度（gang）、拓扑（机内优先、机架/域约束）、低抖动环境。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;C 类：弹性训练（可伸缩 worker、容忍波动）&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;目标：在资源紧张时保持总体吞吐最大化。&lt;/li&gt;
&lt;li&gt;调度重点：允许伸缩/重排、与自动扩缩容协同、抢占后快速恢复。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;平台治理的关键是：不同层的作业要用不同的承诺模型。A 类可以“尽快跑”；B 类必须“整团满足”；C 类才适合机会式资源。&lt;/p&gt;
&lt;p&gt;下图展示训练工作负载的分层分类。A 类（绿色）：单机微调/小规模训练（1～N GPU，单节点），目标快速周转、低运维成本，调度重点是排队公平、基本配额、低抢占损失。B 类（黄色）：分布式训练（多节点 DDP/FSDP，强同步），目标稳定 step time 避免跨域不确定性，调度重点是最小可用 min-available、成组调度 gang、拓扑约束、低抖动环境。C 类（蓝色）：弹性训练（可伸缩 worker，容忍波动），目标资源紧张时保持总体吞吐最大，调度重点是允许伸缩重排、与自动扩缩容协同、抢占后快速恢复、机会式资源利用。底部灰色区域展示训练的四个关键资源特征：持续性、同步通信与拓扑敏感、资源单位更刚性、显存与 IO 相互制约。结论：对训练而言让作业快点开始不如让作业开始后别掉速、别中断，调度系统必须支持成组准入、拓扑约束、抢占策略、可恢复机制。&lt;/p&gt;
&lt;figure class="mx-auto text-center"&gt;
&lt;img src="https://assets.jimmysong.io/images/book/gpu-infra/pytorch/workload-classification.svg" data-img="https://assets.jimmysong.io/images/book/gpu-infra/pytorch/workload-classification.svg" alt="图 1: 训练工作负载分层：你调度的是语义，不是 Pod" data-caption="图 1: 训练工作负载分层：你调度的是语义，不是 Pod"
width="1627"
height="1483"
loading="lazy" decoding="async" class="image-loading"
onload="this.classList.remove('image-loading'); this.classList.add('image-loaded');"
onerror="handleImageError(this); this.classList.remove('image-loading');"&gt;
&lt;figcaption&gt;图 1: 训练工作负载分层：你调度的是语义，不是 Pod&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2 id="pytorch-训练需要的调度语义清单"&gt;PyTorch 训练需要的调度语义清单&lt;/h2&gt;
&lt;p&gt;训练系统落到 Kubernetes 时，最容易踩的坑是：把“训练作业”降维成“Pod 集合”，于是丢掉了语义。要让训练可治理，需要至少具备以下语义（由控制面实现，而非靠用户自觉）：&lt;/p&gt;
&lt;h3 id="最小可用min-available"&gt;最小可用（Min-Available）&lt;/h3&gt;
&lt;p&gt;分布式训练常见需求：没有足够 worker/PS/launcher 就不要启动。否则会出现：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;只启动了一部分 worker，反复 crash / backoff；&lt;/li&gt;
&lt;li&gt;训练框架一直等 rendezvous，造成 GPU 空转；&lt;/li&gt;
&lt;li&gt;资源被占着不产出，形成隐性浪费。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="成组调度gang-scheduling"&gt;成组调度（Gang Scheduling）&lt;/h3&gt;
&lt;p&gt;训练的基本单位是一个“组”（一个 job），不是单个 Pod。成组调度解决两件事：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;同时分配：避免分配到一半卡住；&lt;/li&gt;
&lt;li&gt;一致性：保证训练拓扑与通信预期一致。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="拓扑与亲和topology-awareness"&gt;拓扑与亲和（Topology Awareness）&lt;/h3&gt;
&lt;p&gt;对训练而言，拓扑优先级通常是：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;同一节点（NVLink/共享内存）&lt;/li&gt;
&lt;li&gt;同机架/同网络域（RDMA/更低抖动）&lt;/li&gt;
&lt;li&gt;跨域（最不稳定）&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;如果控制面无法表达拓扑偏好，训练会被随机摊开，导致吞吐不可预测。&lt;/p&gt;
&lt;h3 id="可抢占preemptible与不可抢占guaranteed"&gt;可抢占（Preemptible）与不可抢占（Guaranteed）&lt;/h3&gt;
&lt;p&gt;训练平台必须允许用户声明“可抢占”层（低优先级、容忍中断）与“保证层”（关键训练、不可随意打断）。否则多租户下要么大家都不敢跑大任务，要么谁都在抢占别人。&lt;/p&gt;
&lt;h3 id="可挂起恢复suspendresume"&gt;可挂起/恢复（Suspend/Resume）&lt;/h3&gt;
&lt;p&gt;比“杀掉重启”更温和的治理手段是“挂起”：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;把训练从运行态转为等待态，释放 GPU；&lt;/li&gt;
&lt;li&gt;资源回来后再恢复；&lt;/li&gt;
&lt;li&gt;结合 checkpoint，降低抢占损失。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这会显著改善资源紧张时的整体效率与用户体验（尤其是大作业）。&lt;/p&gt;
&lt;p&gt;下图展示 PyTorch 训练需要的 5 种核心调度语义。最小可用（Min-Available）：没有足够 worker/PS/launcher 就不要启动。成组调度（Gang Scheduling）：同时分配与一致性，保证训练拓扑与通信预期一致。拓扑与亲和（Topology Awareness）：优先级从高到低为同节点（NVLink）、同机架（RDMA）、跨域（最不稳定）。可抢占/保证（Preemptible/Guaranteed）：声明可抢占层（低优先级）与保证层（关键训练）。可挂起/恢复（Suspend/Resume）：比杀掉重启更温和，结合 checkpoint 降低抢占损失。右侧灰色区域强调这些语义由控制面实现而非靠用户自觉：Kueue 负责准入配额优先级与公平，Volcano 负责成组调度与策略落地，数据平面定义资源单位与隔离边界。底部展示拓扑优先级，核心理念：把训练作业降维成 Pod 集合会丢失语义，要让训练可治理需要完整的调度语义支撑。&lt;/p&gt;
&lt;figure class="mx-auto text-center"&gt;
&lt;img src="https://assets.jimmysong.io/images/book/gpu-infra/pytorch/scheduling-semantics.svg" data-img="https://assets.jimmysong.io/images/book/gpu-infra/pytorch/scheduling-semantics.svg" alt="图 2: PyTorch 训练需要的调度语义清单" data-caption="图 2: PyTorch 训练需要的调度语义清单"
width="1593"
height="1412"
loading="lazy" decoding="async" class="image-loading"
onload="this.classList.remove('image-loading'); this.classList.add('image-loaded');"
onerror="handleImageError(this); this.classList.remove('image-loading');"&gt;
&lt;figcaption&gt;图 2: PyTorch 训练需要的调度语义清单&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2 id="抢占的真实代价你抢走的是已完成的有效工作"&gt;抢占的真实代价：你抢走的是“已完成的有效工作”&lt;/h2&gt;
&lt;p&gt;抢占不仅仅是“把 GPU 让给更重要的人”，它伴随三类成本：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;重启成本（Restart Overhead）&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;重新拉起容器、重新下载权重/数据、重新初始化通信组。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;回滚成本（Rollback / Lost Progress）&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;checkpoint 周期越长，被抢占损失越大；周期越短，IO 压力越大。这是一个可度量的权衡。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;震荡成本（Systemic Thrashing）&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;如果抢占策略不稳定，会出现“你抢我、我抢你”的系统性抖动：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;GPU 使用率看似很高，但有效训练吞吐下降；&lt;/li&gt;
&lt;li&gt;队列里作业频繁切换，整体完成时间变长；&lt;/li&gt;
&lt;li&gt;用户感知为“平台不靠谱”。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;抢占策略的目标不应是“让某个高优先级作业立刻启动”，而是：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;在满足关键作业 SLA 的前提下，让全局有效吞吐最大，并把无效成本控制在可接受范围内。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;下图展示抢占的三种真实成本。重启成本（红色）：重新拉起容器、重新下载权重/数据、重新初始化通信组、重新 warmup。回滚成本（黄色）：checkpoint 周期越长被抢占损失越大，周期越短 IO 压力越大，这是一个可度量的权衡。震荡成本（蓝色）：GPU 使用率高但有效训练吞吐下降、作业频繁切换整体完成时间变长、你抢我我抢你的系统性抖动、用户感知为平台不靠谱。绿色区域说明抢占策略的正确目标：在满足关键作业 SLA 的前提下让全局有效吞吐最大并把无效成本控制在可接受范围内，而不是简单让某个高优先级作业立刻启动。灰色区域列出降低抢占损失的 5 种策略：区分可抢占层与保证层、制定 checkpoint 规范、引入挂起恢复机制、避免震荡、分离训练与推理资源池。底部核心洞察：抢占不是免费的，真正的代价是已完成的有效工作和系统稳定性。&lt;/p&gt;
&lt;figure class="mx-auto text-center"&gt;
&lt;img src="https://assets.jimmysong.io/images/book/gpu-infra/pytorch/preemption-cost.svg" data-img="https://assets.jimmysong.io/images/book/gpu-infra/pytorch/preemption-cost.svg" alt="图 3: 抢占的真实代价：你抢走的是已完成的有效工作" data-caption="图 3: 抢占的真实代价：你抢走的是已完成的有效工作"
width="1627"
height="1403"
loading="lazy" decoding="async" class="image-loading"
onload="this.classList.remove('image-loading'); this.classList.add('image-loaded');"
onerror="handleImageError(this); this.classList.remove('image-loading');"&gt;
&lt;figcaption&gt;图 3: 抢占的真实代价：你抢走的是已完成的有效工作&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2 id="把训练纳入队列治理体系以-kueue-为准入核心"&gt;把训练纳入队列治理体系：以 Kueue 为准入核心&lt;/h2&gt;
&lt;p&gt;推荐的治理分工如下：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Kueue：负责准入、配额、优先级与公平（Admission / Quota / Fairness）&lt;/strong&gt;&lt;br&gt;
解决“谁能用、何时能用、能用多少”，并做全局资源承诺与统计。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;调度器/批处理控制面（如 Volcano 或具备同等语义的组件）：负责成组调度与策略落地（Gang / Policy）&lt;/strong&gt;&lt;br&gt;
解决“怎么分配到具体节点、如何满足 min-available、如何处理拓扑与抢占”。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;数据平面（MIG/共享方案）：定义资源单位与隔离边界&lt;/strong&gt;&lt;br&gt;
决定“一个训练作业申请的 GPU 单位到底是什么”。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这套组合能避免一个常见错误：用一个组件同时管准入、公平、拓扑与成组，最终谁都管不好。&lt;/p&gt;
&lt;h3 id="推荐的落地路径从易到难"&gt;推荐的落地路径（从易到难）&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;阶段 0：先把训练从推理池里分出来&lt;/strong&gt;&lt;br&gt;
最小化互相伤害：推理看尾延迟，训练看吞吐，两者混跑时冲突巨大。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;阶段 1：用 Kueue 建立队列与配额&lt;/strong&gt;&lt;br&gt;
先做到可解释：每个团队有配额、作业进队列、等待有原因、使用可统计。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;阶段 2：为分布式训练引入成组与 min-available&lt;/strong&gt;&lt;br&gt;
否则多节点训练会产生大量“半启动”浪费。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;阶段 3：引入抢占，但必须同时引入 checkpoint 规范&lt;/strong&gt;&lt;br&gt;
没有 checkpoint 的抢占等于破坏性操作。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;阶段 4：引入弹性训练（可伸缩 worker）与机会式资源层&lt;/strong&gt;&lt;br&gt;
把可抢占/可弹性的作业放到低成本资源上（空闲、夜间、可回收节点），提升全局利用率。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="验收指标训练平台要验有效产出不是只看-gpu-利用率"&gt;验收指标：训练平台要验“有效产出”，不是只看 GPU 利用率&lt;/h2&gt;
&lt;p&gt;建议把验收指标分三层：&lt;/p&gt;
&lt;h3 id="作业级job"&gt;作业级（Job）&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Time-to-Start：从提交到开始训练的时间（含队列等待）&lt;/li&gt;
&lt;li&gt;有效吞吐：tokens/s 或 steps/s（建议对关键模型固定基准）&lt;/li&gt;
&lt;li&gt;step time P50/P95：衡量抖动&lt;/li&gt;
&lt;li&gt;重启次数与恢复时间：抢占/失败后的恢复能力&lt;/li&gt;
&lt;li&gt;checkpoint 开销占比：IO 成本是否可控&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="队列级queue"&gt;队列级（Queue）&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;公平性：同优先级作业的等待时间分布&lt;/li&gt;
&lt;li&gt;阻塞与头阻塞（HOL）：大作业是否长期卡住队列&lt;/li&gt;
&lt;li&gt;抢占损失率：被抢占导致的无效 GPU 时间占比&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="集群级cluster"&gt;集群级（Cluster）&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;有效训练吞吐总量（全局）&lt;/li&gt;
&lt;li&gt;跨作业干扰事件（网络/存储拥塞导致的抖动）&lt;/li&gt;
&lt;li&gt;碎片率：由于资源单位/拓扑导致的不可用碎片&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="关键风险点清单评审时必须回答"&gt;关键风险点清单（评审时必须回答）&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;多节点训练是否强制 min-available + gang？否则大概率出现“占着资源不产出”。&lt;/li&gt;
&lt;li&gt;拓扑不可控时，性能 SLO 如何保证？不能保证就要在产品层明确“best effort”。&lt;/li&gt;
&lt;li&gt;抢占发生时，checkpoint 策略谁负责？频率如何约束？不回答就等于默认“抢占不可用”。&lt;/li&gt;
&lt;li&gt;恢复时是否会造成队列震荡？需要明确：恢复优先级、冷却时间、最大并发恢复数。&lt;/li&gt;
&lt;li&gt;训练与推理是否共享同一物理池？如果共享，必须有明确的隔离与优先级边界，否则推理尾延迟会被训练放大。&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="总结"&gt;总结&lt;/h2&gt;
&lt;p&gt;PyTorch 训练/微调之所以难，不在于 GPU 数量，而在于它需要的平台语义更多：成组、最小可用、拓扑、可恢复、可抢占、可解释。当这些语义被治理体系（以 Kueue 为准入中心）承接后，训练就不再是“谁先抢到算谁的”，而是变成可评审、可实施、可验收的资源服务。&lt;/p&gt;
&lt;p&gt;下一章将进入更具体的拓扑与分布式场景（Ray 与拓扑），把“语义”落实到可观测、可实验的结论链中。&lt;/p&gt;</content:encoded></item><item><title>Ray/KubeRay 与拓扑约束：从单卡到多节点</title><link>https://jimmysong.io/zh/book/ai-infra/workloads/ray-kuberay-topology/</link><pubDate>Tue, 30 Dec 2025 05:03:00 +0000</pubDate><author>Jimmy Song</author><guid>https://jimmysong.io/zh/book/ai-infra/workloads/ray-kuberay-topology/</guid><description>多卡/多节点场景下，GPU 调度从资源数量升级为拓扑与通信主导，需关注资源表达与可用性验收。</description><content:encoded>
&lt;blockquote&gt;
&lt;p&gt;资源数量易得，资源位置难控。多节点 GPU 平台的核心挑战，是让“可调度”真正等于“可用”。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;单卡阶段，讨论“共享/隔离”时，很多经验默认了一个前提：&lt;strong&gt;瓶颈主要在 GPU 本身与显存&lt;/strong&gt;。一旦进入多卡、多节点，瓶颈会迅速外溢到 &lt;strong&gt;NUMA、PCIe/NVLink、NIC/交换网络与通信库&lt;/strong&gt;。于是会出现一种最常见、也最隐蔽的失败：&lt;strong&gt;看起来可调度（scheduler 成功把 Pod 放上去了），但跑起来不可用（吞吐崩、尾延迟抖、NCCL 报错、训练步时不稳定）&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;本章聚焦于以下三个核心问题：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;控制面如何表达拓扑约束&lt;/strong&gt;：让“该去哪儿跑”从拍脑袋变成可声明、可审查、可执行的约束。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;数据平面如何暴露资源单元&lt;/strong&gt;：让“我到底拿到了什么”可以被工作负载和平台共同验证。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;如何避免“可调度但不可用”的伪成功&lt;/strong&gt;：把拓扑从“最佳努力”升级为“可验收”。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="单卡经验在多卡多节点场景下的失效"&gt;单卡经验在多卡/多节点场景下的失效&lt;/h2&gt;
&lt;p&gt;在多卡/多节点环境下，性能瓶颈不再只由 GPU 算力决定，而是由一组链路共同决定。下文介绍这些关键链路及其影响。&lt;/p&gt;
&lt;h3 id="性能主导因素的迁移"&gt;性能主导因素的迁移&lt;/h3&gt;
&lt;p&gt;以下是多卡/多节点场景下影响性能的主要链路：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;CPU ↔ GPU 的亲和性（NUMA, Non-Uniform Memory Access）&lt;/strong&gt;：GPU 挂在哪个 CPU Socket，下游内存/PCIe Root Complex 是谁，决定了数据喂给 GPU 的效率与抖动上限。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;GPU ↔ GPU 的互联（NVLink / PCIe）&lt;/strong&gt;：同一节点内，GPU 间通信的拓扑决定了 NCCL ring/tree 的实际带宽与延迟。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;节点 ↔ 节点的网络（以太网 / RoCE / IB / EFA）&lt;/strong&gt;：跨节点 all-reduce、参数同步、KV cache 分片都依赖网络与 RDMA（Remote Direct Memory Access）栈质量。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;系统软件栈的“隐形限制”&lt;/strong&gt;：驱动版本、NCCL/UCX、容器权限、&lt;code&gt;/dev/shm&lt;/code&gt;、IOMMU、cgroup/CPU pinning 等，都会把“可用性”变成概率事件。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="典型伪成功症状"&gt;典型“伪成功”症状&lt;/h3&gt;
&lt;p&gt;以下场景中常见“调度成功但不可用”的现象：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;训练作业&lt;/strong&gt;：NCCL 初始化慢/失败；步时波动大；跨节点带宽远低于预期；节点间拓扑不一致导致效率崩盘。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Ray 推理/Serving&lt;/strong&gt;：同一模型副本放到了跨 NUMA 的 GPU 上，吞吐下降但 p99/p999 尾延迟暴涨；或者 worker 分散到网络质量差的节点，出现系统性抖动。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Ray 分布式任务&lt;/strong&gt;：placement group 表面满足资源数，但实际落点跨机架/跨 AZ，导致“可跑但不值钱”。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;结论：&lt;strong&gt;从单卡到多节点，本质是从“资源数量问题”升级为“资源位置问题”。&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id="拓扑约束的三个层次"&gt;拓扑约束的三个层次&lt;/h2&gt;
&lt;p&gt;为了便于理解拓扑约束，通常将其分为三层，层级越低越贴近硬件：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;集群拓扑（Cluster Topology）&lt;/strong&gt;：Region / Zone / 机房 / 机架 / 交换域 / 网络平面（如是否同一 ToR、是否同一 RDMA fabric）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;节点拓扑（Node Topology）&lt;/strong&gt;：NUMA 节点、CPU socket、PCIe 拓扑、GPU/NIC 挂载关系、NVLink island。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;设备拓扑（Device Topology）&lt;/strong&gt;：GPU 之间的 P2P 能力、NVLink 链路数量、GPU 与 NIC 的亲和（GPUDirect RDMA）、MIG（Multi-Instance GPU）实例归属等。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;控制面&lt;/strong&gt;负责将这些拓扑“表达出来并约束调度”，&lt;strong&gt;数据平面&lt;/strong&gt;负责将这些拓扑“暴露出来并可被验证”。&lt;/p&gt;
&lt;p&gt;下图展示拓扑约束的三个层次。集群拓扑（蓝色区域，最上层）：Region/Zone/机房/机架/交换域/网络平面，适用于跨机房部署、容灾、网络质量约束。节点拓扑（黄色区域，中间层）：NUMA 节点/CPU socket/PCIe 拓扑/GPU NIC 挂载关系/NVLink island，适用于单节点多卡训练、PCIe/NVLink 优化、NUMA 亲和。设备拓扑（绿色区域，最底层）：GPU 之间 P2P 能力/NVLink 链路数量/GPU 与 NIC 亲和/MIG 实例归属，适用于 MIG 虚拟化、GPU-GPU 通信优化、RDMA 亲和。底部灰色区域说明控制面与数据面的职责分工：控制面将拓扑表达并约束调度（Kubernetes 的 nodeSelector/affinity/spread/gang + Ray 的 Placement Group），数据平面将拓扑暴露并可验证（NFD、GPU/NIC 拓扑采集）。核心理念：从单卡到多节点，本质是从资源数量问题升级为资源位置与结构问题。&lt;/p&gt;
&lt;figure class="mx-auto text-center"&gt;
&lt;img src="https://assets.jimmysong.io/images/book/gpu-infra/ray-kuberay-topology/topology-layers.svg" data-img="https://assets.jimmysong.io/images/book/gpu-infra/ray-kuberay-topology/topology-layers.svg" alt="图 1: 拓扑约束的三个层次：从集群到设备" data-caption="图 1: 拓扑约束的三个层次：从集群到设备"
width="1587"
height="1763"
loading="lazy" decoding="async" class="image-loading"
onload="this.classList.remove('image-loading'); this.classList.add('image-loaded');"
onerror="handleImageError(this); this.classList.remove('image-loading');"&gt;
&lt;figcaption&gt;图 1: 拓扑约束的三个层次：从集群到设备&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2 id="控制面kubernetes-如何表达拓扑约束"&gt;控制面：Kubernetes 如何表达拓扑约束&lt;/h2&gt;
&lt;p&gt;Kubernetes 默认调度以“资源计数 + 约束过滤”为主。要让其理解拓扑，需用到以下原语：&lt;/p&gt;
&lt;h3 id="节点选择与亲和nodeselector--nodeaffinity"&gt;节点选择与亲和（NodeSelector / NodeAffinity）&lt;/h3&gt;
&lt;p&gt;通过标签将“节点能力/位置”编码成可声明条件：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;topology.kubernetes.io/zone=...&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;kubernetes.io/hostname=...&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;自定义：&lt;code&gt;gpu.nvidia.com/arch=hopper&lt;/code&gt;、&lt;code&gt;rdma=true&lt;/code&gt;、&lt;code&gt;nvlink.island=...&lt;/code&gt;、&lt;code&gt;numa.policy=...&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;适用于粗粒度过滤（如同 AZ、同机房、必须 RDMA 等），但不适用于细粒度“同一节点内 GPU 亲和”或“跨节点成组”需求（此时需更强语义）。&lt;/p&gt;
&lt;h3 id="反亲和与分布podantiaffinity--topologyspreadconstraints"&gt;反亲和与分布（PodAntiAffinity / TopologySpreadConstraints）&lt;/h3&gt;
&lt;p&gt;通过分散副本到不同故障域或网络域，避免“同机架/同节点热点”：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;推理副本：跨节点/跨机架 spread，提高容灾与尾延迟稳定性。&lt;/li&gt;
&lt;li&gt;训练 worker：通常更希望“靠近”而不是 spread（此时需用 gang + 机架亲和）。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="组调度gang-scheduling"&gt;组调度（Gang Scheduling）&lt;/h3&gt;
&lt;p&gt;多卡/多节点训练的核心不是“一个 Pod 能不能放”，而是“&lt;strong&gt;一组 Pod 能否一起放到满足拓扑的资源集合上&lt;/strong&gt;”。&lt;/p&gt;
&lt;p&gt;在原生 Kubernetes 中，组调度能力尚在完善。常见方案包括：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Volcano&lt;/strong&gt;：PodGroup / queue / priority / preemption（偏批处理语义）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Kueue&lt;/strong&gt;：准入 + 配额 + 资源承诺/归还（偏治理语义）。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;无论选用哪种方案，核心目标都是：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;要么整组满足拓扑并一起启动，要么都不启动。&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="控制面raykuberay-如何表达资源位置"&gt;控制面：Ray/KubeRay 如何表达“资源位置”&lt;/h2&gt;
&lt;p&gt;Ray 的抽象是“资源与调度”，KubeRay 负责将这些抽象落到 Kubernetes 上。需理解两套语义如何叠加。&lt;/p&gt;
&lt;h3 id="ray-的资源表达从数量到形状"&gt;Ray 的资源表达：从“数量”到“形状”&lt;/h3&gt;
&lt;p&gt;Ray 常见的资源表达方式包括：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;num_cpus&lt;/code&gt; / &lt;code&gt;num_gpus&lt;/code&gt;：数量型资源（最基础）。&lt;/li&gt;
&lt;li&gt;自定义资源：如 &lt;code&gt;{&amp;quot;accelerator_type:A100&amp;quot;: 1}&lt;/code&gt; 或 &lt;code&gt;{&amp;quot;rdma&amp;quot;: 1}&lt;/code&gt;（用于约束落点）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Placement Group&lt;/strong&gt;：将一组任务/actor 以“bundle”打包调度，并指定策略：
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;STRICT_PACK&lt;/code&gt;：尽量同节点（适合单节点多卡，最强 locality）。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;STRICT_SPREAD&lt;/code&gt;：强制分散（适合高可用副本或跨节点并行但要求分散）。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;实战建议：多节点训练/多 actor 并行时，优先用 placement group 明确“通信边界”，否则只能依赖调度器默认行为。&lt;/p&gt;
&lt;h3 id="kuberay-的落地workergroupspec--k8s-约束"&gt;KubeRay 的落地：WorkerGroupSpec + K8s 约束&lt;/h3&gt;
&lt;p&gt;KubeRay 的 RayCluster / RayJob 通常按 head/worker 分组定义 PodTemplate。可将 Kubernetes 的拓扑约束写入每个 group：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;训练 worker group：nodeAffinity 约束到同一网络域（如同机架/同 IB fabric），并配合 gang（Volcano/Kueue）。&lt;/li&gt;
&lt;li&gt;推理副本 group：topology spread 到不同节点/不同故障域，降低尾延迟系统性风险。&lt;/li&gt;
&lt;li&gt;需要 RDMA 的 group：强制 &lt;code&gt;rdma=true&lt;/code&gt;，并加 admission 校验。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Ray 的 placement group 与 Kubernetes 的 affinity/spread 并不互斥：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Kubernetes 负责“候选节点集合”&lt;/strong&gt;（能跑、在哪儿跑）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Ray 负责“组内结构”&lt;/strong&gt;（谁跟谁在一起、谁必须分开）&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="数据平面如何暴露你拿到的资源单元"&gt;数据平面：如何暴露“你拿到的资源单元”&lt;/h2&gt;
&lt;p&gt;控制面负责“表达约束”，数据平面则必须将资源单元暴露为可观测、可校验的事实。否则平台只能凭标签与静态配置猜测拓扑，伪成功难以避免。&lt;/p&gt;
&lt;h3 id="节点侧发现与标注discovery--labeling"&gt;节点侧：发现与标注（Discovery &amp;amp; Labeling）&lt;/h3&gt;
&lt;p&gt;需将“拓扑事实”转化为节点标签或可查询信息。常见方式包括：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Node Feature Discovery（NFD, Node Feature Discovery）&lt;/strong&gt;：将硬件能力/特征打到 Node label。&lt;/li&gt;
&lt;li&gt;GPU/NIC 拓扑采集：将 NVLink island、PCIe Root Complex、NIC 亲和等生成标签或 CRD（Custom Resource Definition，具体实现相关）。&lt;/li&gt;
&lt;li&gt;网络域标签：机架、ToR、fabric id（来自机房资产系统或自动发现）。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;目标是让调度约束&lt;strong&gt;不依赖人工维护&lt;/strong&gt;，否则维护成本极高。&lt;/p&gt;
&lt;h3 id="设备侧gpu-资源暴露与隔离"&gt;设备侧：GPU 资源暴露与隔离&lt;/h3&gt;
&lt;p&gt;根据所选数据平面（整卡、MIG、共享虚拟化），暴露的资源单元不同：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;整卡&lt;/strong&gt;：资源单元清晰，但仍需暴露拓扑（如 GPU0 和 NIC0 的亲和、GPU0/1 是否 NVLink）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;MIG（Multi-Instance GPU）&lt;/strong&gt;：资源单元离散且带强隔离，需要额外暴露“实例属于哪张物理卡/哪组 NVLink”信息，避免“实例可分配但通信不可控”。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;共享虚拟化（如 HAMi 类）&lt;/strong&gt;：更需可观测性与干扰检测，否则无法证明共享后性能是否满足 SLA（Service Level Agreement，服务等级协议）。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="避免可调度但不可用将拓扑约束升级为可验收"&gt;避免“可调度但不可用”：将拓扑约束升级为可验收&lt;/h2&gt;
&lt;p&gt;伪成功往往源于缺少“端到端校验”。建议将校验分为三层：&lt;strong&gt;准入校验、启动校验、运行时验收&lt;/strong&gt;。&lt;/p&gt;
&lt;h3 id="准入校验admission"&gt;准入校验（Admission）&lt;/h3&gt;
&lt;p&gt;在作业进入队列或被准入前，检查是否声明了必要的拓扑需求，且集群中存在满足条件的资源池：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;需要 RDMA 的训练：必须声明 &lt;code&gt;rdma=true&lt;/code&gt;，且配额池中有对应资源。&lt;/li&gt;
&lt;li&gt;需要同节点多卡（NVLink）的推理：必须声明 &lt;code&gt;nvlink=true&lt;/code&gt; 或“同 island”标签。&lt;/li&gt;
&lt;li&gt;多节点训练：必须声明 &lt;code&gt;minNodes&lt;/code&gt;、&lt;code&gt;gpusPerNode&lt;/code&gt;、&lt;code&gt;topologyScope&lt;/code&gt;（同机房/同机架/同 AZ）。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;结果只有两种&lt;/strong&gt;：可准入 / 不可准入。避免作业“先进队列再慢慢失败”。&lt;/p&gt;
&lt;h3 id="启动校验startup-probing"&gt;启动校验（Startup Probing）&lt;/h3&gt;
&lt;p&gt;作业启动时，在 worker init 阶段做快速自检，尽早暴露“不可用”：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;GPU ↔ GPU P2P/NVLink 可用性探测（同节点）&lt;/li&gt;
&lt;li&gt;NCCL/UCX 初始化探测（跨节点）&lt;/li&gt;
&lt;li&gt;NIC/IB 设备存在与权限（容器内可见、驱动匹配）&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/dev/shm&lt;/code&gt;、hugepage、CPU pinning/NUMA policy 是否满足&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;启动校验失败应&lt;strong&gt;快速 fail-fast&lt;/strong&gt;，并将失败原因写入事件与指标系统，避免“挂起与重试风暴”。&lt;/p&gt;
&lt;h3 id="运行时验收slosla"&gt;运行时验收（SLO/SLA）&lt;/h3&gt;
&lt;p&gt;将“拓扑是否正确”转化为可观测指标与验收门槛：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;训练：
&lt;ul&gt;
&lt;li&gt;NCCL all-reduce 带宽/延迟（可用基准测试或训练过程 proxy 指标）&lt;/li&gt;
&lt;li&gt;step time 的 p95/p99 抖动&lt;/li&gt;
&lt;li&gt;GPU util、PCIe/NVLink throughput、网络吞吐与重传&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;推理（Ray Serve / vLLM on Ray）：
&lt;ul&gt;
&lt;li&gt;p99/p999 latency 与 tail amplification&lt;/li&gt;
&lt;li&gt;每卡吞吐稳定性（标准差/变异系数）&lt;/li&gt;
&lt;li&gt;关键路径的 CPU NUMA 远端访问比例（若可采集）&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;当验收不达标时，排查方向主要有两类：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;约束未表达清楚&lt;/strong&gt;（调度放错地方）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;资源单元未暴露清楚&lt;/strong&gt;（以为有 NVLink/有 RDMA，实际没有或不可用）&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;下图展示避免“可调度但不可用”的三层校验体系。准入校验（红色区域）：作业进入队列或被准入前，检查是否声明必要的拓扑需求、集群中是否存在满足条件的资源池，结果只有可准入或不可准入两种，避免先进队列再慢慢失败。启动校验（黄色区域）：作业启动时 worker init 阶段做快速自检，包括 GPU↔GPU P2P/NVLink 可用性、NCCL/UCX 初始化、NIC/IB 设备与权限、/dev/shm/hugepage/CPU pinning/NUMA policy，要求快速 fail-fast 并将失败原因写入事件与指标系统。运行时验收（蓝色区域）：将拓扑正确性转化为可观测指标，训练验收 NCCL all-reduce 带宽延迟、step time 的 p95/p99 抖动、GPU util 等，推理验收 p99/p999 latency、每卡吞吐稳定性、CPU NUMA 远端访问比例。底部说明验收不达标时的两个排查方向：约束未表达清楚（调度放错地方）、资源单元未暴露清楚（以为有 NVLink/RDMA 实际没有或不可用）。核心理念：伪成功源于缺少端到端校验，必须把拓扑从最佳努力升级为可验收。&lt;/p&gt;
&lt;figure class="mx-auto text-center"&gt;
&lt;img src="https://assets.jimmysong.io/images/book/gpu-infra/ray-kuberay-topology/validation-layers.svg" data-img="https://assets.jimmysong.io/images/book/gpu-infra/ray-kuberay-topology/validation-layers.svg" alt="图 2: 避免可调度但不可用：三层校验体系" data-caption="图 2: 避免可调度但不可用：三层校验体系"
width="1627"
height="1023"
loading="lazy" decoding="async" class="image-loading"
onload="this.classList.remove('image-loading'); this.classList.add('image-loaded');"
onerror="handleImageError(this); this.classList.remove('image-loading');"&gt;
&lt;figcaption&gt;图 2: 避免可调度但不可用：三层校验体系&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2 id="参考组合从能跑到可交付的三种模式"&gt;参考组合：从“能跑”到“可交付”的三种模式&lt;/h2&gt;
&lt;p&gt;下表总结了三种常见的 GPU 平台成熟度路径，便于后续架构组合参考。&lt;/p&gt;
&lt;p&gt;这是三种典型模式的对比：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;模式&lt;/th&gt;
&lt;th&gt;适用场景&lt;/th&gt;
&lt;th&gt;关键点&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;单节点多卡优先（最强 locality）&lt;/td&gt;
&lt;td&gt;单机多卡推理、单机训练、低运维复杂度团队&lt;/td&gt;
&lt;td&gt;- K8s：强制同节点（nodeAffinity + 资源 bundle）&lt;br&gt;- Ray：placement group &lt;code&gt;STRICT_PACK&lt;/code&gt;&lt;br&gt;- 数据平面：整卡或 MIG（强调实例与物理卡映射可见）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;多节点训练（通信优先 + gang）&lt;/td&gt;
&lt;td&gt;分布式训练/微调、Ray Train、多节点数据并行&lt;/td&gt;
&lt;td&gt;- gang：Volcano 或 Kueue（保证整组准入/启动一致）&lt;br&gt;- 网络域亲和：同机房/同 fabric&lt;br&gt;- 启动校验：NCCL/UCX/RDMA 预检必做&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;推理多副本（稳定性优先 + spread）&lt;/td&gt;
&lt;td&gt;在线推理、多租户、多副本 HA&lt;/td&gt;
&lt;td&gt;- spread：跨节点/跨故障域分散，避免同域抖动&lt;br&gt;- 资源治理：配额/准入（Kueue）防止被训练挤爆&lt;br&gt;- 验收：以尾延迟为硬指标，优先“稳定”而非“峰值吞吐”&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;figcaption class="text-center mb-3"&gt;
表 1: GPU 平台多节点能力落地模式对比
&lt;/figcaption&gt;
&lt;p&gt;下图展示三种典型落地模式的演进路径。模式 1（绿色）：单节点多卡优先（最强 locality），适用场景是单机多卡推理、单机训练、低运维复杂度团队，关键技术点包括 K8s 强制同节点（nodeAffinity + 资源 bundle）、Ray placement group STRICT_PACK、数据平面整卡或 MIG（强调实例与物理卡映射可见）。模式 2（黄色）：多节点训练（通信优先 + gang），适用场景是分布式训练/微调、Ray Train、多节点数据并行，关键技术点包括 gang（Volcano 或 Kueue 保证整组准入启动一致）、网络域亲和（同机房同 fabric）、启动校验（NCCL/UCX/RDMA 预检必做）。模式 3（蓝色）：推理多副本（稳定性优先 + spread），适用场景是在线推理、多租户、多副本 HA，关键技术点包括 spread（跨节点跨故障域分散避免同域抖动）、资源治理配额准入（Kueue 防止被训练挤爆）、验收以尾延迟为硬指标（优先稳定而非峰值吞吐）。底部展示成熟度演进路径：从能跑（模式 1）→ 可交付（模式 2）→ 可运营（模式 3）。核心理念：多节点 GPU 平台的核心挑战是让可调度真正等于可用，避免伪成功。&lt;/p&gt;
&lt;figure class="mx-auto text-center"&gt;
&lt;img src="https://assets.jimmysong.io/images/book/gpu-infra/ray-kuberay-topology/deployment-patterns.svg" data-img="https://assets.jimmysong.io/images/book/gpu-infra/ray-kuberay-topology/deployment-patterns.svg" alt="图 3: 三种典型落地模式：从能跑到可交付" data-caption="图 3: 三种典型落地模式：从能跑到可交付"
width="1627"
height="1023"
loading="lazy" decoding="async" class="image-loading"
onload="this.classList.remove('image-loading'); this.classList.add('image-loaded');"
onerror="handleImageError(this); this.classList.remove('image-loading');"&gt;
&lt;figcaption&gt;图 3: 三种典型落地模式：从能跑到可交付&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2 id="落地清单checklist"&gt;落地清单（Checklist）&lt;/h2&gt;
&lt;p&gt;上线 Ray/KubeRay 多节点能力前，建议至少明确以下问题：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;拓扑事实从哪来？&lt;/strong&gt;（NFD/资产系统/自动探测）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;拓扑如何表达？&lt;/strong&gt;（label/affinity/spread/gang + Ray placement group）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;资源单元是什么？&lt;/strong&gt;（整卡/MIG/共享；是否可观测可计量）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;准入是否强制？&lt;/strong&gt;（缺少拓扑声明能否进入队列/能否被准入）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;启动是否 fail-fast？&lt;/strong&gt;（NCCL/RDMA/NVLink/NUMA 校验）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;验收指标是什么？&lt;/strong&gt;（训练 step time 抖动、推理尾延迟、通信带宽）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;失败如何归因？&lt;/strong&gt;（事件 + 指标 + 日志链路，能快速区分“放错地方”还是“资源不真”）&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="总结"&gt;总结&lt;/h2&gt;
&lt;p&gt;从单卡到多节点，GPU 平台的挑战已从&lt;strong&gt;资源数量&lt;/strong&gt;升级为&lt;strong&gt;资源位置与结构&lt;/strong&gt;。Kubernetes 侧通过 affinity/spread/gang 将“候选落点”转化为可执行约束；Ray 侧用 placement group 明确“组内结构”语义；数据平面则需将拓扑事实与资源单元暴露为可验证信号。唯有如此，才能避免“可调度但不可用”的伪成功，让分布式能力真正成为可交付的 GPU 平台能力。&lt;/p&gt;</content:encoded></item></channel></rss>