Arganzheng's Blog

stay hungry, stay foolish

AI 平台工程(08):可观测、成本与 FinOps

Observability, Cost and FinOps: from DCGM to the Token Bill

月初,财务把上个月的 GPU 账单转给平台组:64 张 H100,按云上的小时价折下来是一个七位数。附带一个问题:这些钱花得值不值?平台组打开 Grafana,能给出的数字是两个——DCGM 报的平均”GPU 利用率”78%,节点上 nvidia.com/gpu 的平均分配率 85%。看起来不错。但换一个指标,DCGM_FI_PROF_SM_ACTIVE 的集群平均只有 35%。三个数字之间差了几十个百分点,而没有一张看板能说清这几十个点分别去了哪里、归哪个团队、对应前七篇里的哪个机制。 这就是本篇要处理的问题。前七篇搭起来的东西——GPU Operator、Kueue、MIG 与 HAMi、RDMA 与 checkpoint 存储、Serving 平台、推理网关...

AI 平台工程(07):模型网关与多租户——路由、配额与灰度

The Model Gateway: KV-Aware Routing, Multi-Tenancy, Quotas and Canaries

上一篇结束时,一个 70B 模型的 4 个副本已经跑在集群里,KEDA 会按 vllm:num_requests_waiting 把它扩到 6 个。把它们暴露出去最省事的做法是一个 Service 加一个 Ingress:kube-proxy 在 4 个 Pod 之间轮询,客户端拿到一个 URL,POST /v1/chat/completions,完事。这套东西跑起来没有任何报错,但第一周的监控会出现一个反直觉的图形:4 个副本的 vllm:kv_cache_usage_perc 长期不齐——一个在 95% 上下抖、两个在 60%、一个 30%;TTFT 的 p50 在 400 ms,p99 却到了 6 s;而 nvidia-smi 看总算力只用了一半。请求没有多到...

AI 平台工程(06):Serving 平台——从 InferenceService 到 llm-d

Serving Platforms: KServe, Triton, Ray Serve, LeaderWorkerSet and llm-d

一个常见的事故是这样的:某个 70B 模型的推理服务用 Deployment 部署了 2 个副本,每个副本 TP=4 占一台 4 卡机器,HPA 按 CPU 利用率 70% 扩容。晚高峰到来,用户侧 TTFT 从 1 秒涨到 20 秒,kubectl get hpa 却显示 CPU 只有 12%——vLLM 的 CPU 几乎全在等 GPU,KV cache 早已占满、几百个请求在引擎内部的等待队列里排队,而 K8s 对此一无所知。值班同学手动把副本改成 6,新 Pod 调度、拉镜像、从对象存储拉 140 GB 权重、加载到显存、做 CUDA graph 捕获,8 分钟后第一个新副本才开始接流量,此时高峰已经过去一半。第二天有人把副本常驻改成 6,于是 16 张 H1...

AI 平台工程(05):网络与存储——RDMA 进容器、并行文件系统与 checkpoint I/O

Networking and Storage: RDMA in Containers, Parallel File Systems and Checkpoint I/O

一个 8 节点 64 卡的训练任务在裸机上跑通了,all_reduce_perf 的大消息 busbw 接近网卡的标称值。同一个镜像、同一套 NCCL 环境变量搬到 Kubernetes 上,Pod 全部 Running,任务也在正常推进,只是 step time 慢了两倍多。再跑一次 all_reduce_perf,busbw 只剩裸机的三分之一。日志里没有报错,nvidia-smi 显示八张卡都在,ibstat 在宿主机上也一切正常。 这种”没有错误、只是慢”的故障在平台层非常典型,因为它不是某个组件坏了,而是引擎想走的那条路平台没有铺。NCCL 需要直接打开 /dev/infiniband/uverbs*、在网卡上注册显存、通过 RDMA 网卡的 IP 完成...

AI 平台工程(04):GPU 共享与切分——MIG、时间片、MPS 与 HAMi

Sharing and Partitioning GPUs: MIG, Time-Slicing, MPS and HAMi

月底看账单,推理平台上一张 80 GB 的 H100 每小时几美元,一个月跑满是四位数。再看 DCGM 的曲线:这张卡上唯一的服务是一个 7B 模型的 vLLM 副本,DCGM_FI_DEV_FB_USED 常年 22 GB 左右,DCGM_FI_PROF_SM_ACTIVE 白天峰值不到 30%,夜里几乎是零。也就是说,这张卡四分之三的显存和七成以上的算力在付费但没有产出。集群里这样的卡有几十张:每个团队的每个小模型都要”一张卡”,因为 nvidia.com/gpu: 1 是 device plugin 唯一听得懂的请求。 于是有人把两个服务塞到同一张卡上——直接在 Pod 里不声明 GPU、用 hostPath 挂上 /dev/nvidia*。省了一半的钱,也...

AI 平台工程(03):AI 任务调度——gang scheduling、队列与拓扑感知

Scheduling AI Jobs: Gang Scheduling, Queues, Quotas and Topology Awareness

周一早上,集群里有 40 张空闲的 GPU。算法团队提交了一个 4 节点 32 卡的预训练任务,kubectl get pods 显示 30 个 Pod Running、2 个 Pending。describe 那两个 Pending 的 Pod,事件是 0/12 nodes are available: 12 Insufficient nvidia.com/gpu——剩下的 8 张卡分散在四台机器上,每台两张,而这个任务的每个 Pod 要 8 张。30 个已经起来的 Pod 在 torchrun 的 rendezvous 里等那两个永远不会来的同伴,占着 30 张卡什么也不算。另一个团队的 8 卡任务也在 Pending:它要的 8 张卡本来在,现在被这 30 个...

AI 平台工程(02):容器里的 GPU——驱动、CUDA、device plugin 与镜像

GPUs in Containers: Driver, CUDA, Device Plugin, DRA and Images

上一篇结束在一个 Pending 的 Pod 上:resources.limits 里写了 nvidia.com/gpu: 1,kubectl describe 里是 0/3 nodes are available: 3 Insufficient nvidia.com/gpu。原因很直接——没有任何组件告诉 kubelet 这台机器上有 GPU。但把 device plugin 装上、Pod 调度成功之后,故障并没有结束,只是换了地方:Pod Running,torch.cuda.is_available() 返回 False,日志里一行 CUDA driver version is insufficient for CUDA runtime version;或者容...

AI 平台工程(01):引擎的需求清单与平台的整体架构

What Engines Demand from the Platform, and the Platform's Two Layers

一个刚装好的 Kubernetes 集群,三台 worker 每台插着一张 GPU。提交一个只有十几行的 Pod,resources.limits 里写 nvidia.com/gpu: 1,它一直 Pending。kubectl describe pod 的 Events 里只有一行:0/4 nodes are available: 3 Insufficient nvidia.com/gpu。三张卡明明在那里,nvidia-smi 在宿主机上能看到,调度器却说”不够”。 这一行事件是本系列的起点,因为它精确地暴露了原生 Kubernetes 与 AI 负载之间的第一道缝:调度器不知道什么是 GPU。它只知道节点的 status.allocatable 里有没有一个...

AI 平台工程:资源层与交付层(总纲)

AI Platform Engineering: the Resource Layer and the Delivery Layer

内容简介 《AI 平台工程:资源层与交付层》是一组共八篇的系列文章,面向已经跑过训练任务或推理服务、准备把它们放到一个多人共用的 GPU 集群上运行和交付的工程师,系统讲解引擎之下和引擎之侧的两层基础设施:资源层把 GPU、网络、存储组织成可调度的资源池,承载引擎运行;交付层把引擎包装成有 SLA、有配额、有账单、可观测的服务。 它回答的问题是: 一个 GPU 集群如何被切分、调度和喂饱?一个训好的模型如何变成一个可运维的服务? 站在引擎的层面看,平台是一个”给我八张卡、一个 RDMA 网口和一个能读 checkpoint 的路径,剩下的我自己来”的黑盒。这个视角是对的,但它掩盖了一件事:平台的每一个设计决定,都是被引擎的某个需求推出来的。 ...

大模型推理系统揭秘(14):回到源码:一次请求在 vLLM 内部的真实旅程

NOTE 本文基于 vLLM v0.27.1(tag 6e448d0, 2026-08-11)源码剖析。文中文件路径、类名和函数名均以该版本为准;vLLM 迭代很快,阅读时请以你手上的版本对照。 前十三篇回答了「是什么」和「为什么」,这一篇回答「怎么实现」。 现在你已经知道 Scheduler 为什么要按 token 预算调度、KV Cache 为什么要分块、Attention Backend 为什么要按 batch 形态分派。带着这些「为什么」回头看数据结构,它们就不再是需要背的字段列表,而是每一个都能对应到一个设计约束。 这一篇刻意放在最后:如果它出现在系列的第二篇,你只会看到一堆 class;出现在这里,你会看到设计决策留下的痕迹。 本...

×