最近我们团队在做一个基于国产大模型的AI服务,需要支撑高并发、高可用的在线推理。

大模型服务和传统的Web服务不一样,它的计算量大、延迟高、资源消耗大,架构设计上有很多特殊的挑战。我们调研了很多方案,也踩了不少坑,最终搭了一套比较稳定的架构。这篇文章,我想分享一下国产大模型服务的架构设计实践,从模型部署到请求调度,从高可用到高并发,聊聊我们的经验。

大模型服务的特点

在说架构之前,先说说大模型服务的特点,这决定了架构设计的方向。

第一个特点是,计算密集型。大模型推理需要大量的GPU计算,一个请求可能需要几秒甚至几十秒的计算时间。这和传统的Web服务不一样,传统Web服务大部分时间在等IO,而大模型服务大部分时间在计算。

第二个特点是,显存占用大。大模型的参数很多,一个7B的模型,量化之后也要十几GB显存;70B的模型,需要多张GPU卡才能放下。显存是大模型服务最宝贵的资源,也是瓶颈。

第三个特点是,延迟高。大模型生成一个token需要一次前向计算,生成几百个token就需要几百次计算。所以,大模型的响应时间通常是秒级的,比传统服务慢很多。

第四个特点是,输入输出长度不确定。用户的输入可能很短,也可能很长(比如长文档问答);输出的长度也不确定,可能几个字,也可能几千字。这导致每个请求的计算量差异很大,调度起来比较复杂。

第五个特点是,并发能力有限。单张GPU卡能同时处理的请求数是有限的,受限于显存和计算能力。要支撑高并发,需要大量的GPU卡,以及高效的调度机制。

第六个特点是,模型更新频繁。大模型迭代很快,经常要更新模型版本、微调模型。架构需要支持模型的热更新,不影响线上服务。

这些特点,决定了大模型服务的架构不能照搬传统Web服务的架构,需要专门设计。

整体架构

我们的整体架构,分为几层:

第一层是,接入层。负责接收用户请求,做鉴权、限流、路由。用Nginx或者API网关,把请求转发到后面的推理服务。接入层是无状态的,可以水平扩展。

第二层是,调度层。负责任务调度,把请求分配给合适的推理节点。调度层要考虑每个节点的负载、当前的队列长度、请求的优先级、模型的版本等。这是大模型服务的核心,直接影响并发能力和响应时间。

第三层是,推理层。负责实际的模型推理。每个推理节点上部署了模型,接收调度层分配的请求,执行推理,返回结果。推理节点是有状态的,因为模型加载在显存里。

第四层是,模型管理层。负责模型的存储、版本管理、热更新。模型文件存在对象存储里,推理节点启动的时候从对象存储拉取模型。更新模型的时候,不需要停机,逐个节点滚动更新。

第五层是,监控和运维层。负责监控整个系统的状态,包括GPU利用率、显存使用、请求量、延迟、错误率等。还要有告警机制,出问题及时通知。

整体架构是微服务架构,每个组件都可以独立扩展。核心是调度层和推理层,下面详细说。

模型部署

模型部署是大模型服务的基础。国产大模型的部署,有几个关键问题。

第一个问题是,模型量化。大模型的参数量很大,直接部署显存不够,也不经济。所以,需要对模型进行量化,减少显存占用。

常见的量化方式有:

  • FP16/BF16:半精度,精度损失小,但显存占用还是比较大。
  • INT8:8位整数,显存占用减半,精度损失较小。
  • INT4:4位整数,显存占用是FP16的四分之一,精度有一定损失,但大部分场景够用。
  • AWQ/GPTQ:更先进的量化算法,在低比特下保持更好的精度。

我们的实践是,7B模型用INT4量化,一张A10或者4090就能跑,性价比高。70B模型用INT8量化,需要多张卡张量并行。对于精度要求高的场景,用FP16或者BF16。

第二个问题是,推理框架。国产大模型的推理,常用的框架有:

  • vLLM:开源的高性能推理框架,支持连续批处理(Continuous Batching)和PagedAttention,吞吐量很高。
  • TensorRT-LLM:NVIDIA的推理框架,性能很好,但部署相对复杂。
  • Text Generation Inference(TGI):Hugging Face的推理框架,功能完善。
  • 国产框架:比如PaddleNLP、MindSpore,对国产硬件支持好。

我们主要用vLLM,因为它开源、性能好、社区活跃。vLLM的连续批处理和PagedAttention,能大大提升GPU的利用率和吞吐量。

第三个问题是,张量并行和流水线并行。对于大模型(比如70B),一张卡放不下,需要多张卡并行。

  • 张量并行(Tensor Parallelism):把模型的参数切分到多张卡上,每张卡存一部分,计算的时候互相通信。适合单节点内的多卡。
  • 流水线并行(Pipeline Parallelism):把模型的层切分到多张卡上,第一张卡算前几层,第二张卡算后面几层。适合跨节点的大模型。

我们的70B模型,用8张A100做张量并行,放在一个节点上。推理延迟和吞吐量都能接受。

第四个问题是,国产硬件适配。如果用国产GPU(比如华为昇腾、寒武纪、海光),需要用对应的推理框架和模型版本。国产硬件的生态还在发展中,适配的时候可能会遇到一些坑,需要提前做验证。

请求调度

请求调度是大模型服务高并发的关键。

传统的负载均衡(比如轮询、最少连接)在大模型服务里效果不好,因为每个请求的计算量差异很大,而且GPU的状态也很复杂。需要专门的调度策略。

我们的调度层,主要做了几件事:

第一件事是,连续批处理(Continuous Batching)。这是vLLM引入的技术,也是大模型推理提升吞吐量的关键。

传统的批处理是,一个批次的请求一起开始、一起结束。如果一个请求输出很长,其他请求就要等它,GPU利用率低。连续批处理是,请求可以随时加入批次,生成完一个token就离开批次,新的请求可以补进来。这样GPU一直在工作,利用率大大提高。

vLLM已经内置了连续批处理,我们在调度层配合它,控制批次的大小和请求的加入。

第二件事是,请求优先级。不同的请求有不同的优先级。比如,付费用户的请求优先级高,免费用户的优先级低;短请求优先级高,长请求优先级低。

我们的调度器会根据优先级来决定请求的处理顺序。高优先级的请求优先加入批次,低优先级的请求在队列里等待。这样,能保证重要用户的体验,同时也能处理低优先级的请求。

第三件事是,队列管理。每个推理节点前面都有一个请求队列。调度器要根据每个节点的队列长度、GPU利用率、显存使用,决定把请求发给哪个节点。

我们的调度策略是,优先发给队列最短、GPU利用率最低的节点。同时,要避免把太多长请求发给同一个节点,导致那个节点的队列被长请求占满。

第四件事是,超时和重试。大模型推理可能会超时,比如请求太长、GPU出问题。调度器要设置超时时间,超时的请求重新调度,或者返回错误。还要有重试机制,失败的请求自动重试几次。

第五件事是,流式输出。大模型的输出是流式的,一个token一个token地生成。调度层要支持流式转发,把推理节点生成的token实时转发给用户,不需要等全部生成完。这样用户的等待时间短,体验好。

高可用设计

大模型服务的高可用,和传统服务类似,但也有特殊之处。

第一个是,多节点部署。每个模型至少部署两个推理节点,分布在不同的服务器上。一个节点出问题,调度器自动把请求切到其他节点。

第二个是,健康检查。调度器定期检查推理节点的健康状态,包括GPU是否正常、模型是否加载、推理是否能正常响应。不健康的节点自动从调度列表中摘除,恢复后再加回来。

第三个是,模型多版本。同时部署多个版本的模型,比如稳定版和测试版。新版本先灰度发布,给一小部分流量,验证没有问题再全量。出了问题,快速切回旧版本。

第四个是,降级策略。在系统压力大的时候,自动降级。比如,限制免费用户的请求量,或者降低输出的最大长度,或者用更小的模型处理简单请求。保证核心用户和核心功能的可用性。

第五个是,故障恢复。推理节点崩溃之后,自动重启,重新加载模型,加入调度。模型文件存在对象存储里,重启后自动拉取。整个过程不需要人工干预。

第六个是,跨可用区部署。对于重要的服务,跨可用区部署。一个可用区出问题,流量切到其他可用区。不过,大模型服务的跨可用区部署成本很高,因为GPU很贵。我们目前是单可用区多节点,重要服务才跨可用区。

高并发优化

高并发是大模型服务的核心挑战。我们做了几个优化。

第一个优化是,批处理优化。前面说过,连续批处理能大大提升吞吐量。我们还调整了批次的最大大小、最大等待时间等参数,找到吞吐量和延迟的平衡点。

第二个优化是,KV缓存优化。大模型推理的时候,会把之前token的KV(键值)缓存起来,避免重复计算。vLLM的PagedAttention就是优化KV缓存的管理,减少显存碎片,提高显存利用率。

我们还做了前缀缓存。对于有相同前缀的请求(比如相同的系统提示),缓存它们的KV,新请求可以直接复用,不需要重新计算。这在RAG场景下很有用,因为很多请求的系统提示是一样的。

第三个优化是,推测解码(Speculative Decoding)。用一个小模型快速生成候选token,然后用大模型批量验证。这样能加快生成速度,提高吞吐量。这个技术我们在测试中,效果还不错,但部署复杂度高一些。

第四个优化是,请求路由优化。把相似长度的请求路由到同一个节点,避免短请求被长请求阻塞。比如,把短请求发到一个节点集群,长请求发到另一个集群。

第五个优化是,结果缓存。对于相同的输入,缓存输出结果。如果用户的请求和之前的一样,直接返回缓存的结果,不需要重新推理。这在一些场景下(比如常见问题问答)能大大减少计算量。

第六个优化是,弹性扩缩容。根据请求量动态调整推理节点的数量。流量大的时候加节点,流量小的时候减节点。因为GPU很贵,弹性扩缩容能节省成本。不过,GPU节点的启动时间比较长(要加载模型),所以扩缩容的策略要提前预测流量。

成本控制

大模型服务的成本很高,主要是GPU的成本。成本控制很重要。

第一个是,模型选型。不是所有场景都需要最大的模型。简单的任务(比如分类、摘要)用小模型就够了,复杂的任务才用大模型。根据场景选择合适大小的模型,能大大降低成本。

第二个是,量化。前面说过,量化能减少显存占用,让一张卡能跑更大的模型,或者同时跑更多实例。INT4量化在大部分场景下精度损失可接受,但成本能降很多。

第三个是,共享GPU。一张GPU卡可以同时跑多个模型实例,或者多个用户共享。通过显存隔离和调度,提高GPU的利用率。vLLM支持多租户,能在一个实例里服务多个用户。

第四个是,按需使用。非高峰时段减少节点数量,或者用竞价实例(云厂商的闲置GPU,价格便宜很多)。不过竞价实例可能会被回收,适合非核心服务。

第五个是,缓存和降级。前面说的结果缓存、前缀缓存,能减少计算量。降级策略能在高峰期保护系统,避免资源耗尽。

第六个是,模型蒸馏。用大模型的输出训练小模型,小模型在大部分场景下能达到大模型的效果,但成本低很多。对于固定场景,蒸馏是降低成本的有效手段。

监控和运维

大模型服务的监控,比传统服务更复杂。

我们监控的指标包括:

  • 基础设施指标:GPU利用率、显存使用、GPU温度、功耗、CPU、内存、网络。
  • 模型指标:请求量、吞吐量(token/s)、延迟(首字延迟、总延迟)、错误率、队列长度。
  • 业务指标:活跃用户数、调用次数、平均输出长度、用户满意度。

监控数据用Prometheus采集,Grafana展示。设置了告警阈值,比如GPU利用率持续超过90%、延迟超过阈值、错误率上升,都会告警。

运维方面,我们做了:

  • 自动化部署:用Docker和Kubernetes部署,模型更新走CI/CD流水线。
  • 日志收集:所有节点的日志集中收集到ELK,方便排查问题。
  • 链路追踪:用OpenTelemetry做分布式追踪,能看到一个请求经过了哪些组件,每个环节花了多长时间。
  • 故障演练:定期做故障演练,模拟节点崩溃、网络中断、GPU故障,验证系统的容错能力。

一些经验总结

做国产大模型的架构设计,我总结了一些经验。

第一,不要过度设计。大模型服务的架构,能满足当前的需求就好,不要一开始就搞很复杂的分布式架构。先把单节点跑通,然后根据流量和需求逐步扩展。

第二,重视推理框架的选择。推理框架对性能影响很大,vLLM、TensorRT-LLM、TGI各有优劣。要根据自己的模型、硬件、需求做选择,做基准测试,不要盲目跟风。

第三,连续批处理是必须的。不用连续批处理,GPU利用率会很低,成本会很高。vLLM的连续批处理是目前最好的开源实现,建议直接用。

第四,显存是最宝贵的资源。大模型服务的瓶颈通常是显存,不是计算。所有的优化,都要围绕提高显存利用率来做。量化、PagedAttention、前缀缓存,都是为了更高效地使用显存。

第五,高可用和成本要平衡。高可用需要冗余,冗余意味着成本。要根据业务的重要性,找到合适的平衡点。核心服务多冗余,非核心服务少冗余。

第六,国产模型和硬件的适配要提前做。国产大模型和国产GPU的生态还在发展中,适配的时候可能会遇到各种问题。要提前做验证,留足时间。

写在最后

大模型服务的架构设计,是一个很有挑战性的领域。它结合了AI、分布式系统、硬件优化等多个方面,需要综合考虑性能、成本、可用性、可扩展性。

国产大模型的发展很快,从模型到硬件到推理框架,都在快速进步。我们的架构也在不断演进,今天的最佳实践,明天可能就过时了。保持学习,持续优化,才能跟上这个领域的发展。

希望这篇文章能给做国产大模型服务的朋友一些参考。如果你有更好的架构方案或者优化经验,欢迎在评论区交流。