导读:本期聚焦于松本一香创作的《K8s集群中如何实现GPU节点专用与调度限制?多种方案详解》,敬请观看详情。GPU资源昂贵且稀缺,如何避免普通Pod占用GPU节点、如何让训练任务独占整卡或整台GPU机器,是集群管理中绕不开的问题。本文围绕Kubernetes下GPU节点专用与调度限制展开,介绍通过节点标签与污点容忍实现节点隔离,利用节点亲和性与反亲和性控制Pod落点,讲解 extended resource 与 Device Plugin 机制下的显卡分配原理,并给出多卡任务整卡独占、细粒度显存切分等进阶方案与常见踩坑点,帮助你搭建稳定可控的GPU资源池。

GPU服务器的采购成本通常是普通节点的十倍以上,但在没有做任何调度约束的Kubernetes集群里,一个不声明GPU资源的普通Pod完全可能被调度到GPU机器上,白白占掉宝贵的CPU和内存,甚至出现CPU密集型任务把GPU节点塞满的情况。反过来,深度学习训练任务如果没有正确声明显卡需求,也可能被调度到没有GPU的节点上直接启动失败。要解决这些问题,核心思路就是两件事:一是把GPU节点“保护”起来,只允许特定任务进入;二是让GPU任务能够精确声明并独占显卡资源。下面结合实际落地经验,逐一展开几种常用方案。

K8s集群中如何实现GPU节点专用与调度限制?多种方案详解

用标签与污点把GPU节点隔离出来

最基础也是最有效的手段,是给GPU节点打上专属标签,同时配合污点(taint)让默认调度器主动避开这些节点。标签解决的是“我想去哪里”的问题,污点解决的是“谁能进来”的问题,两者配合才能形成完整的闭环。只打标签不加污点,普通Pod依然可能被调度过来;只加污点不打标签,GPU任务也没法明确表达自己的落点偏好。

先给节点打标签和污点:

# 给所有GPU节点打上标签
kubectl label nodes gpu-node-01 hardware=gpu

# 给节点添加污点,key为nvidia.com/gpu,效果为NoSchedule
kubectl taint nodes gpu-node-01 nvidia.com/gpu=true:NoSchedule

污点有三种效果:NoSchedule 表示不容忍就绝不调度;PreferNoSchedule 表示尽量不调度,属于软性约束;NoExecute 则更严格,连已经在节点上运行的Pod如果不容忍该污点也会被驱逐。GPU节点一般建议使用NoSchedule,这样存量任务不会被误杀,但新任务会被严格拦截。

GPU任务这边需要在Pod spec中同时声明容忍度和节点选择器:

apiVersion: v1
kind: Pod
metadata:
  name: train-job
spec:
  tolerations:
  - key: "nvidia.com/gpu"
    operator: "Equal"
    value: "true"
    effect: "NoSchedule"
  nodeSelector:
    hardware: gpu
  containers:
  - name: trainer
    image: nvidia/cuda:12.4.0-base-ubuntu22.04
    resources:
      limits:
        nvidia.com/gpu: 2

这里有个容易被忽略的细节:即使有节点标签,也建议显式声明nvidia.com/gpu的资源请求,否则kubelet不会为容器分配具体显卡,容器内也看不到GPU设备。标签只负责把Pod引到正确的机器,真正的显卡分配还是要靠extended resource的配额机制。

用亲和性实现更灵活的调度控制

nodeSelector的表达能力有限,只能做精确匹配。当集群里存在多种型号的GPU机器,比如A100训练集群、T4推理集群、消费级卡的实验集群时,就需要节点亲和性(nodeAffinity)来支持更复杂的匹配逻辑。它支持In、NotIn、Exists、Gt、Lt等多种操作符,还可以配合preferredDuringSchedulingIgnoredDuringExecution实现优先级式的软约束。

spec:
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
        - matchExpressions:
          - key: gpu-model
            operator: In
            values: ["A100", "H100"]
      preferredDuringSchedulingIgnoredDuringExecution:
      - weight: 80
        preference:
          matchExpressions:
          - key: zone
            operator: In
            values: ["zone-a"]
  tolerations:
  - key: "nvidia.com/gpu"
    operator: "Exists"
    effect: "NoSchedule"

上面的配置表示任务必须在A100或H100节点上运行,同时尽量调度到zone-a机房。注意preferred是软约束,如果zone-a资源不足,调度器会退而求其次,不会让任务卡在Pending状态。这种硬软结合的写法在多机房GPU资源池里非常实用。

除了节点亲和性,还要考虑Pod级别的反亲和性。一个多副本的推理服务如果把所有副本都堆在同一台GPU机器上,一旦机器故障服务就全挂了。通过podAntiAffinity可以让副本尽量打散到不同节点:

spec:
  affinity:
    podAntiAffinity:
      preferredDuringSchedulingIgnoredDuringExecution:
      - weight: 100
        podAffinityTerm:
          labelSelector:
            matchLabels:
              app: gpu-inference
          topologyKey: kubernetes.io/hostname

topologyKey设置为hostname表示打散到不同节点,设置为topology.kubernetes.io/zone则表示打散到不同可用区,可以根据可用性要求灵活选择。

理解Device Plugin与显卡分配的底层机制

Kubernetes本身并不理解GPU,它把显卡当作一种extended resource来管理。真正干活的是NVIDIA提供的Device Plugin,它以DaemonSet形式运行在每个GPU节点上,负责三件事:向kubelet上报节点上的显卡数量、监控显卡健康状态、在Pod调度成功后把具体的设备路径挂载给容器。所以nvidia.com/gpu: 2这个声明能生效的前提,是Device Plugin已经在节点上注册了这个资源名。

可以用下面的命令确认节点上报的GPU数量:

kubectl describe node gpu-node-01 | grep -A 5 "Allocated resources"

默认情况下,GPU分配是整卡粒度的,一个容器声明了1张卡,这张卡在当前节点上就不能再分给其他容器,即使显存只用了一小部分。这种模式简单可靠,避免了显存争抢导致的OOM和性能抖动,但也带来资源利用率低的问题。对于推理这类小显存负载,可以考虑GPU共享方案。

目前主流的共享方案有两类:一是时间片共享,通过NVIDIA的MIG(Multi-Instance GPU)技术把A100、H100这类卡在硬件层面切分成多个隔离实例,每个实例有独立的显存和计算单元,隔离性最好,但只支持少数数据中心卡;二是显存虚拟化方案,例如HAMi或gpushare-scheduler-extender,它们通过改写调度器和挂载逻辑,允许多个Pod分时复用同一张卡,通过声明nvidia.com/gpumem这类自定义资源来申请具体显存量。选择哪种方案,要在隔离性和利用率之间做权衡。

常见踩坑点与落地建议

第一个坑是驱动与运行时版本不匹配。节点上需要提前安装NVIDIA驱动、nvidia-container-toolkit,并把容器运行时默认运行时配置为nvidia,否则Pod能启动但容器内执行nvidia-smi会报错或看不到设备。可以用nvidia-smikubectl logs结合排查。

第二个坑是kubelet的Device Plugin预留机制。如果Pod没有声明GPU资源,kubelet默认会对其隐藏所有显卡设备,这是好事;但有些任务需要通过IPC或者hostPath访问GPU,这类特殊需求要单独评估,不要为了绕过限制随意把宿主机设备路径挂进容器,容易造成资源泄漏。

第三个坑是抢占与优先级。GPU任务排队时间长是常态,建议配合PriorityClass给高优先级训练任务更高权重,并在集群层面通过ResourceQuota按团队划分GPU配额,避免某个业务把整个GPU池吃光。对于批处理任务,还可以引入Volcano或Kueue这类批调度器,获得Gang Scheduling能力,确保分布式训练的所有worker要么一起启动,要么都不启动,避免出现部分Pod占着GPU等待其他伙伴的死锁局面。

总结一下实践路径:标签加污点做节点隔离是地基,亲和性做精细化路由,Device Plugin机制保障整卡分配,共享方案提升利用率,配额与优先级保障公平性。按这个顺序逐步建设,就能得到一个既安全又高效的GPU资源池。

GPU调度Kubernetes节点亲和性修改时间:2026-09-07 02:10:36

免责声明:已尽一切努力确保本网站所含信息的准确性。网站作品多为原创整理与精心创作,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们进行处理Email:chomcom@qq.com。
引用或转载本作品时,请注明当前出处:https://www.ipipp.com/html/20260907/51909.html,基于非商业用途的前提下,欢迎转载或二创本作品。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。