11 | 调度与资源管理:GPU 与 Device-Plugin 机制
系列专栏《深入剖析 Kubernetes》· 基于张磊极客时间专栏思想整理 · 适合 CSDN 发布11 | 调度与资源管理GPU 与 Device-Plugin 机制K8s 调度器kube-scheduler的职责是把 Pod 放到最合适的节点上。但默认调度器只认 CPU/内存这类通用资源对 GPU、FPGA、SR-IOV 网卡这类特殊硬件一无所知。本文讲清资源模型如何落地 cgroups、默认调度器怎么工作、以及 GPU 这类扩展资源如何通过 Device Plugin 接入。一、requests/limits 如何映射 cgroupsPod 里每个容器都能声明两类资源resources:requests:cpu:500m# 请求 0.5 核memory:256Milimits:cpu:1# 上限 1 核memory:512Mirequests请求调度依据。调度器只把 Pod 放进可用allocatable - 已请求资源足够的节点同时也是 kubelet 设置 cgroups最低保障cpu.shares / memory.min的基准。limits限制运行上限。CPU 超限被限流throttle内存超限被OOM Kill。落地到 LinuxcgroupsCPU request →cpu.cfs_quota_us / cpu.cfs_period_us的相对权重cpu.shares保证至少这么多份额。CPU limit →cpu.cfs_quota_us硬上限超了就 throttle。内存 limit →memory.limit_in_bytes超出触发 OOM。内存不设 request 只设 limit 很危险节点内存压力大时该 Pod 可能被优先 OOM。Pod 声明 requests/limits | v kube-scheduler 按 requests 选节点 (装箱) | kubelet 按 limits 写 cgroups v v Node: 可用资源 - 各 Pod requests 本 Pod /sys/fs/cgroup/.../cpu|memory.*二、默认调度器流程过滤 打分调度分两阶段在 1.15 后框架重构为scheduling frameworkpredicates/priorities被泛化为各扩展点但思想一致过滤Filter / 原 Predicates剔除根本不能放的节点。包含一组General PredicatesPodFitsResources资源够不够、PodFitsHostnodeName 匹配、PodFitsHostPorts端口不冲突、MatchNodeSelector节点选择器、CheckNodeMemoryPressure等。打分Score / 原 Priorities对通过过滤的节点评分择优。典型如LeastRequestedPriority越空越高分均衡分散、BalancedResourceAllocationCPU/内存使用均衡、ImageLocalityPriority节点已有镜像则加分加速启动。绑定Bind把选中的节点写回 Pod 的spec.nodeName。待调度 Pod | v -------------- 过滤(General Predicates) | Filter 阶段 | ------- 剔除: 资源不足/端口冲突/selector不符 的节点 -------------- | v -------------- 打分(Score) | Score 阶段 | ------- 越空闲/越均衡 得分越高 -------------- | v 选中最高分节点 -- Bind三、为什么默认调度器只按数量不按属性注意一个关键局限默认调度器判断 GPU 节点时只看还有几张卡不看是哪张卡、卡的型号/算力/拓扑。它把 GPU 当成整数资源如nvidia.com/gpu: 1来减库存。这带来几个真实痛点无法感知NUMA / PCIe 拓扑两张卡若跨 NUMA 或跨交换机NVLink 无法互联多卡训练带宽骤降。无法感知MIGMulti-Instance GPU切分或混合型号A100/V100 混部时可能被分到性能不一致的卡。无法做亲和/反亲和于特定卡如要求同机组卡做 NCCL 通信。所以AI 训练任务 GPU 调度异常往往不是卡没了而是拓扑/型号不匹配导致训练效率低下或 NCCL 建链失败而默认调度器对此视而不见。解决方向通常是专门的 GPU 调度器如 volcano、kube-batch或设备插件扩展如 GPU topology-aware scheduling。四、Extended Resource 与 Device Plugin 机制要让 K8s “认识” GPU思路是把它登记为一种扩展资源Extended Resource名字带域名前缀、值必须为整数例如nvidia.com/gpu。节点上的 kubelet 通过Device Plugin机制把这些设备上报给 API Server调度器就能像数 CPU 一样数 GPU。Device Plugin 工作流程遵循device-plugingRPC 接口插件启动并注册设备插件如nvidia-device-plugin以 DaemonSet 部署在每个 GPU 节点向本节点 kubelet 的Register接口登记自己管哪类设备。ListAndWatch 上报插件持续ListAndWatch把可用设备列表如 GPU-0、GPU-1 及其健康状态推送给 kubeletkubelet 据此把nvidia.com/gpu: 2写进节点的status.allocatable。调度与分配Pod 声明nvidia.com/gpu: 1后调度器按可用数把它放到有卡的节点节点 kubelet 在真正起容器前调用插件的Allocate插件返回设备路径如/dev/nvidia0、环境变量、挂载点kubelet 据此把 GPU 设备注入容器训练进程才能cudaGetDeviceCount()看到卡。---------- Register ----------- ListAndWatch ------------ | kubelet |-------------| Device |-----------------| 节点 GPU 资源| | (节点) | | Plugin | 上报可用设备 | allocatable | --------- |(DaemonSet)| ------------ | ----------- | 容器启动前 Allocate | 返回设备路径/挂载 v ---------- | 容器看到 | /dev/nvidia0, 环境变量 NVIDIA_VISIBLE_DEVICES | GPU 设备 | ----------Pod 使用 GPU 的写法apiVersion:v1kind:Podmetadata:name:cuda-trainspec:containers:-name:trainerimage:nvidia/cuda:12.2-runtimecommand:[nvidia-smi]resources:limits:nvidia.com/gpu:1# 扩展资源申请 1 张 GPU# 注意GPU 目前只能放在 limits不能只 request注意GPU 是不可压缩的独占资源目前只能写在limits且必须等于requests意味着申请几张就独占几张不会超卖。五、AI 训练任务 GPU 调度异常的常见原因结合上述机制排障清单节点没装 Device Plugin / 插件 Crashkubectl describe node看不到nvidia.com/gpuPod 永远 Pending。GPU 被其他 Pod 占满nvidia.com/gpu是整数减库存申请 2 张但该节点只剩 1 张 → Pending。多卡跨 NUMA/拓扑不佳默认调度器不感知训练 NCCL 慢甚至超时——需用拓扑感知调度或 nodeSelector/亲和锁定机组。limits 没写 GPU容器起得来但nvidia-smi看不到卡训练直接报错找不到设备。驱动/CUDA 版本不匹配节点驱动版本低于镜像要求容器无法初始化 CUDA。六、Custom Metrics 驱动 HPA资源用量除了静态 requests/limits还能自动扩缩HPAHorizontal Pod Autoscaler根据指标调整副本数。默认 HPA 只看cpu/memory来自 metrics-server。但要按自定义指标如 QPS、队列长度、GPU 利用率扩缩需要Custom Metrics API——由 Prometheus Adapter 等组件把业务指标暴露成 K8s 可查询的 APIHPA 据此伸缩。apiVersion:autoscaling/v2kind:HorizontalPodAutoscalermetadata:name:api-hpaspec:scaleTargetRef:apiVersion:apps/v1kind:Deploymentname:apiminReplicas:2maxReplicas:10metrics:-type:Resourceresource:name:cputarget:type:UtilizationaverageUtilization:70# CPU 平均利用率 70% 触发扩缩-type:Pods# 自定义指标示例pods:metric:name:http_requests_per_sectarget:type:AverageValueaverageValue:100# 每 Pod 100 QPS小结 / 核心要点requests是调度与 cgroups 最低保障依据limits是 cgroups 硬上限CPU 限流、内存 OOM。默认调度器两阶段过滤General Predicates 剔除不合格节点 打分Score 择优 绑定本质按数量装箱。默认调度器不感知 GPU 拓扑/型号只数整数张数这是 AI 训练效率异常的根因之一。GPU 经Extended Resourcenvidia.com/gpuDevice Plugin接入ListAndWatch 上报 → Allocate 注入设备到容器。GPU 只能放 limits 且不可超卖。自动扩缩靠 HPAcpu/memory用 metrics-server业务指标需 Custom Metrics API如 Prometheus Adapter支撑。