文章目录
-
- [一、 显卡不是 CPU:整数绑卡的坑](#一、 显卡不是 CPU:整数绑卡的坑)
-
- [1.1 痛点:算力利用率只有 15%](#1.1 痛点:算力利用率只有 15%)
- [1.2 避坑解法:算力切分的三大流派](#1.2 避坑解法:算力切分的三大流派)
- [二、死锁灾难:为什么默认调度器会让 100 张卡整夜"空转"?](#二、死锁灾难:为什么默认调度器会让 100 张卡整夜“空转”?)
-
- [2.1 默认调度器的"贪心灾难"](#2.1 默认调度器的“贪心灾难”)
- [2.2 避坑解法:必须引入 Gang Scheduling(组调度)](#2.2 避坑解法:必须引入 Gang Scheduling(组调度))
- [三、 存储与网络风暴:50 个 Pod 并发拉取,底层直接瘫痪](#三、 存储与网络风暴:50 个 Pod 并发拉取,底层直接瘫痪)
-
- [3.1 存储风暴(Storage Storm)](#3.1 存储风暴(Storage Storm))
- [3.2 网络黑洞(NCCL 通信超时)](#3.2 网络黑洞(NCCL 通信超时))
- [四、 幽灵故障:cAdvisor 看着一切正常,模型其实一直在输出废话](#四、 幽灵故障:cAdvisor 看着一切正常,模型其实一直在输出废话)
-
- [4.1 隐蔽的 CUDA Xid 硬件错误](#4.1 隐蔽的 CUDA Xid 硬件错误)
- [4.2 避坑解法:DCGM + NPD 自动排空自愈](#4.2 避坑解法:DCGM + NPD 自动排空自愈)
- [五、 流式打字机的 Nginx 惨案:为什么会"卡顿憋大招"和 504 超时?](#五、 流式打字机的 Nginx 惨案:为什么会“卡顿憋大招”和 504 超时?)
-
- [5.1 惨案根因:](#5.1 惨案根因:)
- [5.2 生产级 Nginx 配置标准:](#5.2 生产级 Nginx 配置标准:)
- [六、 写在最后:传统运维人的二次跃迁](#六、 写在最后:传统运维人的二次跃迁)
一、 显卡不是 CPU:整数绑卡的坑
在传统的微服务架构里,CPU 和内存是弹性且可以任意细分的。你给一个 Web Pod 分配 cpu: 500m、memory: 1Gi,K8s 的 Cgroups 限制得明明白白。
当我们把大模型往上扔时,迎头撞上的第一堵墙,就是 K8s 原生的资源分配机制。
1.1 痛点:算力利用率只有 15%
K8s 原生的 nvidia.com/gpu: 1 只能做整数整卡挂载。当时业务部门要上线一个 14B 的客服微调模型,单卡显存占用大概 18GB。我们用的是 80GB 的 A800/H800,结果一张卡挂给这个 Pod 后,剩下的 62GB 显存完全闲置,其他服务根本用不了!
月底调出 Grafana 大盘一看账单,:"几千万采购的算力,怎么平均利用率才 15%?!"
传统思维 :Pod 需要显卡 -> resources.limits: nvidia.com/gpu: 1(整卡锁定,巨大浪费)
生产实际:单卡多租户混合、显存切分与算力共享才是常态!
1.2 避坑解法:算力切分的三大流派
为了把显存利用率拉到 80% 以上,我们先后踩遍了三种技术路线:
- NVIDIA MPS(多进程服务):适合小模型推理的高并发算力共享,算力利用率翻倍;但痛点是一个 Pod 崩溃容易导致共享该卡的所有任务一起挂掉
- NVIDIA MIG(多实例 GPU):A100/H100 上的物理硬隔离,安全级别极高,但切分规格固定(比如 10G/20G),无法随意定制。
- 开源 vGPU 方案(如 Hami):在 K8s 层劫持 CUDA 调用,成功实现了对 RTX 4090 / 6000 等卡按 1GB 显存颗粒度和 10% 算力核心进行超卖切分,最终将综合利用率提升到了 75% 以上。
二、死锁灾难:为什么默认调度器会让 100 张卡整夜"空转"?
这是我们经历过的最严重的一次生产事故。那天晚上算法团队提交了两个大规模分布式训练任务(DeepSpeed 多机多卡并行),任务 A 和任务 B 各需要 32 张卡(4 台 8 卡机器协同)。
2.1 默认调度器的"贪心灾难"
K8s 默认的 kube-scheduler 是典型的"来一个 Pod 分配一个 Pod":
任务 A 抢到了节点 1、节点 2(共 16 张卡),正在等待剩下的 16 张卡;
任务 B 抢到了节点 3、节点 4(共 16 张卡),也在等待剩下的 16 张卡;
此时集群里只剩下零散卡槽,任务 A 和任务 B 谁也凑不齐 32 张卡!
任务 A:占住 16 卡 ──(等待另外 16 卡)──► 无法启动
任务 B:占住 16 卡 ──(等待另外 16 卡)──► 无法启动
结果:全集群陷入"活锁死锁",几千万的 GPU 白白空转了一整夜!
2.2 避坑解法:必须引入 Gang Scheduling(组调度)
传统 Web 服务可以一个一个起,但分布式 AI 训练必须满足:"All-or-Nothing(要么全部满足同时起,要么一个都不起)"。
我们紧急下线了默认调度,全面切换到云原生批量调度平台 Volcano 与 Kueue:
- Gang 组调度:设置 minAvailable: 32,只有当 32 个 Pod 所需资源全部锁定时才统一放行绑定,彻底杜绝互相死锁;
- Queue 队列与优先级抢占:区分"高优先级在线推理"与"低优先级离线微调",白天推理并发高时抢占算力,夜间算力空闲时自动释放给批处理任务跑微调。
三、 存储与网络风暴:50 个 Pod 并发拉取,底层直接瘫痪
大模型动辄几十 GB,这不是常规几百 MB 的 Java/Go 容器镜像,而是一场对机房 I/O 的极限摧残。
3.1 存储风暴(Storage Storm)
当我们尝试做一次水平扩容,50 个推理 Pod 同时启动并从内部共享存储(NFS / Ceph)拉取 80GB 的模型权重时,存储集群的 IOPS 和网络出口瞬间被打爆,节点大面积陷入 NotReady,K8s 出现大面积探针超时。
- 避坑方案:放弃传统的集中式拉取,采用 JuiceFS + 本地高速 NVMe 缓存,并在每个 GPU 节点上挂载只读缓存盘;同时引入 Dragonfly P2P 镜像加速,百台节点分发大镜像不再依赖中心镜像仓库。
3.2 网络黑洞(NCCL 通信超时)
在做多机多卡训练时,最折磨运维的报错莫过于:
NCCL WARN Timeout waiting for operation to complete...
传统的千兆/万兆以太网在处理模型全量同步(AllReduce)时延迟极高,CPU 频繁丢包。搞 AI Infra 必须补课 RoCE v2(RDMA)无损网络,在 K8s 中通过 SR-IOV 插件将物理网卡直通进容器,才能把通信延迟压缩到微秒级。
四、 幽灵故障:cAdvisor 看着一切正常,模型其实一直在输出废话
传统的微服务监控体系,在 GPU 硬件故障面前就像个"睁眼瞎"。很多时候,K8s 显示 Pod 处于 Running 状态,CPU 和内存占用完全正常,但算法同学却跑来投诉:"模型推理怎么卡死了?吐字速度只有 1 Token/s,有时候还全是乱码!
4.1 隐蔽的 CUDA Xid 硬件错误
GPU 硬件非常脆弱。供电瞬断、散热不良、显存 ECC 双位翻转,都会触发 NVIDIA 底层的 Xid 错误(例如经典的 Xid 31: GPU memory page fault 或 Xid 62)。此时显卡可能已经掉频甚至半死,但只要容器进程没彻底 Crash,K8s 就觉得它"很健康"
4.2 避坑解法:DCGM + NPD 自动排空自愈
我们彻底重构了监控链路:
- 废弃单纯的 Node-Exporter,全量上线 NVIDIA DCGM-Exporter,实时采集 GPU 温度、风扇转速、显存带宽利用率与 ECC 错误率;
- 联动 Node Problem Detector(NPD):一旦捕获到特定的硬件 Xid 错误,立刻给当前节点打上污点(Taint),自动将上面的业务 Pod 优雅驱逐(Drain),并触发硬件保修脚本,不再等人肉排查。
五、 流式打字机的 Nginx 惨案:为什么会"卡顿憋大招"和 504 超时?
好不容易把集群和模型跑通了,挂上 Nginx 对外提供 API 服务时,前端用户又炸锅了:"说好的丝滑打字机效果呢?怎么屏幕卡住几十秒,然后突然哗啦一下蹦出一大坨字?!"更有甚者,遇到复杂长文本推理,前端动不动就弹窗报错:504 Gateway Timeout。
5.1 惨案根因:
-
缓冲陷阱:Nginx 默认配置了 proxy_buffering on;,它把大模型吐出的单个 SSE Token 存积在内存缓冲区里(攒满 4KB/8KB),才一次性发给客户端,打字机瞬间变成"憋大招";
-
超时陷阱:Nginx 默认读取超时是 proxy_read_timeout 60s;,大模型做长文本预填充(Prefill)或深度思考时超过 60 秒,Nginx 直接无情断开连接。
5.2 生产级 Nginx 配置标准:
在转发大模型推理路由时,必须严格配置以下参数:
shell
location /v1/chat/completions {
proxy_pass http://vllm-backend;
# 彻底关闭缓冲,强制逐 Token 秒级直推客户端
proxy_buffering off;
proxy_cache off;
chunked_transfer_encoding on;
# 调大超时时间,适配长文本思考与推理
proxy_read_timeout 600s;
proxy_send_timeout 600s;
# 维持 HTTP/1.1 长连接
proxy_http_version 1.1;
proxy_set_header Connection "";
}
六、 写在最后:传统运维人的二次跃迁
过去微服务时代,K8s 运维的工作大多被各大公有云平台(ACK、TKE、EKS)封装成了标准服务,纯微服务运维的薪资天花板越来越低。