推理工程的目的是使生成式AI模型运行得更快、成本更低、更可靠,从而帮助你构建更好的产品。
只有当你的推理工程工作真正落地生产环境,并能够随着成功AI产品所带来的超高速增长和病毒式流量峰值同步扩展时,这一承诺才能得以实现。
当你扩展生产流量时,你的各种假设都会受到严格的考验。从序列形状到流量模式,再到用户决定聊什么话题,一切因素都会影响你在生产环境中观察到的实际性能。而维护安全、健壮的基础设施,与在GPU上优化模型推理完全是两种不同的技能体系。
无论单个实例能以多快的速度和多高的效率为模型提供服务,流量足够大时,服务终将不堪重负。这不是PyTorch的问题,也不是CUDA的问题,而是基础设施的问题,需要不同的思维方式和不同的技术手段。
在生产环境中进行扩展,会带来新的复杂性:如何获取GPU、在哪里获取、如何在多个GPU之间进行流量均衡,以及如何防止服务中断。此外,从按百万token付费过渡到直接为基础设施付费,成本核算也会变得复杂混乱。
生产环境中的延迟不仅仅来自预填充和解码阶段。你需要对整个系统进行端到端的评估,消除服务器、网络乃至客户端(在你能够掌控或影响客户端代码的情况下)中存在的低效环节。
本章将介绍在生产环境中扩展低延迟、高吞吐量推理服务的核心注意事项。在本章末尾,我将邀请你尝试使用Baseten来部署关键任务推理工作负载。
7.1 容器化
容器化是将应用程序与其依赖项打包在一起,以标准化生产环境部署的实践。容器将程序转变为可在任何地方运行的打包工件——不再有”在我的机器上能运行”这种问题。
容器之所以轻量,是因为它们共享底层宿主操作系统内核(在此上下文中,内核指的是 Linux 内核,而非 CUDA 内核)。这使得容器非常适合用于打包推理服务。
对于大多数开发者来说,容器化与 Docker 是同义词。使用容器会涉及一些更具体的术语:
- 容器:一个正在运行的环境,用于隔离应用程序及其依赖项。
- 镜像:一个可执行包,包含运行某个软件所需的一切内容。
- Dockerfile:一个人类可读的文件,包含用于创建镜像的、明确规范且机器可解释的指令。
- 镜像仓库(Registry):用于管理、存储、共享和分发镜像的中央存储库。
NVIDIA、多家云服务提供商以及 Docker 官方都运营着容器镜像仓库。Docker Hub 是 AI 领域流行的镜像仓库——Docker Hub 之于镜像,正如 Hugging Face 之于模型权重,或 PyPI 之于 Python 包。
Docker 容器由多个层组成。你可以基于一个预配置的基础镜像,在其上添加包含额外文件系统变更的其他层。
层共分三种类型:
- 基础镜像层:可以是 Ubuntu 等操作系统发行版,也可以是从镜像仓库获取的更复杂的镜像。基础镜像本身也由多个层组成。
- 附加层:由 Dockerfile 指令指定的文件系统变更,包括依赖项、应用程序代码和配置文件。
- 容器层:在运行时创建的薄型临时层。对运行中容器的任何更改(如创建、更新或删除文件)都会写入该层,并在容器终止时丢失。
vLLM 和 SGLang 等推理引擎为其活跃版本提供了官方基础镜像。通常情况下,建议从这些经过验证的镜像开始构建,而不是从头构建自己的镜像。
7.1.1 依赖管理
推理的依赖链既长又脆弱。要成功完成构建是非常困难的,这使得容器化对于在一个破坏性变更极为常见的生态系统中保留已知可用构建至关重要。
镜像是针对特定的 GPU 架构和模型构建的。一个容器包含许多运行时组件:
-
CUDA 工具包版本:与您的其余技术栈兼容的 CUDA、cuDNN 和驱动程序的特定版本。
-
Python 包:如 torch、transformers 和 diffusers 等依赖项。
-
推理引擎:所使用的 vLLM、SGLang、TensorRT-LLM 或任何其他推理引擎的版本。
-
系统包:如 ffmpeg 等 Linux 包,在处理音频、图像或视频模型时尤为常见。
就像背包旅行中的徒步者一样,您需要轻装上阵。为推理构建的镜像通常有数十 GB。为了实现快速部署和高效运行,请仅包含严格必要的依赖项。
另一个最佳实践是锁定版本。拥有一个已锁定的依赖树可以使系统运行时行为在不同环境中保持一致,并能以相同的结果重复构建镜像。明确指定镜像中应包含的每个依赖项的确切版本。
uv、poetry 或 pip 等工具在构建镜像时会标记任何版本不兼容问题并抛出错误。通过锁定版本,一旦镜像成功构建一次,它将始终将依赖项解析为相同的版本,从而保护您免受破坏性变更的影响。
在使用新发布的模型时,破坏性变更尤为常见。当 DeepSeek 等新版本模型发布时,整个推理生态系统都在争相提供零日支持。
在为全新模型构建镜像时,推理工程师通常依赖夜间构建版本或其他开发者预发布版本的依赖项,而非稳定版本。这些早期版本更容易出现错误,通常需要在模型发布后的数天乃至数周内在稳定版本上进行重新构建。
7.1.2 NIMs
NVIDIA推理微服务(NIMs)是针对热门开源模型预构建的Docker容器。
容器使推理服务的实现具有可移植性。NVIDIA创建了两种类型的NIM:
-
多LLM NIM:一种灵活的容器,可在受支持的GPU架构上运行一系列模型。
-
特定LLM NIM:针对特定GPU上的特定模型进行优化的引擎,配置为最大性能。
NIM适用于模型、GPU架构、GPU数量和配置的各种常见组合。
NIM与其他容器一样,您可以将NIM作为构建的起点、学习的参考架构,或开箱即用的推理服务。
但是,如果您追求的是最大程度的控制权,而非使用现成的配置,那么通常最好从一个限制较少的基础镜像构建自己的容器,而不是对NIM进行改造。
7.2 自动扩缩容
自动扩缩容的目标是确保您始终拥有足够的资源来处理所有传入请求,同时在维持延迟 SLA 的前提下,避免在闲置 GPU 上浪费资金。
自动扩缩容系统使用 Kubernetes(一个开源容器编排系统),以及用于供应和释放计算资源的集群级系统。Kubernetes 可以运行一个模型容器的一个或多个副本,每个副本运行在独立的实例上。一个实例包含容器所需的 GPU 和其他硬件资源。
Kubernetes 通过将一组硬件资源组合成一个集群来运行。该集群包含两类组件:
-
控制平面:负责做出路由和扩缩容决策。
-
工作平面:运行实际的容器化应用程序。
一个 Kubernetes 集群可以运行多个模型的多个副本。但如何决定每个模型应运行多少个副本?除非您的流量异常稳定,否则可能不存在一个能完美匹配您需求的固定副本数量。
自动扩缩容是在集群中动态调整分配给指定模型的副本数量的实践。做出自动扩缩容决策有两种方式:
-
利用率:根据 GPU 利用率信号(如内存使用率或计算使用率)进行扩缩容。
-
流量:根据系统中正在处理的请求数量进行扩缩容。
利用率和流量并不总是一致。例如,在大语言模型的预填充阶段,少数包含数十万个未缓存输入 token 的请求,可能比大量缓存命中率高的小请求产生更高的利用率。
基于流量的扩缩容决策可以主动做出,而利用率则是一个滞后指标。将两者结合使用,以保持系统资源与需求相匹配。
在设计基于流量的自动扩缩容系统时,您需要配置以下五个因素:
-
最小副本数:无论流量如何,始终保持运行的最小副本数量是多少?
-
最大副本数:在流量高峰时,您可以分配的最大副本数量是多少?
-
自动扩缩容窗口:用于衡量流量并做出自动扩缩容决策的滑动时间窗口有多长?
-
缩容延迟:在建议缩容后,您需要等待多长时间,以防再次出现流量峰值?
-
并发目标:每个副本可以同时处理多少个请求?
具体配置决定了自动扩缩容系统在维持延迟 SLA 同时避免资源浪费方面的表现。例如,增大缩容延迟可以防止针对突发性流量过早缩容,但在流量真正平息后可能导致不必要的资源开销。
7.2.1 并发与批处理大小
要正确运行基于流量的自动扩缩容系统,您需要深入了解每个实例能够处理多少并发流量。大多数模型推理服务可以通过批处理同时处理多个请求。批处理有以下几种方式:
-
静态批处理:服务器等待批次填满后再开始推理。
-
动态批处理:服务器等待批次填满或经过配置的时间后再开始推理。
-
连续批处理:服务器持续运行推理,当槽位可用时不断替换新请求。
vLLM、SGLang 和 TensorRT-LLM 等推理引擎实现了强大的连续批处理(TensorRT-LLM 将其称为在途批处理),请求在词元级别进行批处理。与静态批处理相比,这种方式能最大限度地降低延迟。
批处理大小需要在延迟和吞吐量之间进行权衡。增大批次大小会提高整体吞吐量,但每个用户的延迟会变差。请在多种批次大小下测试性能,以找到适合您的模型、实例、延迟目标和预算的最佳配置。
这通过自动扩缩容配置层面的并发目标以及副本层面的批次大小来控制,两者应保持一致。
一旦每个活跃副本都达到其最大并发量,自动扩缩容系统就会启动更多副本。如果有足够多的副本正在处理未填满的批次,则表示需要缩容了。
7.2.2 冷启动
冷启动是指启动模型新副本所需的时间。自动扩缩容系统的整体性能取决于其冷启动速度。如果无法快速启动副本,就很难放心地缩容,从而导致资源过度供应。
影响冷启动时间的因素有以下几个:
-
GPU 采购:向集群添加必要的 GPU 并将其分配给模型的速度有多快?
-
镜像加载:将容器镜像加载到新采购实例上的速度有多快?
-
模型加载:将模型权重加载到容器中的速度有多快?
-
引擎启动:启动推理引擎(包括任何编译时间)的速度有多快?
上述每个因素都需要单独优化。
除非你拥有一个可在多个模型之间灵活切换的预热节点池,否则 GPU 采购速度在很大程度上取决于你的云服务提供商。7.3.1 节介绍了 GPU 的采购问题,节点启动时间是合同中可协商的因素之一。
然而,工程师在镜像和权重加载以及容器和引擎启动方面可以做很多优化。
镜像和模型权重的加载速度取决于你向实例写入数据(通常是数百 GB)的速度。加快镜像和权重加载速度有两种方法:缩小体积或增加带宽。
只包含必要的步骤和依赖项可以使镜像更小、构建容器更快,而对模型权重进行量化还有一个额外的好处——可以加快冷启动时的加载速度。
对于小型模型,以前的策略是将权重直接打包到镜像中,以简化缓存和加载流程。然而,如今大多数模型拥有数百亿甚至数千亿参数,权重远超镜像本身的大小,因此最好将其单独加载。
权重的加载来源对带宽有重大影响。如果从 Hugging Face 等第三方加载,速度受限于其出口速度。而将权重存储在 S3 存储桶中则会引入网络延迟和数据传输成本。
对于加载数千亿参数级别的模型,你需要每秒数 GB 的带宽。实现这一点的最佳方式是在同一数据中心内,通过网络从物理上靠近 GPU 实例的缓存源进行加载。
vLLM 和 SGLang 等推理引擎启动速度很快。但 TensorRT-LLM 以及使用 PyTorch 的优化模型则需要一个编译步骤,该步骤会针对特定硬件资源来构建模型推理引擎,编译过程通常需要数分钟。
在这种情况下,缓存已构建的引擎可以大幅缩短冷启动时间。TensorRT-LLM 和 PyTorch 都提供了实现这一功能的镜像缓存机制。不过,你始终需要将缓存引擎加载到与构建该引擎时完全相同的环境中——即相同的 GPU 类型、CUDA 版本和软件依赖项,才能确保其正常运行。
7.2.3 路由、负载均衡与队列
一旦有多个副本上线,系统就需要决定将哪些请求发送到哪些副本。负责做出这些决策的组件有两种类型:
-
路由器:路由器在请求层面工作,负责确定发送给定请求的最佳位置。路由器回答的是”这个请求应该去哪里?”
-
负载均衡器:负载均衡器在系统层面工作,负责在多个选项之间均匀分配请求。负载均衡器回答的是”这个请求可以去哪里?”
在复杂系统中,并非只有一个路由器和一个负载均衡器。路由在整个技术栈中发生,负载均衡器被注入到关键节点,以保持系统整体性能的稳定。
总体而言,你希望在各副本之间均等地分配负载。然而,路由和负载均衡并不像说”我有3个副本和12个请求,所以让每个副本处理4个请求”那么简单。
每个请求可能包含不同数量的输入token。如果大多数请求有100个输入token,而某个请求有10,000个,则会使简单系统失去平衡。某些请求也更适合由特定副本处理。示例包括:
-
KV缓存感知路由:将请求定向到其KV缓存中已有匹配前缀的副本。
-
LoRA感知路由:将请求定向到内存中已加载所需LoRA微调权重的副本。
智能请求路由利用推理引擎及NVIDIA Dynamo等编排器的信息,根据序列长度、前缀和LoRA需求来路由请求。
仅有负载均衡和路由还不够。当自动扩缩容系统接收到超过其处理能力的流量时,需要一种方式在扩展更多资源或等待现有资源可用时暂存请求。
队列是处理这种情况的基础设施原语。标准队列是一种针对超量请求的先进先出系统,当然你也可以实现更复杂的形式,例如优先级队列,以便在高流量场景中让付费用户优先于免费用户。
当新副本上线时,确保队列能感知到它们,请求不会继续等待已有副本。每个新副本一旦激活,应立即被分配不超过其并发上限的排队流量。
7.2.4 缩容至零
高级自动扩缩容系统实现了一种”缩容至零”的机制,即当没有流量时,系统可以将活跃副本数缩减至零,然后在收到流量时自动扩容。
缩容至零依赖于两个前提条件:
-
快速冷启动:由于用户可能正在实时等待,冷启动必须尽可能快速。
-
健壮的请求队列:系统需要能够在副本启动期间,将传入的请求保持在队列中。
即使具备这些能力,缩容至零也并非适用于所有工作负载。缩容至零非常适合开发阶段,此时测试具有突发性,且第一次请求的延迟并不重要。在生产环境中,缩容至零适用于那些只会周期性获得流量的应用程序,例如仅在某个国家工作时间内被访问的智能体,或专为每日批处理作业设计的离线系统。
然而,如果你依赖缩容至零来降低一个对延迟敏感、流量少且不规律的应用的成本,这很可能意味着你的 AI 应用尚未准备好使用专用基础设施,应该继续使用按 Token 计费的 API,直到达到更大的规模。
7.2.5 独立组件扩展
AI 应用程序越来越多地构建在多模型、多阶段的复合 AI 工作负载之上,推理工程师需要协调多个步骤来完成单个请求。
这些步骤可能具有不同的硬件需求。语音活动检测模型所需的 GPU 性能远低于其为之分块数据的转录模型,而处理该转录文本的 LLM 可能需要配备多个 GPU 的完整节点。此外,流水线中每个步骤的扩展参数也各不相同。
对于这些流水线,您需要分解自动扩展决策,并对每个步骤单独进行扩展,以便为每个步骤合理配置资源,同时避免瓶颈和过度供给。
但是,流水线中的每个模型都应在同一集群中运行。如果在集群内发送消息需要 10 毫秒,而在集群间发送消息需要 50 毫秒,那么在一个 5 步骤的流水线中,40 毫秒的差异将占 1 秒延迟 SLA 的 20%。
7.3 多云容量管理
单个集群内的自动扩缩容有其局限性。但是,为全球用户群提供服务的大规模部署需要分布在世界各地的数千个 GPU。
将多云推理构建为跨不同云提供商的一组孤立计算资源,这一做法相对简单。但在这种设置中,无法灵活地使用跨云计算资源,而且在云之间迁移工作负载是一个繁琐且容易出错的过程。
真正的多云推理需要构建一个多区域、多提供商的装箱工具,将不同的计算资源池视为可相互替代的资源。就像单个集群中的 Kubernetes 一样,多云容量管理必须具备全局视野,实现自我修复和全局调度。
运行真正的多云推理可带来以下优势:
-
容量:汇聚多个提供商的容量,实现更大且更灵活的 GPU 访问。
-
冗余:将推理任务分散到多个提供商,以提升对故障的抵御能力。
-
延迟:在靠近终端用户的位置运行推理,以降低网络延迟开销。
-
合规性:在符合数据主权及其他监管要求的条件下运行推理。
从单个云中的一个集群扩展到多个云中的多个集群,需要一个新的协调层。多云架构包含:
-
控制平面:负责模型部署和全局扩缩容决策,接收实时事件流。
-
工作负载平面:负责处理直接推理流量和集群内扩缩容决策,上报资源利用率和需求情况。
这种职责分离确保了各个工作负载平面能够独立处理流量。如果控制平面或某个工作负载平面发生故障,其他工作负载应不受影响。
7.3.1 GPU 采购
目前有相当数量的公司提供 GPU 访问服务。主要分为三大类:
- 超大规模云服务商:如 AWS 或 GCP 等大型云服务提供商。
- 新兴云服务商:如 Coreweave 或 Nebius 等以 GPU 为核心的云平台。
- 转售商:如 SF Compute Company 等二级市场。
该领域的参与者在容量、可用性和可靠性方面各有差异。超大规模云服务商通常收费较高,而在所有提供商中,成本与正常运行时间 SLA、技术支持、区域可用性、实例配置和集群规模等因素之间存在权衡取舍。
首要挑战是确保获得足够的资源。获取所需的 GPU 往往并非易事,尤其是最新硬件。大型集群同样难以获得,能够提供数百个节点的服务商相对较少。
许多云服务提供商将大部分热门 GPU 以长期预留的方式分配给其最大客户。您可能需要与多家云服务提供商合作,才能在合适的地区获得所需的 GPU。
云端 GPU 可通过三种不同方式采购:
- 预留:以折扣价预留数百或数千个 GPU,预留期为数月或数年。
- 按需:根据需要随时获取单个实例,受配额限制,按小时计费,费用相对较高。
- 竞价:可被抢占的折扣按需实例,通常会提前数分钟通知。
大规模推理通常综合使用多种 GPU 资源,以低成本预留实例为基础,并混合使用按需和竞价实例来应对流量峰值。这些 GPU 分布在全球多个集群中,以便更好地服务于终端用户。
7.3.2 地理感知负载均衡
成功的 AI 应用拥有遍布全球的用户。正如单个集群需要负载均衡器来确保集群中每个 GPU 接收到适量的流量一样,多集群系统也需要一个全局负载均衡器。
你不希望在其他地方还有空闲容量时,某个用户请求却在队列中等待,但同样也不希望习惯性地将来自新加坡的请求发送到旧金山的服务器上。
根据经验,一个请求穿越一个时区大约需要五毫秒。因此,将数据从纽约发送到旧金山单程需要十五毫秒。鉴于延迟预算如此有限,尽可能将工作负载运行在靠近终端用户的地方至关重要。
7.3.3 构建可靠性
GPU 在生产环境中以高故障率而臭名昭著。每一位经历过大规模训练任务的工程师都知道,他们需要考虑到硬件最终会发生故障的情况。
例如,在 Llama 3 的论文中,Grattafiori 及其同事披露,在使用 16,000 块 GPU 运行 54 天的过程中,Llama 团队遭遇了 419 次意外中断,主要原因是硬件故障。这相当于大约每 50,000 GPU 小时发生一次故障。50,000 小时听起来似乎很长,但将一个拥有八块 GPU 的单节点用于推理整整一年,就超过了 70,000 GPU 小时。推理工程师应当预料到硬件故障的发生。
GPU 健康状况是节点级别的问题。当单块 GPU 发生故障时,同一节点上的其他 GPU 往往随之出现故障,或需要下线进行维护。主动记录故障、隔离节点并循环重启 Pod,有助于保持各个集群的健康状态。
GPU 故障并不是导致推理服务中断的唯一原因。云服务提供商有计划内维护,也会出现计划外停机。基础设施的每一层都必须得到加固,以提供高可靠性。
多云推理带来了两种实现高可靠性的新方法:
-
主主模式(Active-active):一种高可用架构,多个区域或集群同时主动承载实时流量。若任意一个平面发生故障,流量将无缝地在其他平面上继续处理。
-
主备模式(Active-passive):一种故障转移架构,保留一个”热备用”集群或区域处于就绪但空闲状态。若主用平面发生故障,流量将切换至备用平面。
当单个集群、区域或云服务提供商发生宕机时,无缝切换至另一个工作负载平面,可保持高可靠性并降低延迟。
7.3.4 安全与合规
云基础设施在安全与合规部门已是热议话题超过二十年。要使AI模型能够驱动关键任务应用,推理必须同时满足安全性与合规性要求。安全与合规方面的讨论通常围绕以下三个领域展开:
-
用户数据:安全与合规部门希望确保所有数据(包括用户输入和模型输出)均受到保护。
-
模型权重:对于拥有经过微调或专有模型的公司而言,模型权重是无价的商业机密。
-
基础设施:GPU本身以及对智能服务的访问权限,都是潜在的滥用目标。
提升安全性最简便的决策之一,就是直接不存储用户输入或模型输出。这可能并非总是可行——你或许有日志记录要求,或有用户协议需要保留使用数据以用于未来的模型训练——但如果你无需保留用户数据,便可以缩小攻击面。
保护AI推理工作负载及相关数据,与保护其他容器化工作负载类似。数据加密、容器安全、网络与访问控制,以及工作负载隔离,并辅以广泛的第三方渗透测试验证,仍然是行业黄金标准。
推理工程师越来越需要支持在受监管行业和合规要求严格地区运行的应用。多云基础设施的一个优势在于:为使你的应用符合SOC 2 Type II等认证或HIPAA等法规,你的云服务提供商通常也必须满足相应的合规要求。在这种情况下,能够将工作负载迁移至合规的提供商就显得尤为重要。
多集群基础设施的另一个优势是可以在多个地区运行同一模型。某些行业和国家有数据驻留要求,规定来自本国的用户数据不得在其他国家的服务器上处理。
例如,在多伦多附近的提供商部署一个集群,在纽约附近的提供商部署另一个集群,既可以确保加拿大数据留存于加拿大境内、美国数据留存于美国境内,又能为整个地理区域的用户提供最低限度的延迟开销。
7.4 测试与部署
除了在配置推理引擎时进行副本级别的测试和基准测试之外,在部署前进行端到端的系统测试同样至关重要。
推理测试有以下几种策略:
-
手动测试:编写脚本(或点击按钮)向推理服务发送模拟流量。
-
负载测试:自动发送大量流量,以测试系统的扩展能力和性能维持能力。
-
影子流量:将线上流量复制到测试部署环境中,以衡量在真实场景下的性能表现。
推理服务的测试成本较高。配置测试和衡量结果需要耗费工程时间,运行测试流量的推理也需要占用 GPU 资源。在一定程度上,这只是业务运营的必要成本,但应仔细考虑如何将测试开销降至最低。例如,影子流量测试可以从复制一部分随机生产流量开始,然后再进行较短时长的负载测试。
在测试时,请注意 AI 产品的使用量通常会呈现出每日和每周的周期性波动。
一旦您对更新后的推理系统的性能和稳定性感到满意,就可以将其部署到生产环境中了。
7.4.1 零停机部署
推理工程师使用高可用部署策略来避免停机。
一种传统的高可用设计是蓝绿部署。在这种设置中,存在两个完全相同的环境:原有的蓝色部署,以及运行更新后服务的新绿色部署。一旦绿色环境准备就绪,全部流量就会从蓝色环境切换到绿色环境,而蓝色环境仍保留,以便在出现问题时回滚。
然而,蓝绿部署并不适合大规模推理工作负载,因为它会遭遇与大规模测试相同的 GPU 容量和成本问题。如果蓝色部署正在使用 100 块 GPU,那么在流量切换之前,绿色部署也需要额外的 100 块 GPU。
因此,推理工程师通常会采用金丝雀部署,以更低的 GPU 开销获得类似收益。金丝雀部署这一名称源自矿井中用于探测有毒气体的金丝雀,它的作用是在问题影响大量用户之前先捕获错误。
金丝雀部署通常包含四个步骤:
- 构建一个新的推理服务部署,并使其准备好接收请求。
- 将一小部分线上实时流量导向新服务。
- 监控新服务,确认其能够正确处理流量;一旦出现问题则回滚。
- 在持续监控的同时逐步增加流量,直到新部署承接 100% 的流量。
这种金丝雀部署既可以在几分钟内快速完成流量爬坡,也可以缓慢推进,以确保每个阶段都足够稳定。借助自动扩缩容,金丝雀部署在大规模场景下不会显著增加成本,因为当生产系统所承载的流量减少时,它会相应缩减部分副本。
在启用自动扩缩容的情况下,新部署在没有流量时通常只保留最小副本数。在整个金丝雀部署过程中,必须确保新部署拥有足够的活跃副本来正确处理请求。否则,请求会在自动扩缩容完成前排队,用户将看到明显的延迟尖峰。
7.4.2 成本估算
从使用公共 API 消耗令牌转向在专用 GPU 上自行推理,需要改变对成本的思考方式。公共 API 的成本很简单:每百万令牌的价格乘以使用的令牌数量。有几个变量——输入令牌的缓存命中与缓存未命中、高用量用户的折扣——但成本仍然是使用量的线性函数。
投入时间和精力进行推理工程的一个动机是掌控单位经济效益,摆脱按令牌计费的定价方式。但这是一个困难的思维转变。
专用推理的优缺点并存:成本现在取决于许多变量。这一点有利,因为它给了你控制权,但也使估算变得困难。影响成本的因素包括:
-
批处理大小:部署是针对低批处理量的低延迟进行优化,还是针对高批处理量的高吞吐量进行优化?
-
流量模式:流量是否持续使活跃 GPU 满负荷运行,还是存在空闲产能?
-
序列长度:请求的平均和极端情况下分别有多少输入和输出令牌?
鉴于这种复杂性以及输入和输出令牌之间的成本差异,通常更有效的做法是将令牌价格转换为总成本,并与专用部署进行比较,而不是试图从 GPU 费用中反推出每令牌价格。
成本估算应采用较长的时间跨度,理想情况下至少一周,以平滑使用量的波动。
在专用部署中需要考虑的另一个因素是构建和维护推理系统所花费的工程时间成本。这项投入虽然在提高可靠性、安全性和可控性方面是合理的,但应将其加入 GPU 成本中,以形成对推理总拥有成本(TCO)的完整认识。
7.4.3 可观测性
推理是关键任务,因此必须像应用程序中任何其他关键任务组件一样对其进行监控,并在适当的抽象层级上构建告警、日志和可观测性。
首先要考虑的问题是监控什么。推理可观测性包括以下度量指标:
-
总流量:模型部署接收的请求数量。
-
请求和响应大小:正在处理的请求的输入和输出序列长度。
-
响应代码:模型服务器发出的 2XX、4XX 和 5XX 响应代码的计数。
-
延迟:以 P50、P90 和 P99 为基准的首个令牌时间、每秒令牌数以及端到端延迟等指标。
-
副本数量:当前正在处理流量的实例数量,以及正在启动的实例数量(如有)。
-
利用率:CPU、主机内存、GPU 和 GPU 内存的利用率。
-
队列深度:对于具有异步流量的系统,已入队等待处理的请求数量。
这些指标是相互关联的。延迟峰值可能来源于请求量,也可能来源于较长的输入序列。将这些指标综合来看,可以让推理工程师不仅了解正在发生什么,还能明白其背后的原因。
当出现问题时,推理工程师需要信息来解决问题。服务器日志和显示推理服务变更的审计日志,都能实时提供这些信息。
可观测性不能形成孤岛。在为推理构建可观测性时,应与现有的可观测性和告警工具深度集成——如 Grafana、Datadog、PagerDuty、Sentry——以便将推理信息与应用程序的其余部分结合起来进行综合分析。
7.5 客户端代码
推理工程涉及许多技术,从 CUDA 到 Kubernetes。但在优化延迟和构建规模化系统时,有一个关键领域往往被忽视:客户端代码。
调用推理服务涉及两个层面:
-
客户端:向推理引擎发出请求的浏览器、智能体或应用程序。
-
服务端:处理客户端请求并返回模型结果的推理服务。
客户端代码的行业标准是 OpenAI SDK,它除支持 OpenAI 自有模型外,还兼容众多其他提供商。LangChain、Vercel AI SDK、LiteLLM、LlamaIndex 等流行的 AI 工程框架和库也可作为客户端使用。
无论是使用现有库还是自行编写代码,都可能存在延迟开销或吞吐量瓶颈。对于实时应用,可能还需要使用 HTTP 以外的协议,如 WebSockets,以维持持续连接。
7.5.1 客户端延迟开销
根据客户端的网络连接情况和所使用的协议,在客户端与服务端之间建立会话需要数十毫秒。
在 P95 端到端延迟 SLA 为 300 毫秒的高性能系统中,TLS 握手在推理开始之前就至少消耗了该延迟预算的 10%。来自同一客户端的后续请求应通过复用已有会话来节省时间。
会话复用并不是什么新概念,OpenAI SDK 等工具已在底层默默实现了这一功能。然而,在为非标准模态构建自己的客户端时,应遵循会话复用等最佳实践。
7.5.2 异步推理
某些系统是为吞吐量而非延迟而构建的。批量文档处理和语料库嵌入等使用场景对延迟不敏感,因此切换到异步任务是合理的选择。
异步请求是一种”发送即忘”的推理执行方式。
普通同步推理请求有超时限制,通常为几分钟,超时后请求将失败。异步任务通过立即确认请求并在稍后将结果返回至原始请求中提供的 Webhook 来解决这一问题。
异步任务同样有时间限制,但通常以小时而非分钟为单位。结合强大的服务端队列机制,异步请求使高吞吐量、延迟不敏感的系统更加健壮和高效。
7.5.3 流式传输与协议支持
流式传输让应用程序感觉即时响应。对于语言模型,通过 HTTP 进行流式文本输出已经足够。但对于其他模态,尤其是实时语音和视频,输入和输出流都需要能够承载更多数据。
两种最常见的双向流式客户端-服务端连接协议为:
-
WebSockets:适用于不需要严格模式约束的流式传输场景。
-
gRPC:适用于定义明确的服务间通信。
WebSockets 适用于传输非结构化实时数据(如音频),服务端接收请求后可对其进行解析并在下游进行处理。使用 WebSockets 时,服务端可支持的客户端数量上限由开发者配置;达到该并发上限后,新连接将无法建立,必须等待空闲槽位或新副本扩容。
与 WebSockets 类似,gRPC 支持双向流式传输,但面向结构化数据。通过 gRPC 传输的请求必须遵循预定义的模式,从而省去了解析输入的负担。这一额外的验证层使 gRPC 比 WebSockets 略慢。
7.6 基于 Baseten 的生产推理
本书汇集了我在 Baseten 工作四年间所学到的关于推理的一切知识。
Baseten 是一家成立于 2019 年的 AI 基础设施公司。在 Baseten,我们专注于为开源模型和自定义模型提供高性能、高可用的推理服务。我们还提供预训练、后训练和强化学习平台。
我们为全球增长最快的初创企业和最具创新力的企业提供推理服务,包括 Cursor、World Labs、Notion、OpenEvidence、Clay、Abridge、Gamma、Ambience、Writer 等数百家公司。
在 Baseten,我们专注于四大核心支柱,以提供最快速的关键任务推理:
-
性能:由 Baseten 推理栈驱动,在大规模场景下保持稳定的低延迟。
-
基础设施:可靠的多云部署、快速精细的自动扩缩容以及强大的安全保障。
-
工具链:直观高效的开发者体验,涵盖日志记录、可观测性和程序化访问。
-
专业支持:由前线部署工程师提供亲身实践的实施与协助。
我们荣幸地为您的 AI 驱动产品提供快速、可靠的推理服务。
此外,我们正在持续招募工程、销售、市场营销和运营等各职能岗位的人才。如需了解更多信息,请访问 Baseten 招聘页。