恒美微站
首页
关于我们
建站服务
主题模板
案例展示
资讯中心
联系我们
AI-Infra入门指南:GPU集群调度与模型部署的工程实践
首页
资讯中心
/
AI-Infra入门指南:GPU集群调度与模型部署的工程实践
AI-Infra入门指南:GPU集群调度与模型部署的工程实践
发布时间:2026/10/8 20:37:27
如果一年前有人告诉我接下来一年我会花大量时间处理 GPU 驱动、调度器日志和数据加载速度我一定觉得他在开玩笑。但现实是我确实从一个每天写在线接口的后端工程师慢慢变成了团队里那个“啥都管的 AI 基础设施人”。这个词圈内叫 AI-Infra范围大得吓人GPU 集群、容器平台、任务调度、数据管线、模型推理、监控告警全是它的地盘。标题里写着“第一章”所以这篇我不打算讲那些特别炫的底层黑科技就想老老实实把入门时会踩的坑、必须建立的认知以及真正能把事情做稳的路径理一遍。无论你是刚被安排去维护 GPU 集群的同学还是算法团队里想搞明白“训练环境为什么这么慢”的工程师应该都能从中看到自己遇过的场景。1. 为什么是 AI-Infra一个后端工程师的意外转向1.1 从“加两台机器”到“整个平台跑不起来”我刚开始接触这块起因特别朴素算法团队说训练任务太多GPU 不够用要扩容。我当时的想法是加机器嘛像加两台 Web 服务器一样买来、上架、装系统、接负载均衡完事。但真正动手以后才发现事情完全不是这个逻辑。新的 GPU 机器到了以后光装驱动就折腾了一天算法同学把训练脚本丢进 Docker 镜像在单机上跑得好好的丢到集群里立刻报“找不到 GPU 设备”后来好不容易跑起来了又有任务因为调度策略不对8 个 worker 卡死了 7 个。这些事情单看每一件都很小但它们会同时压到你头上。尤其是团队里没有专职的 AI 基础设施工程师时最后往往是后端出身的人去接这摊事。我自己就是在那个状态下从“负责接口的人”变成了“负责让模型能跑起来的人”。整个过程没有任何正式交接驱动安装、镜像构建、任务编排、监控告警全部要靠现查现学。那段时间我的浏览器收藏夹里全是 NVIDIA 官方文档、Kubernetes 调度相关的 issue以及各种论坛里“同样报错”的讨论帖。现在回头看AI-Infra 最反直觉的地方是模型训练本身往往不是瓶颈基础设施才是。算法的 Loss 掉不下去可能是数据加载卡住了训练卡住不报错可能是某条网络链路在有损转发推理服务超时可能只是模型文件冷启动拉取太慢。这些问题都不在模型代码里却直接影响模型迭代效率。做这块的人不是在服务某个具体的模型而是在服务整个团队的产出速度。1.2 AI-Infra 究竟在管什么如果要给 AI-Infra 画一个边界我一般会按模型生命周期分四段来看。第一段是数据。数据从哪来、怎么预处理、放哪个存储、怎么被训练集群高效读取。这一段的工程难点不在“处理数据”本身而在“让成百上千个 GPU 同时拿到数据且不会卡住”。第二段是训练。这包括任务怎么提交、调度到哪些节点、资源怎么分配、多机多卡通信是否正常、检查点怎么保存和恢复。第三段是推理。模型训练完以后要部署成常驻服务得有接口、有健康检查、有自动扩缩容、有过载保护。第四段是观测。GPU 温度、显存占用、网络吞吐、任务排队时间、服务延迟所有状态都得能监控到出了问题能快速定位。很多刚接触 AI-Infra 的人会误以为它只是“把 GPU 塞进 Kubernetes”这远远不够。GPU 塞进去以后还要解决怎么分配、怎么排队、怎么避免任务之间互相干扰、怎么在训练和推理之间平衡资源。每一个问题背后都牵扯到硬件、操作系统、容器运行时、平台调度多个层面。这也是为什么标题里我把它叫“路”因为它不是某个单一技术而是一整套围绕模型生命周期的工程能力。2. GPU 集群的第一课从裸机到容器调度2.1 一台 GPU 节点内部的分层结构刚开始接触 GPU 集群时我最容易犯的错是把它当成普通的 Linux 服务器。实际上一台 GPU 节点上跑 AI 任务至少要经过五层。最底下是硬件层GPU 卡通过 PCIe 或 NVLink 连在主机上显存、算力、带宽这些物理参数都是这一层决定的。再往上是驱动层NVIDIA 驱动通过内核模块把 GPU 的能力暴露给操作系统通常还会带 CUDA 运行库。没有驱动操作系统根本不知道 GPU 存在。驱动再往上是容器运行时层。Docker 或者 containerd 默认是摸不到 GPU 的需要借助 nvidia-container-toolkit 这类组件把宿主机上的 GPU 设备文件和运行库“注入”到容器里容器里才能执行nvidia-smi。再往上是设备插件层Kubernetes 不会平白无故知道你节点上有几张 GPU需要通过 Device Plugin 把 GPU 数量上报成一种可调度的扩展资源比如nvidia.com/gpu。最上面才是调度层用户 Pod 声明需要的 GPU 数量调度器按照资源情况进行分配。很多部署问题都出在层与层之间。比如容器里nvidia-smi能跑但 Pod 调度不上可能是 Device Plugin 没起来宿主机上nvidia-smi正常但容器里看不见卡可能是 nvidia-container-toolkit 配置没生效。排障的时候一定要先搞清楚问题到底落在哪一层而不是蒙着头重启所有组件。2.2 Kubernetes Device Plugin 为什么成了事实标准早期很多团队训练模型直接用裸机一台机器装好驱动和虚拟环境跑完一个任务再人工切到下一个。这种做法在小规模时挺省事但一旦有多个人、多个团队同时要 GPU就乱了谁用哪张卡、显存会不会互相挤爆、任务怎么排队全靠口头协调。Kubernetes 能成为事实标准核心原因是它把“CPU、内存、GPU”都抽象成可声明、可调度、可隔离的资源。具体做法倒不复杂。节点上部署 NVIDIA Device Plugin它启动后会向 kubelet 报告nvidia.com/gpu: 8这样 Kubernetes 就知道这台节点有 8 个 GPU 资源。用户在 Pod 里声明resources.limits[nvidia.com/gpu]: 2调度器就能像分配内存一样分配 GPU。实际资源分配成功后Device Plugin 会负责把对应的设备文件映射进容器并设置好环境变量。这套机制最厉害的地方在于健康检查。Device Plugin 会持续监控 GPU 状态如果某张卡出现 ECC 错误或驱动异常它会把对应的资源标记为不可用调度器就不会再把新任务分配过去。这个特性在生产环境极其重要因为 GPU 硬件故障率并不算低。如果没有这一层自动隔离一张坏卡可能让整个集群上的训练任务反复失败。不过也要提醒一点Kubernetes 只是解决了“调度和分配”并不解决“显存共享”。默认情况下一张 GPU 卡就是一份资源谁申请到就是谁的。如果你想让多个小任务共享同一张卡需要额外开 MIG、时间片或者 MPS这些都会引入新的隔离问题。我的建议是第一版平台不要追求“极致共享”先把整卡级别的调度做稳定再考虑优化碎片。2.3 混部、异构与驱动管理的现实取舍真实集群几乎没有“全场只有一个 GPU 型号”的完美环境。要么是 A100 和 H800 混着要么是训练节点和推理节点用了不同规格的卡。处理异构最直接的办法是给节点打上 GPU 型号标签并让用户提交任务时通过nodeSelector或亲和性指定需要的型号。但异构带来的麻烦不只是调度还有驱动。不同代的 GPU 可能需要不同版本的驱动而节点内核升级之后驱动模块可能需要重新编译。如果全靠人工维护很快你就会发现某个节点驱动版本不对任务一上去就报“no kernel image available”。这种情况下比较推荐的做法是引入 GPU Operator它能以 DaemonSet 的方式自动安装和管理驱动还能联动容器运行时和 Device Plugin把整个 GPU 软件栈都变成可声明、可升级的对象。需要提醒的是驱动版本别为了“支持所有卡”而盲目追求统一。团队里曾经有个阶段驱动版本不一致导致同一份训练镜像在不同节点上表现完全不同有的节点正常有的节点 CUDA 版本不匹配有的节点性能明显偏低。后来规范成“每个节点池固定驱动版本 标签强制匹配”这类问题才基本消失。硬件异构可以存在但软件栈必须收敛。3. 训练任务调度比“有空位”多一百个细节3.1 “整组调度”不是洁癖是分布式训练的底线如果用 Kubernetes 默认的调度器去跑一个需要 8 张 GPU 的分布式训练任务你会遇到一种很恶心的现象8 个 worker Pod 被调度上 7 个最后一个因为资源不够一直 Pending而已经启动的 7 个 worker 在互相等待整个任务卡死没有任何报错。这就是分布式训练的“整组调度”问题。训练任务不是一个可以“先启动一部分剩下稍后再补”的普通服务。无论是 PyTorch DDP 还是大模型常用的 Megatron 框架所有 rank 通常都要在初始化阶段建立通信有一个 rank 没起来其他 rank 就会一直阻塞在初始化或者集合通信上。所以调度器必须保证要么把整套任务的资源一次给足要么一个都不调度。解决这个问题的常用方案是引入支持 Gang Scheduling 的调度组件比如 Volcano。Volcano 允许你把一组 Pod 定义成一个 PodGroup并设置minAvailable为整套任务需要的副本数。调度器会先尝试为整个 PodGroup 找齐资源找不齐就一直等待不会出现“部分启动、部分等待”的死局。Kubernetes 社区近两年也在推 Kueue它对配额管理、优先级和排队做了更细致的抽象和 Volcano 各有侧重。第一阶段我建议先理解 Gang Scheduling 的思想再根据团队规模选择组件不要一上来就造轮子。3.2 拓扑感知让通信尽量在交换机以内发生整组调度解决了“能不能同时启动”但没解决“跑得快不快”。分布式训练里GPU 之间的通信量非常大尤其是每次迭代都要做 all-reduce把几十张卡上的梯度汇总一遍。如果通信发生在同一台机器内走的是 NVLink 或 PCIe带宽高、延迟低如果跨机器就必须走网络这时候拓扑就变得极其关键。先说单个节点。你用nvidia-smi topo -m能看到不同 GPU 之间的互联方式。理想情况下一个训练任务最好占用同一节点里 NVLink 全互联的那组 GPU而不是分散到不同 PCIe Switch 下。再说跨节点。如果任务要跨两台乃至多台机器这些机器最好在同一个接入交换机下避免流量绕到核心交换机引来越大的延迟和丢包。曾经有个常识跨交换机训练的吞吐可能只有同交换机训练的一半甚至更低。实际落地时最简单的做法是给节点打“机架/交换机”标签并让训练任务通过节点亲和性尽量落在同一组节点里。例如给同一个接入交换机下的 4 台机器打上topologyrack-a任务申请多机资源时优先选同一个 rack。更高级的还能用调度器插件做拓扑感知调度但在第一章阶段先用标签和亲和性解决 80% 的问题比零散地追求最优解更实际。3.3 配额、优先级与抢占写崩过的机制资源充足时调度很轻松但现实是 GPU 永远不够。团队里十个项目同时排队总有人问“凭什么我的任务不能先跑”。这时候就需要配额和优先级机制。配额方面Kubernetes 自带的 ResourceQuota 只能限制数量比如某个命名空间最多占用 32 张 GPU但它不知道这个配额是“用了一个小时”还是“用了一整天”。所以好多团队会引入“GPU 时间”的概念通过平台统计每个租户累计占用的 GPU 卡时数再按比例分配。这个统计不需要很精确但它解决的问题是“公平感”。优先级和抢占更微妙。你可以给关键任务设置高优先级低优先级任务在资源不足时被抢占。但抢占对分布式训练是致命打击8 卡任务跑了两天突然被抢走 2 张卡整个任务直接崩掉前两天算力全部白费。所以我们后来约定抢占只发生在两类场景一是高优在线推理服务扩容二是预算弹性的短任务长时间大规模训练任务必须走预留资源池不参与抢占竞争。这个约定本质上不是技术问题而是资源管理策略的问题但基础设施工程师必须把它变成平台能力否则永远在处理吵架。4. 数据管线GPU 不值钱的错觉是怎么来的4.1 先查数据再查训练一次 GPU 利用率排查记录有一段时间监控面板显示集群 GPU 平均利用率很低但所有训练任务都显示“正在运行”。算法同学的反馈是“任务没报错但变慢了。”我一度怀疑是不是驱动调度有问题后来逐层排查才发现瓶颈根本不在 GPU而在数据读取。当时的场景是训练脚本用 NFS 从共享存储里读图片数据每轮迭代都要拉一批样本。NFS 单服务器的吞吐和处理不了多个训练任务同时读取DataLoader 的 worker 全堵在 IO 等待上GPU 因为拿不到数据只能空转。现象就是nvidia-smi显示 GPU 利用率在 10% 到 30% 之间波动算力完全没有发挥出来。这个案例让我养成了一个习惯看到 GPU 利用率低第一反应不是检查显存或温度而是看数据链路。具体排查时我会在容器里看iostat和top确认 CPU 是不是大量时间处于wa状态也会看 DataLoader 的日志确认数据预取是否匹配训练步长。如果是数据读取慢加牛 X 的 GPU 也没用省下来的钱不如先投到存储和缓存上。4.2 存储分层的正确姿势NVMe 并行文件系统与对象存储在 AI-Infra 里存储不能只有一种。我见过太多团队一开始“所有数据都放 NFS”接着 NFS 成为瓶颈然后大家一起痛苦。合理的做法是分三层。第一层是节点本地 NVMe SSD作为热数据缓存。训练数据集如果太大无法全部装进内存也至少要把当前 epoch 最常读的文件放到本地盘。像 JuiceFS 这类文件系统支持配置本地缓存第一次读取后数据留在节点上后续 epoch 就快很多。第二层是并行文件系统或分布式文件存储比如 Lustre、CephFS 或 JuiceFS用于多节点同时读取大规模数据集、保存共享检查点。它的吞吐能撑得住几十张 GPU 同时读数据。第三层是对象存储比如 S3 兼容的桶用来保存原始数据、历史模型制品。对象存储容量大、成本低但不适合高频直接读取。每一层之间要有清晰的数据流转规则原始数据进对象存储训练前把数据集同步到高速层或拉起本地缓存训练结束只把检查点和模型制品回传对象存储。这条链路一旦跑顺你的 GPU 利用率自然会上来。反过来如果数据结构一开始就没想清楚后面每次训练都是一次数据搬运地狱。4.3 检查点并发写入凌晨三点血的教训训练任务最值钱的东西是检查点。模型跑了两天突然因硬件故障崩了如果没有检查点一切归零。而检查点的写入恰恰有很多隐蔽的坑。最大的坑是并发写。分布式训练中多个 rank 可能同时向同一个检查点目录写文件。如果每个 rank 写一份“完整模型”文件会互相覆盖如果约定每个 rank 写自己的 shard又需要最后有一个索引文件把它们组装起来。更常见的是某个进程在写临时文件时被中断留下一个半截文件下一次恢复时框架直接崩掉。我自己的处理办法是检查点先写到集群本地或共享目录的临时位置例如checkpoint.tmp全部写完后再执行原子性rename最终生成checkpoint-{step}.pt。同时写一个latest.txt记录当前最新的检查点路径恢复时只读这个索引避免把半成品当成可用模型。有些框架还支持异步检查点把保存动作放到后台线程不让磁盘慢速拖累训练步长。这个细节看似不起眼但真正遇到“跑了三天突然恢复失败”的时候你会回来感谢这一段的。5. 推理服务模型部署是另一套工程体系5.1 常驻服务与批处理任务的本质差异训练和推理虽然用的都是 GPU但工程形态完全不同。训练任务是批处理跑完就退出即使中途卡住也可以人工介入推理服务是常驻服务要长期运行、有固定接口、随时承受线上流量。很多第一次部署模型的团队会踩同一个坑把 PyTorch 的 Python 脚本直接flask run跑起来不做显存规划不做并发控制结果服务一上线几个请求就把显存打爆进程直接崩溃。推理服务的第一个要求是标准化接口。算法团队输出的模型可以是 PyTorch、TensorFlow 或者 ONNX但对外最好统一成 HTTP/gRPC 接口。这样上层调用方不用关心模型怎么部署后续替换引擎也不影响业务。接口层下面才是推理引擎比如 vLLM 对生成式模型做得比较顺手Triton 对多模型多框架更灵活。第一版可以用一个引擎撑住一种模型但接口层一定要收敛否则每个模型部署方式都不一样运维复杂度会直线上升。推理的另一个关键点是长连接和批处理。生成式模型的请求通常比较慢如果每个请求独占一张卡成本不可接受。vLLM 这类引擎引入了 continuous batching一个前向过程里同时处理多个请求的多个 token哪个请求先结束就先释放新的请求只要能插入就接进去。这要求你在部署时配置好max_num_seqs和gpu-memory-utilization给 KV Cache 留足空间同时避免显存占用过高导致 OOM。5.2 显存、排队与自动伸缩的调优起点推理服务的扩缩容逻辑不能照搬 Web 服务。Web 服务看 CPU推理服务的瓶颈可能在显存和请求排队长度。比如你部署了一个 13B 模型单卡能并行处理 8 个请求当第 9 个请求到来时它只能排队。这时候整卡算力可能还没用满但显存已经决定并发上限。所以如果你只看 CPU 利用率永远触发不了扩容。更实际的伸缩指标是请求队列深度。你可以把 “排队请求数” “请求平均排队时间” 暴露成 Prometheus 自定义指标让 HPA 根据这些指标扩容。队列变长说明服务处理不过来就该加副本。反过来如果请求延迟正常但没有新请求可以考虑缩容但要给每个副本一个冷却时间避免模型加载造成的慢启动反复触发。有一个容易被低估的问题是一个 Pod 里到底放几张 GPU 业务卡。我的建议是推理服务尽量用单卡或双卡部署通过多副本横向扩展而不是在一个 Pod 里塞 8 张卡做单点大服务。横向扩展的好处是故障半径小一个副本挂了不影响全部流量而且更利于缩容时精确释放资源。多卡部署只有在模型大到单卡放不下时才需要那个场景要另外设计张量并行。5.3 冷启动一个 8GB 模型引发的超时事故我印象很深的一次事故是新模型上线后前几个请求全部超时。看监控发现服务有健康检查但健康检查的逻辑是“进程活着就通过”实际上模型权重还在从对象存储慢慢拉取。Pod 状态显示 Running但根本没能力处理请求调用方打进来的流量全部超时重试形成雪崩。排查后发现两个问题。第一模型文件存在远端对象存储冷启动时每次都要实时下载8GB 的模型拖了三分钟。第二健康检查没有覆盖“模型是否真正加载完成”导致流量提前打进来。后来我们把模型文件预置在节点本地磁盘或镜像里开启“预热请求”让服务启动后先自己发一个空请求把模型完全加载进显存再对外提供服务同时把 ReadinessProbe 改成一个真正校验模型的就绪接口。这样处理之后冷启动从 3 分钟压到 10 秒左右再也没出现“进程活着但服务不可用”的怪异状态。推理部署的很多坑其实都集中在“进程状态”和“服务能力”之间的差距上你要保证平台判断的永远是后者。6. 观测和排障AI-Infra 工程师的白天与黑夜6.1 用 DCGM 把 GPU 的“体检报告”拉到监控屏做 AI-Infra 不能靠“感觉”。GPU 是不是真的在干活、温度是否接近红线、显存是否泄漏、NVLink 带宽是否正常都必须有数据支撑。NVIDIA 官方提供的 DCGM也就是数据中心 GPU 管理接口能采集到比nvidia-smi更丰富的指标生产中通常用 DCGM Exporter 把这些指标暴露给 Prometheus。我监控面板上常驻的指标包括SM 利用率、显存占用率、GPU 温度、核心频率、显存频率、PCIe 和 NVLink 吞吐、功耗。这些指标能帮你回答几个高频问题训练任务是不是真的吃满了算力是不是显存不够导致模型换入换出是不是跨节点通信瓶颈导致 GPU 一直在等待有了这些数据很多争论就有了依据。告警别太密也别太松。我常用的两条底线规则一是 GPU 温度持续超过 85 度属于过热去看机房散热或风扇策略二是训练任务在跑但 GPU 利用率长期低于 10%这通常是数据和通信问题需要马上预警因为浪费的是真金白银。另外要关注 XID 错误GPU 驱动会报告硬件错误码一旦频发这张卡大概率要下线换卡了。6.2 高频问题排查速查表现象可能原因排查手段Pod 一直 Pending提示 insufficient nvidia.com/gpu节点 GPU 不足Device Plugin 未运行GPU 健康检查失败kubectl describe node查看可分配资源kubectl get pods -n gpu-operator检查节点上的 Device Plugin 日志容器里执行 nvidia-smi 报 No devices容器缺少 GPU 设备映射nvidia-container-toolkit 配置不对驱动与容器运行时不兼容重建容器并确认 runtime 是 nvidia检查/etc/docker/daemon.json或 containerd 的 runtime 配置宿主机先验证 nvidia-smiNCCL 报错 timeout waiting for rank跨节点网络问题任务跨了上层交换机某条链路丢包启动时加NCCL_DEBUGINFO在节点间跑通信带宽测试用nvidia-smi topo -m检查拓扑模型推理首请求极慢冷启动拉取模型没有预热健康检查不准确检查模型文件是否在本地缓存配置预热请求修正 ReadinessProbe训练任务 GPU 利用率低数据加载慢DataLoader 配置不合理通信瓶颈看 iostat、网络吞吐调大 DataLoader num_workers 或 prefetch检查跨节点通信6.3 一套我喜欢的排查顺序AI-Infra 的问题很容易让人手忙脚乱因为太多环节看起来都像“有问题”。我自己养成的习惯是固定排查顺序从应用层往硬件层一层一层剥。第一步是确认任务本身。先看训练或推理进程的日志排除代码逻辑问题第二步是确认平台层比如调度状态、配额、镜像、资源限制第三步是确认节点层比如驱动版本、内核日志、容器运行时配置第四步才是怀疑硬件比如 GPU 温度、XID 错误、网卡丢包。不要一上来就重启节点也不要看到 GC 日志就断言内存不足大部分诡异问题都藏在层与层的交界处。这个顺序不能帮你解决所有问题但能保证你不会在错误的地方浪费时间。尤其是夜班排查的时候清醒的头脑比炫技更重要。先把表面大概率排除剩下的根因就会浮出来。我写这一章的时候最想分享的也不是某个具体配置而是这种“分层排查、边查边验证”的思维习惯。后面第二章如果还有机会我想仔细聊聊弹性训练、资源隔离和推理引擎选型那些又是另一大片坑。