GPU集群作业调度与队列治理实战 - 第2节
资源模型:CPU、内存、GPU、网络带宽等资源的抽象与建模
聊到资源模型,我脑子里第一个蹦出来的词就是「抽象」。说白了,集群里的机器千奇百怪,有的CPU强,有的内存大,有的挂着GPU。调度器怎么统一看待它们?靠的就是资源抽象。
我个人习惯把资源抽象理解成「给机器画一张标准化的资源清单」。每台机器上报自己有多少CPU、多少内存、几块GPU、网卡带宽多大。调度器拿到这张清单,才能决定把任务往哪扔。
CPU资源的抽象
CPU在Kubernetes里用「核」来表示。1核等于一个物理核心或者一个虚拟CPU(vCPU)。但这里有个坑——CPU是可压缩资源。什么意思?你给一个容器分配了1核,它用不满,剩下的CPU可以被其他容器抢走。但如果它想用超过1核,就会被限流。
我在项目中遇到过一个问题:某个Java服务把CPU Request设得很低,Limit设得很高。结果高峰期CPU被打满,服务响应变慢,但没被杀死。因为CPU是可压缩的,系统只会限流,不会OOM Kill。嗯,这其实是个设计选择——让服务降级而不是崩溃。
内存资源的抽象
内存就不一样了。它是不可压缩资源。一旦容器申请了2GB内存,这2GB就被独占,别的容器用不了。如果容器试图超过Limit,系统会直接把它杀掉(OOM Kill)。
你想想看,这其实很残酷。CPU超了还能凑合用,内存超了直接没命。所以我建议:内存的Requests和Limits最好设成一样。别给自己挖坑。
| 资源类型 | 可压缩? | 超限后果 | 建议 |
|---|---|---|---|
| CPU | 是 | 限流(Throttling) | Requests可设低,Limit设高 |
| 内存 | 否 | OOM Kill | Requests = Limits |
| GPU | 否 | 任务失败 | 显式声明,不要超分 |
| 网络带宽 | 是(部分) | 丢包/限速 | 结合QoS策略 |
GPU资源的抽象
GPU的抽象比较特殊。它不像CPU那样可以按毫核来分。一张GPU卡要么给某个任务用,要么不给。Kubernetes通过Extended Resource机制来管理GPU,比如 nvidia.com/gpu: 1 就表示申请一张卡。
我曾经踩过一个坑:两个小任务各申请0.5张GPU,结果调度器直接拒绝了。因为GPU不支持分数。后来我们改用MIG(多实例GPU)技术,把一张A100切分成7个实例,每个实例算一个独立资源。这才解决了细粒度分配的问题。
网络带宽的抽象
网络带宽的抽象是最容易被忽略的。很多调度系统根本不建模带宽,结果任务跑起来才发现网络成了瓶颈。
我个人习惯把带宽建模成节点属性。比如每台机器标注「上行带宽10Gbps,下行带宽10Gbps」。调度时检查所有任务的总带宽请求是否超过节点上限。当然,这需要配合CNI插件来做流量控制。
为什么会这样?因为网络是共享的。一个任务疯狂发数据,其他任务就得等着。你不建模带宽,调度器就是个瞎子。
资源请求与限制(Requests & Limits)
Requests和Limits是Kubernetes资源管理的核心机制。我刚开始用的时候也觉得不就是设两个数嘛,后来才发现这里面的门道很深。
Requests:调度时的保证
Requests告诉调度器:「这个任务至少需要这么多资源」。调度器会找一台能满足所有Requests总和的节点。比如一个Pod请求2核CPU、4GB内存,那它只会被调度到剩余资源大于等于这个数的节点上。
但注意,Requests不代表实际使用量。你的Pod可能只用了0.1核,但它占着2核的配额。其他Pod不能抢。这就是资源预留的概念。
Limits:运行时的上限
Limits是硬限制。CPU超了会被限流,内存超了会被杀掉。我见过很多团队把Limits设得特别大,以为这样能「弹性伸缩」。结果呢?节点资源被某个Pod吃光,其他Pod全挂。
一个典型的配置示例
apiVersion: v1
kind: Pod
metadata:
name: demo-pod
spec:
containers:
- name: app
image: nginx
resources:
requests:
memory: "512Mi"
cpu: "500m"
limits:
memory: "1Gi"
cpu: "1"
这个配置的意思是:保证给这个Pod 0.5核CPU和512MB内存,但允许它最多用到1核CPU和1GB内存。CPU可以超,内存不能超。
资源建模的核心逻辑
说了这么多,我画了一张图来总结资源模型的核心逻辑。你一看就明白了。
这张图把整个流程串起来了。节点上报资源,Pod声明需求,调度器做匹配,运行时做限制。每一步都离不开资源模型。
避坑指南
我曾经犯过一个低级错误:给一个批处理任务设了CPU Requests为4核,但集群里所有节点都只有2核。结果任务永远处于Pending状态。调度器不是万能的,它只能在你给的数据上做决策。
还有一次,我把内存Limits设成了节点总内存的80%,结果一个Pod的内存泄漏直接拖垮了整个节点。后来我学乖了:所有Pod的Requests总和不能超过节点资源的80%,留点余量给系统进程和突发情况。
嗯,资源模型这块就聊到这儿。说白了就是两件事:把资源抽象成标准格式,然后用Requests和Limits控制分配与使用。你只要记住CPU可压缩、内存不可压缩、GPU要整数、带宽别忽略,基本就不会出大问题。
想要更多资料,联系可可