ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

大模型时代云原生架构设计:GPU调度与推理服务全解析

大模型时代云原生架构设计:GPU调度与推理服务全解析 咱们做技术的人最怕的不是需求有多刁钻而是“又要上大模型又要控制成本还要保证稳定”这种三连。我最近大半年基本都泡在大模型相关的云原生架构改造里踩过的坑比吃过的盐都多但也积累了一套实打实能落地的方案。今天就把第11章“大模型时代的云原生架构设计”这块内容掰开了揉碎了讲清楚。如果你正准备把大模型接入现有业务或者刚被老板指派去做一个AI中台再或者只是想搞清楚大模型部署和传统微服务到底差在哪那这篇文章就是给你写的。我会从底层资源调度讲到上层推理服务再到微调任务的编排全部用我们实操过的案例和配置说话不整那些虚头巴脑的理论。1. 大模型为什么正在逼着云原生架构“重新设计”先聊点背景。过去十年我们讲云原生核心是微服务、容器、K8s、DevOps 这一套本质上是把无状态应用拆小、拆细然后弹性伸缩。这套玩法在普通Web业务上极其成熟比如你搞个电商秒杀扛不住就横向扩容Pod加节点、加副本流量下来再缩容非常丝滑。但大模型一来这套逻辑当场失灵。第一座大山是有状态且极重。大模型模型文件动辄几个GB到几十GB光加载到显存里就要占几十GB显存你还想“秒级扩容”压根不可能。Pod调度、镜像拉取、模型加载都是分钟级的事传统HPA那套基于CPU的扩容规则在这里就是个笑话。第二座大山是GPU资源的异构与稀缺。手上有 A100、H100、4090、3090性能差异巨大算力资源池怎么管理显存碎片怎么处理GPU配额怎么划分更别提现在很多团队遇到的是“迭代任务排队、GPU不够用”的常态。热搜里提到的“GPU配额已不够预冻结折合1.33核时”这种场景我做项目时几乎每周都见。第三座大山是推理服务的长连接与高吞吐。传统HTTP接口请求响应是短连接但大模型对话是流式的一个请求可能持续几十秒甚至几分钟中间一直有Token在返回。这对网关、负载均衡、超时控制全是挑战。所以大模型时代的云原生架构绝对不只是“在K8s上跑个模型”这么简单而是要把资源池化、任务调度、服务治理、弹性伸缩这套体系围绕“GPU 大模型”重新设计一遍。换句话说从“无状态应用编排”进化到“有状态算力编排”。1.1 大模型上云原生的三种主流架构形态我自己做项目时发现大模型和云原生结合基本绕不开下面三种架构形态大家可以根据业务成熟度去选形态一裸机 Docker 模型服务。最轻量适合个人开发或者快速验证直接把模型、推理框架如vLLM打包成镜像跑在单机或几台机器上。缺点是没有调度没有资源池纯靠人工分配和抄写命令。形态二K8s GPU调度 推理服务框架。这是目前生产环境的主流形态。把所有GPU节点纳入K8s集群用NVIDIA Device Plugin来感知和分配GPU模型推理服务以Deployment或StatefulSet方式部署配合Ingress和网关做流量接入。形态三智算云平台 / AI中台。在K8s之上再加一层面向算法工程师的平台层提供训练任务提交、数据集管理、模型注册、推理服务发布等完整生命周期管理。这就是企业级“大模型底座”了也是我目前最推荐的终极形态。不同阶段的团队别盲目去搞形态三先评估自己的业务量和工程能力。但你架构设计的思想得是形态三的否则后面升级会很痛苦。1.2 云原生与大模型结合的架构总览我习惯把整套架构分成四层每一层都有独立的技术栈和解决的核心问题各层之间通过API和标准协议衔接基础设施层GPU节点、高速网络RDMA或InfiniBand、分布式存储读模型文件用。这一层要做的是“算力池化”把N张GPU卡抽象成资源池屏蔽异构差异。资源调度层核心是K8s 调度器扩展负责GPU资源分配、任务排队、故障重试。需要做的是让A100和4090能“混部”并且按优先级抢占。服务编排层负责模型推理服务的生命周期管理。包括模型加载、实例扩缩容、灰度发布、流量切分以及vLLM、Triton这类高性能推理框架的接入。应用接入层面向业务开发者的API网关和Serverless形态屏蔽底层基础设施细节。业务方不管你是跑在什么卡上只需要按Token量或被调用的次数计费。这四层每一层展开都是大工程我后面几节会重点拆解最容易出问题的资源调度和推理服务部分。2. 架构设计的核心拆解从资源到服务的全链路说实话大模型云原生化最难的往往不是推理本身而是它周边那一圈“看不见的”基础能力。这节我把四个核心环节逐个拆开讲都是坑。2.1 资源层GPU的抽象、调度与共享咱们先解决最关键的钱的问题也就是GPU怎么管。K8s默认把GPU当成一种资源用nvidia.com/gpu来标记但是注意它只能做到整数卡调度。比如一张A100要么整卡给一个Pod要么不给。这在真实场景下太浪费了因为很多微调任务根本用不满整卡。我常用的解决方案是引入GPU共享和显存切分。具体两种路子时间片共享多个推理任务交替使用同一块GPU。适合对延迟不太敏感的场景比如离线批量处理。显存切分MIG或类似机制把物理GPU切成多个独立实例每个实例有独立的显存和计算单元。比如 A100 40GB可以切成2份20GB跑两个小模型。在K8s侧实现呢通常用nvidia.com/gpu.memory和nvidia.com/gpu.shares这种自定义资源扩展或者直接上第三方Device Plugin比如NVIDIA的官方插件增强版或者开源社区的GPU共享方案。注意GPU共享虽然好但涉及显存隔离和故障域的问题一旦有个任务写越界整个卡上的任务都可能被干翻。生产环境建议优先用MIG或者显存切分时间片共享只跑非关键任务。调度策略上也别只依赖K8s默认调度器。K8s默认调度器是“满足即可”不会考虑全局最优。比如三个大Pod分散在不同的物理机上三台机器都有碎片但你再想塞一个大Pod任何一台都放不下。这种情况需要加拓扑感知调度或者直接用类似coscheduling的插件来实现一个任务的所有Pod要么全部调度成功、要么全部等待。2.2 服务层模型推理的接入、流式输出与生命周期管理这层是业务最容易感知到的地方也是架构设计里我认为最值得花心思的一层。先讲接入。模型推理服务和普通HTTP服务最大的区别是长连接 流式输出。传统HTTP服务网关是短连接模型连接建立、请求、响应、关闭对网关压力不大。但大模型对话是一次请求长时间占用连接比如一个32秒的SSE流。如果你还用传统超时配置默认60秒那对话稍微长一点就会被网关掐断。所以我做Service Mesh或网关层时一定要单独配置大模型路由的策略超时时间拉长读超时建议设置到10分钟以上写超时5分钟以上。连接复用模型服务Pod内部保持与底层推理框架的持久连接不要每次请求都重建gRPC连接否则并发一高就内存爆炸。流式转发确保网关支持text/event-stream的流式代理并且要能实时透传Token内容而不是等全部Token生成完再一次性返回。然后说生命周期管理。推理服务发布新版本比如微调后的新权重时最怕的就是中断在线业务。我一个比较稳妥的做法是蓝绿部署 预热 流量灰度我先把新版本的Deployment建出来Pod起来之后会做一次模型加载加载完成后跑一次健康检查比如调用一次推理接口验证能正常返回。全部就绪后我再通过Ingress或者服务网格的流量权重先切1%流量过去观察错误率和延迟逐步放量到100%。这套流程如果靠人工执行心智负担极重所以必须做成Pipeline。我是用Argo Workflows或KubeFlow的Pipeline来编排“拉模型权重 → 构建镜像 → 滚动发布 → 自动化验证 → 流量切分”。每一步失败都会自动回滚。2.3 数据层与开发层微调、数据集与模型版本管理很多人最容易忽略的是微调流程的云原生化。GPU不能只用来跑推理还得跑微调和训练。所以这套架构要同时兼顾“训练任务”和“推理任务”两类工作负载。训练任务主要是批量、长时、独占GPU。你不可能把一个微调任务和推理服务共享一张显卡。所以K8s调度器的优先级策略要设成“推理服务优先保在线训练任务用空闲资源或排队”。数据集和数据版本管理我强烈建议挂到对象存储上而不是放在本地磁盘。每次微调跑完把模型权重、Checkpoint、评估指标一起存成一个“版本”然后注册到模型仓库比如MLflow或自研模型注册表。后续推理服务要发布时直接指定版本号即可做回滚时也只需要切版本号。数据链路端还有个细节是数据预处理Pipeline。微调之前原始数据往往需要清洗、去重、格式化。这个步骤特别适合丢进K8s里做批量Job按数据切片动态创建Worker处理完了就销毁毫不浪费GPU资源。3. 实操记录一套可落地的大模型云原生部署方案理论说多了容易飘直接上点真刀真枪的实战。下面这套方案我在多个环境验证过从零到上线基本3天内能搞定非常适合作业抄。3.1 基础设施选型K8s、GPU驱动与共享内存配置首先别用太老的K8s版本1.26以上会比较好。要不然GPU调度、设备插件这些兼容性会让人头大。我用的版本是1.28.5。GPU节点的Driver必须匹配好CUDA版本。这里有个容易踩的坑如果你同一个集群里有A100和4090它们的Driver版本和CUDA兼容性可能不一致。最简单的办法是节点打标签区分gpu-typea100和gpu-type4090然后用NodeSelector精确调度。另外推理服务高频访问时模型文件需要缓存到内存或本地NVMe SSD。比如我们用vLLM时模型权重通常要从对象存储拉到本地如果每拉起一个Pod都从远端拉一次几十GB的文件那启动时间会极其感人。我建议集群给每个GPU节点挂一块大容量NVMe盘并加一个LocalPV或hostPath的PV来缓存模型文件。第一次加载后后续Pod直接命中本地缓存启动时间能从10分钟降到1分钟以内。3.2 推理服务的部署vLLM 网关实战我们现在线上最主要的推理引擎是vLLM吞吐量高、显存利用好而且兼容OpenAI的接口协议对业务方极其友好。一个标准的vLLM Deployment模板大概长这样apiVersion: apps/v1 kind: Deployment metadata: name: llama3-8b-inference namespace: ai labels: app: llama3-8b spec: replicas: 2 selector: matchLabels: app: llama3-8b template: metadata: labels: app: llama3-8b spec: nodeSelector: gpu-type: a100 containers: - name: vllm image: vllm/vllm-openai:latest command: [python3, -m, vllm.entrypoints.openai.api_server] args: - --model - /models/llama3-8b-instruct - --served-model-name - llama3-8b - --gpu-memory-utilization - 0.9 - --max-model-len - 8192 - --port - 8000 ports: - containerPort: 8000 resources: limits: nvidia.com/gpu: 1 volumeMounts: - name: model-cache mountPath: /models readinessProbe: httpGet: path: /health port: 8000 initialDelaySeconds: 20 periodSeconds: 5 livenessProbe: httpGet: path: /health port: 8000 initialDelaySeconds: 30 periodSeconds: 30 volumes: - name: model-cache persistentVolumeClaim: claimName: model-cache-pvc注意几个关键点--gpu-memory-utilization 0.9表示vLLM最多能用到90%的显存留一部分给CUDA上下文和碎片否则高并发下容易OOM。--max-model-len 8192表示最大上下文长度。这个值越高显存占用越大要根据你的业务卡型合理设置。readinessProbe必须走/health接口。vLLM服务起来之后模型加载需要时间没就绪之前不能接流量。如果模型 单卡显存需要做张量并行加参数--tensor-parallel-size 2并且nvidia.com/gpu要申请2。网关侧我用的Ingress Controller本身流量都不小但大模型每个请求又长又慢所以未来IaaS的流量管理绝对是要自研一层的。我这里用Nginx Ingress做流式转发也踩过不少坑后面问题排查部分细讲。3.3 微调任务的编排从数据预处理到分布式训练微调任务在K8s上跑我强烈建议用KubeFlow Training Operator里面的PyTorchJob控制器不要自己写Pod去啃。它天然支持分布式训练的Master和Worker角色还会管理失败重试。看一个最小的LoRA微调任务声明apiVersion: kubeflow.org/v1 kind: PyTorchJob metadata: name: llama3-lora-finetune namespace: ai spec: pytorchReplicaSpecs: Master: replicas: 1 restartPolicy: OnFailure template: spec: nodeSelector: gpu-type: a100 containers: - name: pytorch image: registry.example.com/finetune:latest command: [python, -m, train_script.py] resources: limits: nvidia.com/gpu: 1 Worker: replicas: 2 restartPolicy: OnFailure template: spec: nodeSelector: gpu-type: a100 containers: - name: pytorch image: registry.example.com/finetune:latest command: [python, -m, train_script.py] resources: limits: nvidia.com/gpu: 1Packaging镜像时记得把训练脚本、依赖库、以及推理用的权重下载地址都打成参数不要在镜像里硬编码。数据从对象存储读Checkpoint写回对象存储这样任务删了也不心疼。还有一个非常容易被忽略的点微调作业的优先级和驱逐策略。在线推理服务是长跑型Pod微调任务往往是批量的。如果资源紧张调度器应该优先保证在线服务不被抢占。这时就得配PriorityClassapiVersion: scheduling.k8s.io/v1 kind: PriorityClass metadata: name: online-inference value: 1000000 globalDefault: false description: Online inference service pods.推理服务的Pod带上priorityClassName: online-inference微调任务用默认低优先级这样系统资源不足时低优先级任务会被驱逐或排队在线服务永远稳如狗。3.4 弹性伸缩与成本控制HPA、队列与配额大家都关心成本但大模型的成本控制和普通Web完全不同。普通的HPA看CPU/内存大模型推理服务的瓶颈是显存和并发数CPU反而不重要。我用的伸缩策略有三层基于并发数的HPA暴露一个指标比如当前正在处理的请求数inflight_requests超过阈值就扩。这是因为大模型推理是IO密集和显存密集单个Pod能同时处理的并发很有限。基于队列深度的伸缩如果请求要排队则说明Pod数量已经不够立刻扩容。定时扩容比如早晚高峰时段提前扩好Pod避免冷启动延迟吃掉用户体验。配私服HPA的示例apiVersion: autoscaling/v2 kind: HorizontalPodAutoscaler metadata: name: llama3-8b-hpa namespace: ai spec: scaleTargetRef: apiVersion: apps/v1 kind: Deployment name: llama3-8b-inference minReplicas: 2 maxReplicas: 8 metrics: - type: Pods pods: metric: name: inflight_requests target: type: AverageValue averageValue: 8注意扩缩容不能只看Prometheus指标延迟一定要观察显存占用。如果显存已经达到90%左右再扩容的Pod即使被调度上也会因OOM起不来所以要想清楚集群的资源余量。而配额这块就是K8s的ResourceQuota 和 PriorityClass 结合使用。给每个业务线划一个Namespace设定GPU配额上限和每天可消耗的核时数。热搜里那个“预冻结折合1.33核时”其实和这个很像本质上是平台上做配额预算的预占模式。我们平台的做法是训练任务提交时申请预计的核时数平台确认有额度后预冻结任务结束结算实际消耗这样就不会超买。4. 常见问题与排查技巧实录最后这部分全是真金白银的踩坑经验每一条都是拿线上故障换来的。4.1 GPU显存溢出与碎片化模型刚上线的时候我发现跑4、5个小时后Pod会莫名其妙OOM。后来排查发现是显存碎片化导致的。大模型推理时KV Cache的动态分配会产生大量显存碎片。如果用的是vLLM一定要开启它在PagedAttention的显存池管理并合理设置--gpu-memory-utilization不要追求100%填充。最稳的值是0.85到0.92之间留出余量。另外如果多路请求中有的context特别长有些很短KV Cache的分配极不均衡。处理办法是限制单卡最大并发数或者在网关层做请求长度预估和路由把长上下文请求均匀分发到多个Pod上避免一台服务器被打爆。有个排查技巧nvidia-smi看显存空闲很多但新服务就是起不来八成是显存碎片化严重。这时候重启一下推理Pod往往能立刻恢复。4.2 流式响应在网关层被截断SSE流式输出时的超时是最典型的线上问题。Nginx Ingress默认proxy-read-timeout是60秒大模型回答稍长就断流前端一直转圈用户体验极差。排查时你先在Pod内部直接 curl 一下如果Pod响应没问题那基本就锁定是网关问题。解决办法在Ingress注解里调大超时nginx.ingress.kubernetes.io/proxy-read-timeout: 600 nginx.ingress.kubernetes.io/proxy-send-timeout: 600 nginx.ingress.kubernetes.io/proxy-buffering: off第二条很关键proxy-buffering off必须关掉。否则Nginx默认会缓冲整个响应体再统一发送大模型这种持续生成的流式响应可能一直等不到结束就超时了。4.3 GPU配额冻结与预占策略的运营坑前面平台设计了配额预冻结实际操作中也很容易翻车。比如有团队提交了一个分布式训练任务GlobalBatchSize设置异常导致显存估算偏差申请了200核时实际要跑400核时任务跑到一半被扣成负数直接Kill。极其惨烈。我的建议是做配额预算时必须建立一个相对准确的任务成本预估算法至少包含卡型单卡算力训练数据量预估时长 数据量 / (全局BatchSize × 吞吐率)预估核时 卡数 × 预估时长平台侧要允许超卖一定比例比如配额池预留10%~20%的缓冲量否则大任务永远排不上队。我就碰到过因为配额池塞满重要任务排了一整天也起不来的情况最后只能手动扩集群节点。现在我们在平台层做了“弹性配额池”业务线高峰期可以借用公共池按优先级抢资源低优先级任务会被抢占归还。4.4 模型冷启动对发布的影响模型服务发布新版本时最怕的是流量瞬间被打进来Pod还没来得及加载模型。我一开始也以为有readinessProbe就够了后来发现读不了问题。因为readinessProbe一旦通过Ingress马上开始转发但vLLM实际还要做一次预热推理把CUDAGraph等缓存建立起来。这个过程可能又要几十秒。所以我又加了启动后延迟的postStart检测到推理框架Ready后主动构造一个低优先级的“预热请求”发送到/v1/chat/completions确保模型图和KV Cache预分配完成再启动接收流量。这一步让线上发布时的5xx率从3%降到了0。结尾的小提醒在大模型云原生这条路上我个人的体会是架构设计从来不是一步到位的。你先把推理服务跑起来再补调度的精细化再上配额和发布流水线这个递进节奏最适合大多数团队。我没有一步到位设计一个完美平台因为那样的平台大概率落不了地。最后分享一个小技巧所有和大模型相关的服务日志和指标一定要打够详细信息。我们每次请求都会记录模型名称、输入Token数、输出Token数、首Token延迟、总延迟、显存峰值。这样出了问题才能反向定位到底是架构问题、模型问题还是业务问题。大模型时代的云原生拼的就是谁能把黑盒变成白盒把混沌变成可控。
返回列表