本节介绍 Kubernetes 如何表示、请求、分配和约束工作负载所消耗的资源, 包括专用硬件设备。
资源管理
- 1: 资源管理器
- 2: 动态资源分配
- 2.1: DRA API 对象
- 2.2: DRA 的工作原理
- 2.3: 动态资源的可观测性
- 2.4: DRA 特性
- 2.5: 设备污点与容忍度
- 3: Pod 级资源管理器
1 - 资源管理器
为了支持对延迟敏感和高吞吐量的工作负载,Kubernetes 提供了一组资源管理器。 这些管理器旨在协调并优化节点资源的对齐, 以满足对 CPU、设备和内存(巨页)资源有特定需求的 Pod 的配置要求。
拓扑管理器
This is a stable feature in Kubernetes, and has been since the 1.27 release. You can no longer toggle this feature (the associated feature gate has been removed).
拓扑管理器(Topology Manager)是一个 kubelet 组件,旨在协调负责这些优化的组件集。 要了解更多信息,请阅读控制节点上的拓扑管理策略。
CPU 管理器
This is a stable feature in Kubernetes, and has been since the 1.26 release. You can no longer toggle this feature (the associated feature gate has been removed).
CPU 管理器(CPU Manager)是一个 kubelet 组件,为 CPU 资源提供独占的资源分配。 它会与拓扑管理器协商,以做出资源分配决策。 要了解更多信息,请阅读控制节点上的 CPU 管理策略。
为 Pod 分配 CPU 的策略
一旦 Pod 绑定到某个节点,该节点上的 kubelet 可能需要多路复用现有硬件 (例如在多个 Pod 之间共享 CPU),或者通过专门划出一部分资源来分配硬件 (例如为某个 Pod 的独占使用分配一个或多个 CPU)。
默认情况下,kubelet 使用 CFS 配额 来强制执行 Pod 的 CPU 限制。当节点上运行许多 CPU 密集型 Pod 时, 工作负载可能会迁移到不同的 CPU 核,具体取决于 Pod 是否被节流以及调度时哪些 CPU 核可用。许多工作负载对这种迁移并不敏感, 因此在没有任何干预的情况下也能正常工作。
但是,对于 CPU 缓存亲和性和调度延迟会显著影响性能的工作负载, kubelet 允许使用其他 CPU 管理策略来确定节点上的一些放置偏好。 这是通过 CPU 管理器及其策略实现的。有两种可用的策略:
none:none策略显式启用现有的默认 CPU 亲和性方案, 除了操作系统调度器自动完成的操作外不提供任何亲和性。 Guaranteed Pod 和 Burstable Pod 的 CPU 使用限制通过 CFS 配额来强制执行。static:static策略允许GuaranteedPod 中具有整数 CPUrequests的容器访问节点上的独占 CPU。这种独占性是使用 CPU 集 cgroup 控制器来强制保证的。
说明:
诸如容器运行时和 kubelet 本身之类的系统服务可以继续在这些独占 CPU 上运行。独占性只延伸到其他 Pod。
CPU 管理器不支持在运行时离线或上线 CPU。
静态策略
静态策略可实现更精细的 CPU 管理和独占 CPU 分配。
此策略管理一个共享 CPU 池,该池最初包含节点中的所有 CPU。
可独占分配的 CPU 数量等于节点中的 CPU 总数减去 kubelet 配置所设置的所有 CPU 预留量。
通过这些选项预留的 CPU 会以整数数量按物理核心 ID 的升序从初始共享池中取用。
此共享池是 BestEffort 和 Burstable Pod 中任何容器运行所在的 CPU 集合。
CPU requests 为小数的 Guaranteed Pod 中的容器也在共享池中的 CPU 上运行。
只有属于 Guaranteed Pod 且具有整数 CPU requests 的容器才会被分配独占 CPU。
说明:
当启用静态策略时,kubelet 要求 CPU 预留量大于零。这是因为预留量零会让共享池变为空。
当容器满足静态分配要求的 Guaranteed Pod 被调度到节点时,CPU 会从共享池中移除并放入容器的 CPU 集中。
不会使用 CFS 配额来限制这些容器的 CPU 使用量,因为它们的使用量由调度域本身限制。
换句话说,容器 CPU 集中的 CPU 数量等于 Pod 规约中指定的整数 CPU limit。
这种静态分配会提高 CPU 亲和性,并减少 CPU 密集型工作负载因节流而导致的上下文切换。
考虑以下 Pod 规约中的容器:
spec:
containers:
- name: nginx
image: nginx
上面的 Pod 以 BestEffort QoS 类运行,因为它没有指定资源 requests 或 limits。它在共享池中运行。
spec:
containers:
- name: nginx
image: nginx
resources:
limits:
memory: "200Mi"
requests:
memory: "100Mi"
上面的 Pod 以 Burstable QoS 类运行,因为资源 requests 不等于 limits,
并且没有指定 cpu 数量。它在共享池中运行。
spec:
containers:
- name: nginx
image: nginx
resources:
limits:
memory: "200Mi"
cpu: "2"
requests:
memory: "100Mi"
cpu: "1"
上面的 Pod 以 Burstable QoS 类运行,因为资源 requests 不等于 limits。它在共享池中运行。
spec:
containers:
- name: nginx
image: nginx
resources:
limits:
memory: "200Mi"
cpu: "2"
requests:
memory: "200Mi"
cpu: "2"
上面的 Pod 以 Guaranteed QoS 类运行,因为 requests 等于 limits。
且该容器对 CPU 资源的资源限制是大于或等于 1 的整数。nginx 容器被授予 2 个独占 CPU。
spec:
containers:
- name: nginx
image: nginx
resources:
limits:
memory: "200Mi"
cpu: "1.5"
requests:
memory: "200Mi"
cpu: "1.5"
上面的 Pod 以 Guaranteed QoS 类运行,因为 requests 等于 limits。
但该容器对 CPU 资源的资源限制是一个小数。它在共享池中运行。
spec:
containers:
- name: nginx
image: nginx
resources:
limits:
memory: "200Mi"
cpu: "2"
上面的 Pod 以 Guaranteed QoS 类运行,因为只指定了 limits,
并且在未显式指定时 requests 会被设置为等于 limits。
且该容器对 CPU 资源的资源限制是大于或等于 1 的整数。
nginx 容器被授予 2 个独占 CPU。
静态策略选项
以下是静态 CPU 管理策略可用的策略选项,按字母顺序列出:
align-by-socket(Alpha,默认隐藏)- 按物理封装/插槽边界对齐 CPU,而不是按逻辑 NUMA 边界对齐 (自 Kubernetes v1.25 起可用)
distribute-cpus-across-cores(Alpha,默认隐藏)- 跨不同的物理核心分配虚拟核心(有时称为硬件线程) (自 Kubernetes v1.31 起可用)
distribute-cpus-across-numa(Beta,默认可见)- 跨不同的 NUMA 域分散 CPU,力求在所选域之间实现均衡 (自 Kubernetes v1.23 起可用)
full-pcpus-only(GA,默认可见)- 始终分配完整的物理核心 (自 Kubernetes v1.22 起可用,自 Kubernetes v1.33 起进阶到 GA)
strict-cpu-reservation(GA,默认可见)- 阻止所有 Pod(无论其服务质量类别如何)在预留的 CPU 上运行 (自 Kubernetes v1.32 起可用,自 Kubernetes v1.35 起进阶到 GA)
prefer-align-cpus-by-uncorecache(GA,默认可见)- 以尽力而为的方式按 uncore(最后一级)缓存边界对齐 CPU (自 Kubernetes v1.32 起可用)
你可以使用以下特性门控,根据选项组的成熟度级别来启用或禁用这些选项:
CPUManagerPolicyBetaOptions(默认启用)。禁用可隐藏 Beta 级选项。CPUManagerPolicyAlphaOptions(默认禁用)。启用可显示 Alpha 级选项。
你仍然必须使用 kubelet 配置文件中的 cpuManagerPolicyOptions 字段启用各个选项。
有关可以配置的各个选项的更多详细信息,请继续阅读。
full-pcpus-only
如果指定了 full-pcpus-only 策略选项,静态策略将始终分配完整的物理核心。
默认情况下,若不使用此选项,静态策略会使用拓扑感知的最佳匹配分配来分配 CPU。
在启用了 SMT 的系统上,该策略可以分配与硬件线程对应的各个虚拟核心。
这可能导致不同的容器共享相同的物理核心;
这种行为又会引发吵闹的邻居问题。
启用该选项后,只有当某个 Pod 的所有容器的 CPU 请求都能通过分配完整的物理核心得到满足时,kubelet 才会接受该 Pod。
如果该 Pod 未通过准入,它会被置为 Failed 状态,并带有 SMTAlignmentError 消息。
distribute-cpus-across-numa
如果指定了 distribute-cpus-across-numa 策略选项,
则在需要多个 NUMA 节点才能满足分配的情况下,静态策略会跨 NUMA 节点均匀分配 CPU。
默认情况下,CPUManager 会将 CPU 打包到一个 NUMA 节点上直到填满,剩余的 CPU 会直接溢出到下一个 NUMA 节点。
这可能会给依赖屏障(及类似同步原语)的并行代码带来不必要的瓶颈,
因为这类代码的执行速度往往只取决于其最慢的工作线程(而至少有一个 NUMA 节点上可用 CPU 较少会拖慢该线程)。
通过在多个 NUMA 节点之间均匀分配 CPU,应用开发者可以更容易地确保没有单个工作线程比其他线程受到更严重的
NUMA 影响,从而提升这类应用的整体性能。
align-by-socket
如果指定了 align-by-socket 策略选项,则在决定如何将 CPU 分配给容器时,
CPU 会被视为以插槽为边界对齐。默认情况下,CPUManager 在 NUMA 边界处对齐 CPU 分配,
如果需要从多个 NUMA 节点拉取 CPU 才能满足分配,这可能会导致性能下降。
尽管它试图确保所有 CPU 都从最少数量的 NUMA 节点中分配,但无法保证这些 NUMA 节点位于同一个插槽上。
通过指示 CPUManager 显式地以插槽边界而不是 NUMA 边界对齐 CPU,
我们可以避免此类问题。注意,此策略选项与 TopologyManager 的 single-numa-node 策略
不兼容,并且不适用于插槽数量大于 NUMA 节点数量的硬件。
distribute-cpus-across-cores
如果指定了 distribute-cpus-across-cores 策略选项,静态策略会尝试跨不同的物理核心分配虚拟核心(硬件线程)。
默认情况下,CPUManager 倾向于将 CPU 打包到尽可能少的物理核心上,这可能导致同一物理核心上的 CPU 之间发生争用,造成性能瓶颈。
通过启用 distribute-cpus-across-cores 策略,静态策略可确保 CPU 尽可能分散到更多的物理核心上,
减少同一物理核心上的争用,从而提高整体性能。但是,需要注意的是,当系统负载很重时,此策略的效果可能较差。
在这种情况下,减少争用带来的好处会减弱。相反,
默认行为有助于减少核心间通信开销,在高负载条件下可能提供更好的性能。
strict-cpu-reservation
KubeletConfiguration 中的
reservedSystemCPUs 参数,或已弃用的 kubelet 命令行选项 --reserved-cpus,
为 OS 系统守护进程和 Kubernetes 系统守护进程定义了一个显式的 CPU 集合。有关此参数的更多详细信息,
请参见显式预留的 CPU 列表页面。
默认情况下,这种隔离仅针对具有整数 CPU 请求的 Guaranteed Pod 实现,
而不适用于 Burstable 和 BestEffort Pod(以及具有小数 CPU 请求的 Guaranteed Pod)。
准入只会将 CPU 请求与可分配的 CPU 进行比较。
由于 CPU 限制高于请求,默认行为允许 Burstable 和 BestEffort Pod 占满
reservedSystemCPUs 的容量,从而在实际部署中导致主机 OS 服务资源匮乏。
如果启用了 strict-cpu-reservation 策略选项,
静态策略将不允许任何工作负载使用 reservedSystemCPUs 中指定的 CPU 核心。
prefer-align-cpus-by-uncorecache
如果指定了 prefer-align-cpus-by-uncorecache 策略,静态策略会为各个容器分配 CPU 资源,
使分配给某个容器的所有 CPU 共享同一个 uncore 缓存块(也称为最后一级缓存或 LLC)。
默认情况下,CPUManager 会紧密打包 CPU 分配,这可能导致分配给容器的 CPU 来自多个 uncore 缓存。
此选项使 CPUManager 能够以最大化 uncore 缓存利用效率的方式分配 CPU。
分配以尽力而为的方式进行,目的是让尽可能多的 CPU 归属同一个 uncore 缓存。
如果容器的 CPU 需求超过单个 uncore 缓存的 CPU 容量,
CPUManager 会尽量减少所使用的 uncore 缓存数量,以保持最佳的 uncore 缓存对齐。
特定工作负载可以从降低缓存间延迟和缓存层面的吵闹邻居影响中获益。
如果节点资源充足而 CPUManager 无法最佳对齐,容器仍会使用默认的紧密打包行为被接受。
内存管理器
More information about this feature
This is a stable feature in Kubernetes, and has been since version 1.32. It was first available in the v1.21 release.
内存管理器(Memory Manager)是一个 kubelet 组件,为内存资源提供独占的资源分配。 它会与拓扑管理器协商,以做出资源分配决策。 要了解更多信息,请阅读控制节点上的内存管理策略。
为 Pod 分配内存的策略
Kubernetes 内存管理器为 Guaranteed
QoS 类中的 Pod
分配 RAM(内存,以及可选的 Linux 巨页)资源。
内存管理器采用提示生成协议,为 Pod 生成最合适的 NUMA 亲和性。 内存管理器将这些亲和性提示提供给中央管理器(拓扑管理器)。 根据这些提示和拓扑管理器的策略,Pod 会被拒绝或接入节点。
此外,内存管理器确保 Pod 请求的内存从最少数量的 NUMA 节点中分配。
要了解更多信息,请阅读控制节点上的内存管理策略。
设备管理器
设备管理器(Device Manager)是一个 kubelet 组件,使用设备插件 API 将硬件设备分配给 Pod。 它会与拓扑管理器协商,利用设备插件提供的拓扑信息来做出资源分配决策。要了解更多信息, 请阅读设备插件与拓扑管理器的集成。
Pod 级资源管理器
More information about this feature
To use this feature, you (or a cluster administrator) will need to enable the PodLevelResourceManagers feature gate for all relevant components in your cluster.
See Enable Or Disable Feature Gates for more information.
对 kubelet 资源管理器(拓扑、CPU 和内存)的 Pod 级资源支持,
允许资源管理器直接使用 .spec.resources 做出 NUMA 对齐和独占分配决策。
要了解更多信息,请参阅专门的 Pod 级资源管理器概念页面, 或阅读如何分配 Pod 级 CPU 和内存资源。
接下来
2 - 动态资源分配
More information about this feature
This is a stable feature in Kubernetes, and has been since version v1.35. It was first available in the v1.30 release. You can no longer disable or opt out of this feature or behavior (it is locked); if you explicitly set a value for the associated feature gate DynamicResourceAllocation, Kubernetes ignores it but does not report any error.
本节介绍 Kubernetes 中的动态资源分配(Dynamic Resource Allocation,DRA)。
DRA 是 kubernetes 提供的一项特性,允许你在多个 Pod 之间请求和共享资源。
这些资源通常是挂接的设备,例如硬件加速器。
借助 DRA,设备驱动和集群管理员可以定义设备的类别,这些类别可供工作负载中的 Pod 申领。 Kubernetes 会将匹配的设备分配给特定的申领,并将相应的 Pod 调度到能够访问这些已分配设备的节点上。
使用 DRA 分配资源提供了与动态卷制备类似的体验: 你使用 PersistentVolumeClaims 从存储类中申领存储容量, 并在 Pod 中请求使用该已申领的容量。
DRA 的优势
DRA 提供了一种灵活的方式来分类、请求和使用集群中的设备。 使用 DRA 具有以下优势:
- 灵活的设备过滤: 使用公共表达式语言(CEL)对特定设备属性进行细粒度过滤。
- 设备共享: 通过引用相应的资源申领,与多个容器或 Pod 共享同一资源。
- 设备配置: 将特定于供应商的设备配置附加到你的资源申领, 启用按工作负载的设备配置,而非如今的按节点设备配置。
- 集中式设备分类: 设备驱动和集群管理员可以使用 DeviceClass 为应用运维人员提供针对各种用例优化的硬件类别。 例如,你可以为通用工作负载创建成本优化的 DeviceClass, 为关键作业创建高性能的 DeviceClass。
- 简化的 Pod 请求: 通过 DRA,应用运维人员无需在 Pod 资源请求中指定设备数量。 相反,Pod 引用一个资源申领,该申领中的设备配置将应用于该 Pod。
与需要按容器请求设备、不支持设备共享且不支持基于表达式的设备过滤的 设备插件相比, 这些优势在设备分配工作流方面提供了显著改进。
DRA 用户类型
使用 DRA 分配设备的工作流涉及以下类型的用户:
-
设备所有者(Device owner): 负责设备。设备所有者可以是商业供应商、集群运维人员或其他实体。 要使用 DRA,设备必须具备兼容 DRA 的驱动,这些驱动需要完成以下工作:
- 创建 ResourceSlice,为 Kubernetes 提供关于节点和资源的信息。
- 当集群中的资源容量发生变化时更新 ResourceSlice。
- 根据申领配置设备,并通过容器设备接口(Container Device Interface,CDI) 将它们附加到容器。
- 可选地,创建工作负载运维人员可用于申领设备的 DeviceClass。
-
集群管理员(Cluster admin): 负责配置集群和节点、附加设备、安装驱动等类似任务。 要使用 DRA,集群管理员需要完成以下工作:
- 将设备附加到节点。
- 安装支持 DRA 的设备驱动。
- 可选地,创建工作负载运维人员可用于申领设备的 DeviceClass。
-
工作负载运维人员(Workload operator): 负责在集群中部署和管理工作负载。 要使用 DRA 为 Pod 分配设备,工作负载运维人员需要完成以下工作:
- 创建 ResourceClaim 或 ResourceClaimTemplate,以在 DeviceClass 中请求特定配置。
- 部署使用特定 ResourceClaim 或 ResourceClaimTemplate 的工作负载。
限制
- Kubernetes 调度器不支持为 DRA 资源进行 抢占。 这意味着在节点上运行并使用 DRA 资源的现有 Pod, 不能被同样需要 DRA 资源的更高优先级 Pod 抢占。 高优先级 Pod 将保持挂起状态,直到设备可用 —— 这发生在冲突 Pod 终止或被手动删除时。
接下来
- 在集群中设置 DRA
- 使用 DRA 为工作负载分配设备
- 访问 DRA 设备元数据
- 有关设计的更多信息,请参阅 使用结构化参数的动态资源分配 KEP。
2.1 - DRA API 对象
本页介绍 Kubernetes API 种类,这些种类由动态资源分配(DRA)用于对设备进行分类、请求和分配。
DRA 术语
DRA 使用以下 Kubernetes API 种类来提供核心分配功能。
所有这些 API 种类都包含在 resource.k8s.io/v1
API 组中。
- DeviceClass
- 定义了可以被申领的设备类别,以及如何在申领中选择特定设备属性。 DeviceClass 参数可以匹配 ResourceSlice 中的零个或多个设备。 若要从 DeviceClass 中申领设备,ResourceClaim 需要选择特定的设备属性。
- ResourceClaim
- 描述了访问集群中已挂接资源(例如设备)的请求。 ResourceClaim 为 Pod 提供对特定资源的访问。 ResourceClaim 可以由工作负载运维人员创建, 也可以由 Kubernetes 基于 ResourceClaimTemplate 生成。
- ResourceClaimTemplate
- 定义一个模板,Kubernetes 使用该模板为工作负载创建每个 Pod 的 ResourceClaim。 ResourceClaimTemplate 为 Pod 提供对独立的、配置相似的资源的访问。 Kubernetes 从此模板生成的每个 ResourceClaim 都会绑定到一个特定的 Pod。 当 Pod 终止时,Kubernetes 删除对应的 ResourceClaim。
- ResourceSlice
- 代表挂接到节点上的一个或多个资源,例如设备。 驱动在集群中创建和管理 ResourceSlice。当 ResourceClaim 被创建并在 Pod 中使用时, Kubernetes 使用 ResourceSlice 查找能够访问已申领资源的节点。 Kubernetes 将资源分配给 ResourceClaim,并将 Pod 调度到可以访问这些资源的节点上。
DeviceClass
DeviceClass 允许集群管理员或设备驱动在集群中定义设备的类别。 DeviceClass 告知运维人员他们可以请求哪些设备,以及如何请求这些设备。 你可以使用公共表达式语言(CEL)根据特定属性选择设备。 引用 DeviceClass 的 ResourceClaim 随后可以请求 DeviceClass 中的特定配置。
要创建 DeviceClass, 请参阅在集群中设置 DRA。
ResourceClaim 与 ResourceClaimTemplate
ResourceClaim 定义了工作负载所需的资源。每个 ResourceClaim 都包含 requests,它们引用一个 DeviceClass 并从该 DeviceClass 中选择设备。 ResourceClaim 还可以使用 selectors 过滤符合特定需求的设备, 并可以使用 constraints 限制能够满足请求的设备。 ResourceClaim 可以由工作负载运维人员创建, 也可以由 Kubernetes 基于 ResourceClaimTemplate 生成。 ResourceClaimTemplate 定义了一个模板,Kubernetes 可以使用该模板为 Pod 自动生成 ResourceClaim。
ResourceClaim 与 ResourceClaimTemplate 的使用场景
你使用的方法取决于你的需求,如下所述:
- ResourceClaim: 你希望多个 Pod 共享对特定设备的访问。 你手动管理你创建的 ResourceClaim 的生命周期。
- ResourceClaimTemplate: 你希望 Pod 能够独立访问独立的、配置相似的设备。Kubernetes 根据 ResourceClaimTemplate 中的规约生成 ResourceClaim。每个生成的 ResourceClaim 的生存期都绑定到对应 Pod 的生存期。
- PodGroup ResourceClaimTemplate:
你希望 PodGroup
能够独立访问独立的、配置相似的设备,且其 Pod 可以共享这些设备。
Kubernetes 根据 ResourceClaimTemplate 中的规约为 PodGroup 生成一个
ResourceClaim。每个生成的 ResourceClaim 的生存期都绑定到对应 PodGroup
的生存期。这要求启用
DRAWorkloadResourceClaims特性。
当你定义工作负载时,可以使用 公共表达式语言(CEL) 来过滤特定的设备属性或容量。可用于过滤的可用参数取决于设备和驱动。
如果你在 Pod 中直接引用特定的 ResourceClaim,该 ResourceClaim 必须已经存在于与 Pod 相同的名字空间中。如果 ResourceClaim 不存在于该名字空间中, Pod 将无法调度。此行为类似于 PersistentVolumeClaim 必须存在于引用它的 Pod 所在的同一名字空间中。
你可以在 Pod 中引用自动生成的 ResourceClaim,但这并不推荐, 因为自动生成的 ResourceClaim 的生存期绑定到触发生成的 Pod 或 PodGroup 的生存期。
要了解如何使用这些方法之一申领资源, 请参阅使用 DRA 为工作负载分配设备。
优先级列表
More information about this feature
This is a stable feature in Kubernetes, and has been since version 1.36. It was first available in the v1.33 release.
你可以为 ResourceClaim 或 ResourceClaimTemplate 中的请求提供子请求的优先级列表。 调度器会选择第一个可以被分配的子请求。 这允许用户指定在首选方案不可用时工作负载可以使用的备选设备。
在以下示例中,ResourceClaimTemplate 请求了一台颜色为黑色、尺寸为大号的设备。 如果没有具有这些属性的设备可用,Pod 将无法被调度。 借助优先级列表特性,可以指定第二个备选方案,即请求两台颜色为白色、尺寸为小号的设备。 如果大号黑色设备可用,将分配它;如果不可用,但有两台小号白色设备可用,Pod 仍然可以运行。
apiVersion: resource.k8s.io/v1
kind: ResourceClaimTemplate
metadata:
name: prioritized-list-claim-template
spec:
spec:
devices:
requests:
- name: req-0
firstAvailable:
- name: large-black
deviceClassName: resource.example.com
selectors:
- cel:
expression: |-
device.attributes["resource-driver.example.com"].color == "black" &&
device.attributes["resource-driver.example.com"].size == "large"
- name: small-white
deviceClassName: resource.example.com
selectors:
- cel:
expression: |-
device.attributes["resource-driver.example.com"].color == "white" &&
device.attributes["resource-driver.example.com"].size == "small"
count: 2
如果 Pod 适合在集群中的多个节点上运行,调度器在为每个节点打分时, 会将从任何优先级列表中选中的子请求的索引作为输入之一。 因此,能够分配更高级子请求中所请求设备的节点, 比只能分配较低级子请求设备的节点更有可能被选中。
该决策是按每个 Pod 单独作出的,因此如果 Pod 是 ReplicaSet 或类似分组的成员, 你不能依赖该组的所有成员都选用相同的子请求。你的工作负载必须能适应这一点。
工作负载 ResourceClaim
More information about this feature
To use this feature, you (or a cluster administrator) will need to enable the DRAWorkloadResourceClaims feature gate for all relevant components in your cluster.
See Enable Or Disable Feature Gates for more information.
当你使用 Workload API 来组织 Pod 时,你可以为整个 PodGroup 预留 ResourceClaim,而不是为每个 Pod 单独预留; 并为 PodGroup 而非单个 Pod 生成 ResourceClaimTemplate, 从而允许 PodGroup 内的 Pod 共享对生成的 ResourceClaim 所分配设备的访问权。
此特性针对两个问题:
- ResourceClaim API 的
status.reservedFor列表只能包含 256 项。 由于 kube-scheduler 仅在该列表中记录单个 Pod, 因此只能有 256 个 Pod 共享一个 ResourceClaim。 通过允许将 PodGroup 记录在status.reservedFor中, 远多于 256 个 Pod 可以共享一个 ResourceClaim。
- 只有当 ResourceClaim 的确切名称已知时,Pod 才能共享它。 对于复制 Pod 组的复杂工作负载,当组的集合扩缩容时, 每组中 Pod 共享的 ResourceClaim 都需要显式创建和删除。 通过为每个 PodGroup 生成 ResourceClaim, 单个 ResourceClaimTemplate 可以作为既被自动复制、又能在 PodGroup 内的 Pod 之间共享的 ResourceClaim 的基础。
PodGroup API 定义了一个 spec.resourceClaims 字段,其结构与 Pod API 中的
spec.resourceClaims 字段相同,含义也类似:
apiVersion: scheduling.k8s.io/v1beta1
kind: PodGroup
metadata:
name: training-group
namespace: some-ns
spec:
...
resourceClaims:
- name: pg-claim
resourceClaimName: my-pg-claim
- name: pg-claim-template
resourceClaimTemplateName: my-pg-template
与 Pod 提出的申领类似,定义了 resourceClaimName 的 PodGroup
申领按名称引用一个 ResourceClaim。定义了 resourceClaimTemplateName
的申领引用一个 ResourceClaimTemplate,该模板会复制为整个 PodGroup 的一个
ResourceClaim,供其 Pod 之间共享。
当 Pod 定义的申领的 name、resourceClaimName 和
resourceClaimTemplateName 全部与其 PodGroup 的某个
spec.resourceClaims 匹配时,kube-scheduler 会为 PodGroup
而非 Pod 预留该 ResourceClaim。如果 Pod 的申领与 PodGroup 的申领不匹配,
kube-scheduler 则为 Pod 预留 ResourceClaim。在这两种情况下,
预留都会记录在 ResourceClaim 的 status.reservedFor 中。
PodGroup 预留以及对应的资源分配会在 ResourceClaim 中持久存在,
直到 PodGroup 被删除,即使该组中不再有任何 Pod。
当匹配 PodGroup 申领的 Pod 申领定义了 resourceClaimTemplateName 时,
会为 PodGroup 生成一个 ResourceClaim。组中定义了相同申领的其他 Pod
将共享该生成的 ResourceClaim,而不会为每个 Pod 触发新的 ResourceClaim 生成。
无论 resourceClaimTemplateName 申领是否匹配 PodGroup 申领,
生成的 ResourceClaim 的名称都会记录在 Pod 的 status.resourceClaimStatuses 中。
只有当
DRAWorkloadResourceClaims
特性启用时,匹配的 PodGroup 申领才会触发 ResourceClaimTemplate 创建 ResourceClaim。
在该特性禁用时,不会创建 ResourceClaim,以避免在集群升级或 kube-apiserver 与
kube-controller-manager 之间进行特性滚动/回滚期间产生虚假的按 Pod 分配的
ResourceClaim。
从 ResourceClaimTemplate 为 PodGroup 生成的 ResourceClaim 遵循 PodGroup 的生命周期。当 PodGroup 及其 ResourceClaimTemplate 都存在时,才会首次创建 ResourceClaim。在 PodGroup 已被删除且 ResourceClaim 不再被预留后, ResourceClaim 会被删除。
请看以下示例:
apiVersion: scheduling.k8s.io/v1beta1
kind: PodGroup
metadata:
name: training-group
namespace: some-ns
spec:
...
resourceClaims:
- name: pg-claim
resourceClaimName: my-pg-claim
- name: pg-claim-template
resourceClaimTemplateName: my-pg-template
---
apiVersion: v1
kind: Pod
metadata:
name: training-group-pod-1
namespace: some-ns
spec:
...
schedulingGroup:
podGroupName: training-group
resourceClaims:
- name: pod-claim
resourceClaimName: my-pod-claim
- name: pod-claim-template
resourceClaimTemplateName: my-pod-template
- name: pg-claim
resourceClaimName: my-pg-claim
- name: pg-claim-template
resourceClaimTemplateName: my-pg-template
在此示例中,training-group PodGroup 有一个名为 training-group-pod-1 的 Pod。
Pod 的 pod-claim 和 pod-claim-template 申领不匹配 PodGroup 的任何申领,
因此这些申领不受 PodGroup 的影响:ResourceClaim my-pod-claim 变为为 Pod 预留,
并且从 ResourceClaimTemplate my-pod-template 生成一个 ResourceClaim,
同样变为为 Pod 预留。pg-claim 和 pg-claim-template 确实匹配 PodGroup
的申领。ResourceClaim my-pg-claim 变为为 PodGroup 预留,
并且从 ResourceClaimTemplate my-pg-template 生成一个 ResourceClaim,
同样变为为 PodGroup 预留。
将 ResourceClaim 与 Workload API 资源关联由
kube-apiserver、kube-controller-manager、kube-scheduler 和 kubelet 中的
DRAWorkloadResourceClaims 特性门控控制。
ResourceSlice
每个 ResourceSlice 代表一个池中一个或多个设备。 该池由设备驱动管理,驱动会创建和管理 ResourceSlice。 池中的资源可能由单个 ResourceSlice 表示,也可能跨越多个 ResourceSlice。
ResourceSlice 向设备使用者和调度器提供有用的信息, 并且对于动态资源分配至关重要。每个 ResourceSlice 必须包含以下信息:
- 资源池(Resource pool): 驱动管理的一个或多个资源的组。 池可以跨越多个 ResourceSlice。池中资源的变更必须在该池的所有 ResourceSlice 之间传播。管理该池的设备驱动负责确保此传播发生。
- 设备(Devices): 被管理的池中的设备。 ResourceSlice 可以列出池中的每个设备,也可以列出池中的设备子集。 ResourceSlice 定义设备信息,例如属性、版本和容量。 设备使用者可以通过在 ResourceClaim 或 DeviceClass 中过滤设备信息来选择要分配的设备。
- 节点(Nodes): 可以访问这些资源的节点。 驱动可以选择哪些节点可以访问资源, 是集群中的所有节点、单个指定名称的节点,还是具有特定节点标签的节点。
驱动使用控制器将集群中的 ResourceSlice 与驱动必须发布的信息进行协调。 此控制器会覆写任何手动更改,例如集群用户创建或修改 ResourceSlice 的操作。
请看以下 ResourceSlice 示例:
apiVersion: resource.k8s.io/v1
kind: ResourceSlice
metadata:
name: cat-slice
spec:
driver: "resource-driver.example.com"
pool:
generation: 1
name: "black-cat-pool"
resourceSliceCount: 1
# allNodes 字段定义集群中的任何节点是否都可以访问该设备。
allNodes: true
devices:
- name: "large-black-cat"
attributes:
color:
string: "black"
size:
string: "large"
cat:
bool: true
该 ResourceSlice 由 resource-driver.example.com 驱动在 black-cat-pool 池中管理。
allNodes: true 字段表示集群中的任何节点都可以访问这些设备。
ResourceSlice 中有一台设备,名为 large-black-cat,具有以下属性:
color:blacksize:largecat:true
DeviceClass 可以使用这些属性选择此 ResourceSlice, ResourceClaim 可以在该 DeviceClass 中过滤特定设备。
命名与优先级
Kubernetes 调度器评估设备分配顺序的依据是 ResourceSlice 和资源池名称的字典序排序。调度器使用最先适应(first-fit)策略, 即选择满足申领需求的第一台可用设备。
这允许通过为池和 ResourceSlice 分配的名称来影响资源分配的优先级。 请注意,没有绑定条件 的池总是比具有绑定条件的池先被评估,无论它们的名称如何。
对于使用 k8s.io/dynamic-resources/kubeletplugin Go 包构建的驱动,
或使用该模块中的 ResourceSlice 控制器构建的驱动,
这些组件会自动处理 ResourceSlice 命名,
确保按照驱动指定的顺序进行评估。
管理员访问
More information about this feature
This is a stable feature in Kubernetes, and has been since version 1.36. It was first available in the v1.32 release.
你可以将 ResourceClaim 或 ResourceClaimTemplate 中的请求标记为具有特权特性, 用于维护和故障排查任务。具有管理员访问的请求可以授予对正在使用中设备的访问权限, 并可能在使设备在容器中可用时启用额外权限:
apiVersion: resource.k8s.io/v1
kind: ResourceClaimTemplate
metadata:
name: large-black-cat-claim-template
spec:
spec:
devices:
requests:
- name: req-0
exactly:
deviceClassName: resource.example.com
allocationMode: All
adminAccess: true
管理员访问是一种特权模式,在多租户集群中不应授予普通用户。
只有被授权在带有 resource.kubernetes.io/admin-access: "true"
(区分大小写)标签的名字空间中创建 ResourceClaim 或
ResourceClaimTemplate 对象的用户,才能使用 adminAccess 字段。
这确保了非管理员用户不会滥用此特性。
管理员访问由 kube-apiserver、kube-scheduler 和 kubelet 中的
DRAAdminAccess 特性门控控制。
列表类型属性
More information about this feature
To use this feature, you (or a cluster administrator) will need to enable the DRAListTypeAttributes feature gate for all relevant components in your cluster.
See Enable Or Disable Feature Gates for more information.
此特性改进了 ResourceSlice API,允许 DRA 驱动为设备属性指定列表值, 而不仅仅是标量。这对于建模更复杂的节点内部拓扑非常有用, 例如当 CPU 与多个 PCIe 根节点相邻时。
对于 ResourceClaim 的编写者(最终用户),
这意味着 matchAttribute 和 distinctAttribute 在此类场景下工作得更好。
matchAttribute— 两个属性必须具有非空的列表交集, 而非完全相同(标量值被视为单项列表)。这仅意味着如果一个驱动为例如 PCIe 根节点发布了单个值,而另一个驱动发布了一个列表, 只要该单个值出现在列表中的某个位置,约束就被满足。
distinctAttribute— 属性值必须两两互不相交(任意两台设备之间不共享任何值)。
为了帮助 ResourceClaim 的编写者在 CEL 表达式中使用可能为列表的属性,
此特性还引入了 includes() CEL 函数。
# 标量属性(向后兼容)
# 假设:device.attributes["dra.example.com"].model = "model-a"
device.attributes["dra.example.com"].model.includes("model-a") # true
device.attributes["dra.example.com"].model.includes("model-b") # false
# 列表类型属性(需要 DRAListTypeAttributes)
# 假设:device.attributes["dra.example.com"].supported-models = ["model-a", "model-b"]
device.attributes["dra.example.com"].supported-models.includes("model-a") # true
device.attributes["dra.example.com"].supported-models.includes("model-c") # false
DRA 驱动编写者的细节
默认情况下,每个 DeviceAttribute 恰好保存一个标量值:
布尔值、整数、字符串或语义版本字符串。
DRAListTypeAttributes 特性门控为 DeviceAttribute 扩展了四个列表类型字段,
允许设备为单个属性通告多个值:
bools— 布尔值列表ints— 64 位整数值列表strings— 字符串列表(每项最多 64 个字符)versions— 符合 semver.org 规范 2.0.0 的语义版本字符串列表 (每项最多 64 个字符)
每台设备的单个属性值总数(标量字段加上所有列表元素的总和)上限为 48。 当 ResourceSlice 中的任何设备使用此特性或其他高级特性(如污点)时, ResourceSlice 最多被限制为 64 台设备。
以下是一台设备使用列表类型字符串属性通告多个支持型号的示例:
kind: ResourceSlice
apiVersion: resource.k8s.io/v1
metadata:
name: example-resourceslice
spec:
nodeName: worker-1
pool:
name: pool
generation: 1
resourceSliceCount: 1
driver: dra.example.com
devices:
- name: gpu-0
attributes:
dra.example.com/supported-models:
strings:
- model-a
- model-b
列表类型属性由 kube-apiserver 和 kube-scheduler 中的
DRAListTypeAttributes 特性门控控制。
派生属性
More information about this feature
To use this feature, you (or a cluster administrator) will need to enable the DRADerivedAttributes feature gate for all relevant components in your cluster.
See Enable Or Disable Feature Gates for more information.
通常,matchAttribute 和 distinctAttribute 约束要求设备
使用完全相同的名称来发布属性。如果 GPU 驱动发布 pcie_locality,
而 NIC 驱动发布 pcie_root(或将相同信息嵌入到诸如 numa0-pcie1 的字符串中),
调度器无法识别它们表示的是同一事物,
因此来自两个驱动的设备无法被共置,除非先就共享属性名称达成一致。
derivedAttributes 让你可以就地弥合这一差距,
而无需等待驱动在共享属性名称上实现标准化。
在请求的 .spec.devices.requests[].exactly 或
.spec.devices.requests[].firstAvailable[] 下添加一个或多个
derivedAttributes 条目。每个条目定义一个 CEL 表达式,
调度器会针对该请求的每个候选设备评估该表达式。
评估结果成为一个虚拟属性,可以与驱动提供的设备属性完全一样地被
matchAttribute 或 distinctAttribute 约束引用。
apiVersion: resource.k8s.io/v1
kind: ResourceClaim
metadata:
name: gpu-nic-numa-alignment
spec:
devices:
requests:
- name: gpu
exactly:
deviceClassName: gpu.example.com
count: 1
derivedAttributes:
- name: derived/numa
expression: device.attributes["gpu.example.com"].numa
- name: nic
exactly:
deviceClassName: nic.example.com
count: 1
derivedAttributes:
- name: derived/numa
expression: device.attributes["nic.example.com"].numaNode
constraints:
- requests: ["gpu", "nic"]
matchAttribute: derived/numa
在此示例中,gpu 和 nic 驱动使用不同的属性名称(numa 和 numaNode)来发布拓扑信息。
每个请求都从自己的设备属性中计算出一个公共的 derived/numa 值,
而 matchAttribute 约束将两个请求在该虚拟属性上对齐,
即使底层驱动从未就共享属性名称达成一致。
关于 derivedAttributes 需要了解以下几点:
- 命名:
name必须是一个 DNS 子域名,后跟/和一个 C 标识符, 格式与驱动提供的属性名称相同(例如example.com/numaNode或derived/numaNode)。如果该名称与驱动已经发布的驱动提供属性相匹配, 则派生属性的值会在约束匹配时遮蔽驱动提供的属性。 如果你希望避免无意中发生遮蔽,请使用不会被任何驱动使用的域名前缀, 例如derived/。每个请求最多可以定义 32 个派生属性。
- 必须被约束使用: 每个派生属性必须被至少一个应用于定义它的请求(或子请求)的
matchAttribute或distinctAttribute约束引用。 否则 ResourceClaim 验证会失败。
- 评估范围和顺序:
expression对每个候选设备评估一次, 评估发生在请求自己的 CEL 选择算符(.selectors[].cel) 已经过滤完该设备之后。因此,派生属性不能被选择算符表达式引用, 也不会通过 CEL 环境中的device.attributes暴露。
- 返回类型:
expression必须评估为一个标量(string、int、bool或语义版本),或者当同时启用了DRAListTypeAttributes特性门控时, 评估为上述标量类型之一的列表。
- 开销限制: 每个表达式都有最大长度和 CEL 评估开销估算上限。
除此之外,ResourceClaim 中所有
derivedAttributes表达式的组合估算开销也有上限,以限制单次调度尝试的总附加开销。 如果超出上述任何限制,ResourceClaim 将被拒绝。
- 运行时错误会中止调度: 如果对某个候选设备的表达式评估失败(例如因为它引用了该设备没有的属性), 调度器会中止分配,且该 Pod 会调度失败,而不是静默跳过该设备。 请以防御方式编写表达式,例如在读取属性之前先检查它是否存在。
派生属性由 kube-apiserver 和 kube-scheduler 中的
DRADerivedAttributes 特性门控
控制。
有关 DRA 驱动可以发布的标准设备属性列表, 请参阅标准设备属性参考。
通过 DRA 的扩展资源分配
More information about this feature
This is a stable feature in Kubernetes, and has been since version 1.37. It was first available in the v1.34 release.
你可以为 DeviceClass 提供一个扩展资源名称。调度器随后会为扩展资源请求选择匹配该类的设备。 这允许用户继续在 Pod 中使用扩展资源请求, 来请求由设备插件提供的扩展资源,或 DRA 设备。 在单个集群节点上,同一扩展资源可以由设备插件提供,也可以由 DRA 提供。 在同一集群中,某些节点上的同一扩展资源可以由设备插件提供, 而其他节点上可以由 DRA 提供。
在以下示例中,DeviceClass 被赋予了一个 example.com/gpu 的 extendedResourceName。
如果 Pod 请求扩展资源 example.com/gpu: 2,
它可以被调度到具有两台或更多匹配该 DeviceClass 设备的节点上。
apiVersion: resource.k8s.io/v1
kind: DeviceClass
metadata:
name: gpu.example.com
spec:
selectors:
- cel:
expression: device.driver == 'gpu.example.com' && device.attributes['gpu.example.com'].type
== 'gpu'
extendedResourceName: example.com/gpu
此外,用户可以使用一种特殊的扩展资源来分配设备,而无需显式创建 ResourceClaim。
使用扩展资源名称前缀 deviceclass.resource.kubernetes.io/ 加上 DeviceClass 名称即可。
这对任何 DeviceClass 都有效,即使它没有指定扩展资源名称。
生成的 ResourceClaim 将包含一个请求,要求分配该 DeviceClass 的指定数量设备的
ExactCount。
通过 DRA 的扩展资源分配由
kube-apiserver、kube-scheduler、kube-controller-manager 和 kubelet 中的
DRAExtendedResource 特性门控控制。
有关请求扩展资源的实际演练, 请参阅将扩展资源分配给容器。
2.2 - DRA 的工作原理
本页介绍 Kubernetes 如何通过动态资源分配(DRA)为工作负载分配设备, 以及预先调度的 Pod 如何与该流程交互。
如何使用 DRA 进行资源分配
以下各节描述了各种 DRA 用户类型的工作流, 以及 Kubernetes 系统在动态资源分配过程中的工作流。
用户的工作流
- 驱动创建: 设备所有者或第三方实体创建驱动, 这些驱动能够在集群中创建和管理 ResourceSlice。 这些驱动还可以选择性地创建 DeviceClass, 用于定义设备的类别以及如何请求它们。
- 集群配置: 集群管理员创建集群、将设备附加到节点并安装 DRA 设备驱动。 集群管理员还可以选择性地创建 DeviceClass, 用于定义设备的类别以及如何请求它们。
- 资源申领: 工作负载运维人员创建 ResourceClaimTemplate 或 ResourceClaim, 在 DeviceClass 中请求特定设备配置。在同一阶段, 工作负载运维人员修改其 Kubernetes 清单以请求这些 ResourceClaimTemplate 或 ResourceClaim。
Kubernetes 的工作流
- ResourceSlice 创建: 集群中的驱动创建 ResourceSlice, 用于表示被管理的同类设备池中一个或多个设备。
-
工作负载创建: 集群控制平面检查新工作负载中是否引用了 ResourceClaimTemplate 或特定的 ResourceClaim。
- 如果工作负载使用 ResourceClaimTemplate,名为
resourceclaim-controller的控制器会为该工作负载生成 ResourceClaim。 - 如果工作负载使用特定的 ResourceClaim,Kubernetes 会检查该 ResourceClaim 在集群中是否存在。如果 ResourceClaim 不存在, Pod 将无法部署。
- 如果工作负载使用 ResourceClaimTemplate,名为
-
ResourceSlice 过滤: 对于每个 Pod,Kubernetes 检查集群中的 ResourceSlice, 以找到满足以下所有条件的设备:
- 可以访问这些资源的节点有资格运行该 Pod。
- ResourceSlice 具有与 Pod 的 ResourceClaim 需求匹配的未分配资源。
- 资源分配: 在为 Pod 的 ResourceClaim 找到合格的 ResourceSlice 后, Kubernetes 调度器使用分配详情更新 ResourceClaim。 调度器使用最先适应(first-fit)策略,并按池和 ResourceSlice 的名称字典序对其进行评估。 驱动可以通过适当的命名为特定的 slice 或池设置优先级。详细信息请参阅 命名与优先级。
- Pod 调度: 资源分配完成后,调度器将 Pod 放置在可以访问所分配资源的节点上。
该节点上的设备驱动和
kubelet通过 gRPC 协调以配置设备和 Pod 对设备的访问权限,除非驱动为不需要节点本地制备或清理的设备声明了 可选的节点操作。
预先调度的 Pod
当你(或另一个 API 客户端)创建 Pod 时,如果 spec.nodeName 已经被设置,
调度器将被绕开。如果该 Pod 所需的某个 ResourceClaim 尚不存在、尚未分配或未为该 Pod
保留,那么 kubelet 将无法运行该 Pod,并会周期性地重新检查,
因为这些需求可能稍后仍会被满足。
当 Pod 被调度时调度器中尚未启用动态资源分配支持时(版本偏差、配置、特性门控等), 也可能出现这种情况。 kube-controller-manager 会检测到此情况,并通过保留所需的 ResourceClaim 尝试使 Pod 变为可运行状态。但是,这仅在这些 ResourceClaim 已被调度器为其他某个 Pod 分配时才有效。
最好避免绕开调度器,因为被分配到节点的 Pod 在挂起期间会阻塞常规资源(RAM、CPU), 使其无法被其他 Pod 使用。若要让 Pod 在特定节点上运行, 同时仍经过正常的调度流程,请为 Pod 创建一个与目标节点精确匹配的节点选择算符:
apiVersion: v1
kind: Pod
metadata:
name: pod-with-cats
spec:
nodeSelector:
kubernetes.io/hostname: name-of-the-intended-node
...
你也可以在准入阶段变更进入系统的 Pod,取消 .spec.nodeName 字段的设置,
改为使用节点选择算符。
设备绑定条件
设备绑定条件(Device Binding Conditions)允许 Kubernetes 调度器延迟 Pod 绑定, 直到外部资源(例如通过交换架构连接的 GPU 或可重新编程的 FPGA)被确认就绪为止。
这种等待行为在调度框架的 PreBind 阶段中实现。 在此阶段,调度器在继续绑定之前会检查所有必需的设备条件是否均已满足。
这通过避免过早绑定提高了调度的可靠性,并支持与外部设备控制器的协调。
要使用此特性,设备驱动(通常由驱动所有者管理)必须在 ResourceSlice 的
Device 部分发布以下字段。集群管理员必须启用 DRADeviceBindingConditions 和
DRAResourceClaimDeviceStatus 特性门控,调度器才会遵循这些字段。
bindingConditions- 在 Pod 可以被绑定之前必须被设置为 True
(位于关联 ResourceClaim 的
.status.conditions字段中)的条件类型列表。 这些条件通常表示就绪信号,例如 DeviceAttached(设备已挂接)或 DeviceInitialized(设备已初始化)。
bindingFailureConditions- 条件类型列表,若在关联 ResourceClaim 的
status.conditions字段中被设置为 True, 则表示失败状态。如果其中任意条件为 True,调度器将中止绑定并重新调度该 Pod。
bindsToNode- 若设置为
true,调度器会在 ResourceClaim 的status.allocation.nodeSelector字段中记录所选中的节点名称。 这不会影响 Pod 的spec.nodeSelector。相反, 它会在 ResourceClaim 内部设置一个节点选择算符, 外部控制器可以使用它来执行节点特定的操作,例如设备挂接或制备。
bindingConditions 和 bindingFailureConditions 中列出的所有条件类型
都根据 ResourceClaim 的 status.conditions 字段进行评估。
外部控制器负责使用标准 Kubernetes
条件语义(type、status、reason、message、lastTransitionTime)更新这些条件。
调度器最多等待 600 秒(默认值),以等待所有 bindingConditions 变为 True。
如果达到超时或任何 bindingFailureConditions 为 True,
调度器会清除分配并重新调度该 Pod。集群管理员可以通过编辑 kube-scheduler
配置文件来配置此超时时间。
以下给出了在 KubeSchedulerConfiguration 中配置此超时的示例:
apiVersion: kubescheduler.config.k8s.io/v1
kind: KubeSchedulerConfiguration
profiles:
- schedulerName: default-scheduler
pluginConfig:
- name: DynamicResources
args:
apiVersion: kubescheduler.config.k8s.io/v1
kind: DynamicResourcesArgs
bindingTimeout: 60s
示例
以下是你可能在集群中看到的一个 ResourceSlice 示例, 该集群中正在使用某个 DRA 驱动,并且该驱动支持绑定条件:
apiVersion: resource.k8s.io/v1
kind: ResourceSlice
metadata:
name: gpu-slice-1
spec:
driver: dra.example.com
nodeSelector:
nodeSelectorTerms:
- matchExpressions:
- key: accelerator-type
operator: In
values:
- "high-performance"
pool:
name: gpu-pool
generation: 1
resourceSliceCount: 1
devices:
- name: gpu-1
attributes:
vendor:
string: "example"
model:
string: "example-gpu"
bindsToNode: true
bindingConditions:
- dra.example.com/is-prepared
bindingFailureConditions:
- dra.example.com/preparing-failed
该示例 ResourceSlice 具有以下属性:
- ResourceSlice 目标为带有
accelerator-type=high-performance标签的节点, 因此调度器仅使用特定的一组合格节点。
- 调度器从所选组中选择一个节点(例如
node-3),并将 ResourceClaim 中的status.allocation.nodeSelector字段设置为该节点名称。
dra.example.com/is-prepared绑定条件指示设备gpu-1必须被制备 (is-prepared条件的状态为True)后才能绑定。
- 如果
gpu-1设备制备失败(preparing-failed条件状态为True), 调度器会中止绑定。
- 调度器最多等待 600 秒(默认值),等待设备准备就绪。
- 外部控制器可以使用 ResourceClaim 中的节点选择算符,在选定的节点上执行节点特定的设置。
设备绑定条件由 kube-apiserver 和 kube-scheduler 中的
DRADeviceBindingConditions 特性门控控制。
节点可分配资源
More information about this feature
To use this feature, you (or a cluster administrator) will need to enable the DRANodeAllocatableResources feature gate for all relevant components in your cluster.
See Enable Or Disable Feature Gates for more information.
由 DRA 管理的设备可能具有由节点可分配资源(如 cpu、memory 或 hugepages)
构成的底层占用。此特性将这些基于 DRA 的请求与常规 Pod spec
中对这些资源的请求一起整合到调度器的标准核算中。
DRA 驱动使用两种不同的模型来定义设备如何消耗节点可分配资源:
- 直接资源映射(
mapping): DRA 设备直接提供标准节点资源 (例如自定义 CPU 核心池或内存块)。申领分配直接映射到节点上的标准 CPU 或内存容量。
- 辅助设备开销(
overhead): DRA 设备(例如 GPU 或加速器)在被分配给 Pod 或容器时,需要主机资源(例如主机 RAM)作为辅助开销才能运行。
Pod 编写者的注意事项
在使用申领为这些类型的设备编写 PodSpec 时,需要注意以下几点:
-
当使用 Pod 级资源时,调度器会同时针对容器的 requests 和 limits 对其进行严格验证:
- 所有容器 requests 与 DRA 申领资源的总和不得超过 Pod 级 requests; 否则该 Pod 将无法调度。
- 每个单独容器的 limit 加上其 DRA 分配不得超过 Pod 级 limits; 否则该 Pod 将无法调度。
-
容器的总资源需求等于其容器级资源与其关联资源申领中的任何节点可分配资源之和。
-
申领共享限制: 使用直接资源映射(
mapping)的申领不能在多个 Pod 之间共享。 带有overhead的设备申领支持设备共享,且开销按每个 Pod 或每个容器追踪。 -
带有 DRA 申领的 Pod 支持对
spec中的标准 requests 进行就地调整大小。 调度器确保调整后的标准 requests 与静态 DRA 分配结合后仍然能适配节点。
DRA 驱动编写者的细节
DRA 驱动使用 ResourceSlice 中设备上的 nodeAllocatableResources 字段
来声明此节点可分配资源占用。
它定义了将请求的 DRA 设备或容量转换为在节点的 status.allocatable
中追踪的标准资源的映射(注意,此字段不支持扩展资源)。
这对于直接暴露原生资源的驱动(例如 CPU 或内存 DRA 驱动)
和需要辅助节点依赖的设备(例如需要主机内存的加速器)都非常有用。
nodeAllocatableResources 字段支持两种不同的使用场景:
- 映射(Mapping): 当 DRA 设备直接代表标准资源时使用
(例如 CPU 或内存 DRA 驱动)。调度器通过使用
capacityMultiplier缩放容量, 或使用deviceMultiplier缩放设备数量来计算精确的数量。
- 开销(Overhead): 当设备需要辅助节点依赖时使用
(例如 GPU 消耗的主机内存)。这可以定义为固定的
perPod成本, 或定义为随引用容器数量线性缩放的可变perContainer成本。
示例:CPU DRA 驱动(映射 Mapping)
以下示例中,CPU DRA 驱动使用 DRA 可消耗容量将一个
CPU 插槽作为 128 个 CPU 的池暴露出来。capacityKey 将消耗的
cpu.example.com/cpu 容量直接链接到节点的标准 cpu 可分配资源:
apiVersion: resource.k8s.io/v1
kind: ResourceSlice
metadata:
name: my-node-cpus
spec:
driver: cpu.example.com
nodeName: my-node
pool:
name: socket-cpus
generation: 1
resourceSliceCount: 1
devices:
- name: socket0cpus
allowMultipleAllocations: true
capacity:
"cpu.example.com/cpu": "128"
nodeAllocatableResources:
mapping:
cpu:
capacityKey: "cpu.example.com/cpu"
- name: socket1cpus
allowMultipleAllocations: true
capacity:
"cpu.example.com/cpu": "128"
nodeAllocatableResources:
mapping:
cpu:
capacityKey: "cpu.example.com/cpu"
capacityMultiplier: 1
示例:带辅助资源的加速器(Overhead)
以下资源切片示例中,一台加速器每个 Pod 需要额外 8Gi 的内存才能运行:
apiVersion: resource.k8s.io/v1
kind: ResourceSlice
metadata:
name: my-node-xpus
spec:
driver: xpu.example.com
nodeName: my-node
pool:
name: xpu-pool
generation: 1
resourceSliceCount: 1
devices:
- name: xpu-model-x-001
attributes:
example.com/model:
string: "model-x"
nodeAllocatableResources:
overhead:
memory:
perPod: "8Gi"
在 Pod 成功绑定到节点后,通过 DRA 分配的精确节点可分配资源数量会由
kube-scheduler 聚合,并直接嵌入到 Pod 的
status.nodeAllocatableResourceClaimStatuses 字段中。
这提供了从调度器到 kubelet 的清晰、持久的交接。
关键是,kubelet 原生地消费此 API 来完美对齐系统级边界:
- cgroups: Pod 和容器的 cgroups 现在将包含基于 DRA 的分配, 防止工作负载被内核人为地节流。
- OOM Scores:
kubelet将容器的 DRA 内存 requests 计算到其有效内存请求中。
节点可分配资源是一个 Alpha 特性,当在 kube-apiserver、kube-scheduler 和
kubelet 中启用 DRANodeAllocatableResources 特性门控
时即启用。
2.3 - 动态资源的可观测性
本页介绍如何观测通过 DRA 动态分配的资源的状态和健康状况。
动态资源的可观测性
你可以使用以下任一方法检查动态分配的资源的状态:
kubelet 设备指标
PodResourcesLister kubelet gRPC 服务让你可以监控正在使用的设备。
DynamicResource 消息提供特定于动态资源分配的信息,例如设备名称和申领名称。
有关细节请参阅监控设备插件资源。
ResourceClaim 设备状态
More information about this feature
This is a stable feature in Kubernetes, and has been since version 1.37. It was first available in the v1.32 release.
DRA 驱动可以在 ResourceClaim 的 status.devices 字段中,
为每个已分配的设备报告驱动特定的设备状态数据。
例如,驱动可能会列出分配给某个网络接口设备的 IP 地址。更新此字段需要特定的合成 RBAC 权限,
请参阅强化指南 - 动态资源分配和在集群中强化动态资源分配(DRA)。
驱动添加到 ResourceClaim status.devices 字段中的信息是否准确取决于驱动本身。
请评估各个驱动,以决定是否可以将此字段作为设备信息的唯一来源。
如果你禁用
DRAResourceClaimDeviceStatus 特性门控,
则在存储 ResourceClaim 时,status.devices 字段会被自动清除。
当 DRA 驱动能够更新 status.devices 字段已设置的现有 ResourceClaim 时,
就支持 ResourceClaim 设备状态。
在以下示例中,负责管理已分配设备的驱动(resource-driver.example.com)
填充了 ResourceClaim 的 status.devices 字段:
apiVersion: resource.k8s.io/v1
kind: ResourceClaim
metadata:
name: macvlan-eth0
spec:
...
status:
allocation:
devices:
results:
- device: eth0
driver: resource-driver.example.com
pool: nic-worker-a
request: macvlan-eth0
shareID: 8e7acdf9-0290-4ecd-a801-a654b021d2b7
consumedCapacity:
resource-driver.example.com/bandwidth: 1G
devices:
- conditions:
- lastTransitionTime: "2025-10-21T08:38:17Z"
message: Device successfully allocated and assigned to the pod
reason: NetworkReady
status: "True"
type: NetworkReady
device: eth0
driver: resource-driver.example.com
networkData:
hardwareAddress: 00:01:ec:84:fb:51
interfaceName: net1
ips:
- 10.10.1.2/24
- 2001:db8::1/64
pool: nic-worker-a
shareID: 8e7acdf9-0290-4ecd-a801-a654b021d2b7
如果某个设备尚未被分配,则驱动使用该设备更新 ResourceClaim
status.devices 字段的请求会被拒绝。
当设备被取消分配(从 status.allocation.devices 中移除)时,
status.devices 中相应的条目会被自动移除。
有关 status.devices 字段的细节,请参阅
ResourceClaim API 参考。
设备健康监控
Kubernetes 提供了一种机制来监控和报告动态分配的基础设施资源的健康状况。 对于在专用硬件上运行的有状态应用,了解设备何时发生故障或变得不健康至关重要。 了解设备是否恢复也同样有帮助。
要使用此功能,必须启用 ResourceHealthStatus
特性门控
(自 v1.36 起为 Beta 版并默认启用),
并且 DRA 驱动必须实现 DRAResourceHealth gRPC 服务。
当 DRA 驱动检测到已分配的设备变得不健康时,它会将此状态报告回 kubelet。
随后此健康信息会直接暴露在 Pod 的状态中。
kubelet 会填充每个容器状态中的 allocatedResourcesStatus 字段,
详细说明分配给该容器的每个设备的健康状况。
每个资源健康状况条目都可以包含一个可选的 message 字段,
其中包含有关健康状况的更多易于理解的上下文信息,如错误详情或故障原因。
如果在超时时间段内 kubelet 未收到来自 DRA 驱动的健康更新,
则该设备的健康状况会被标记为 “Unknown”。
DRA 驱动可以通过在 DeviceHealth gRPC 消息中
设置 health_check_timeout_seconds 字段,按设备配置此超时时间。
如果未指定,kubelet 使用 30 秒的默认超时时间。
这允许不同的硬件类型(例如 GPU、FPGA 或存储设备)根据其健康状况报告特征使用合适的超时值。
这为用户和控制器应对硬件故障提供了关键的可见性。 对于发生故障的 Pod,你可以检查此状态以确定故障是否与不健康的设备有关。
说明:
Pod 终止后(例如处于 Failed 状态),设备健康状况不会在 Pod 状态中更新。
资源池状态
More information about this feature
To use this feature, you (or a cluster administrator) will need to enable the DRAResourcePoolStatus feature gate for all relevant components in your cluster.
See Enable Or Disable Feature Gates for more information.
你可以使用 ResourcePoolStatusRequest API 查询资源池中设备的可用性。 这让你能够了解整个集群的 DRA 资源池中有多少设备可用、已分配或不可用。
要检查资源池状态:
-
创建一个 ResourcePoolStatusRequest,指定驱动名称(必需), 并可选地指定返回的资源池数量限制。 你还可以通过指定资源池名称将其限制为单个资源池:
apiVersion: resource.k8s.io/v1alpha3 kind: ResourcePoolStatusRequest metadata: name: check-gpus spec: driver: example.com/gpu # 可选:筛选特定的资源池 # poolName: my-pool # 可选:限制返回的资源池数量(默认值:100,最大值:1000) # limit: 10
-
等待控制器处理该请求:
kubectl wait --for=condition=Complete resourcepoolstatusrequest/check-gpus --timeout=30s
-
读取状态以查看资源池可用性:
kubectl get resourcepoolstatusrequest/check-gpus -o yaml状态中包括:
poolCount:与筛选条件匹配的资源池总数 (如果因 limit 而被截断,所列出的资源池数量可能更少)。
pools:资源池详情列表,每一项包含:driver和poolName:标识该资源池。generation:在所有 ResourceSlice 中观测到的最新资源池代数。resourceSliceCount:构成该资源池的 ResourceSlice 数量。totalDevices:资源池中的设备总数。allocatedDevices:当前已分配给申领的设备。availableDevices:可供分配的设备 (totalDevices - allocatedDevices - unavailableDevices)。unavailableDevices:因污点或其他状况而不可用的设备。nodeName:与该资源池关联的节点(如果有)。validationError:当资源池的数据无法被完整验证时设置 (例如在代数更新上线期间)。设置后,设备计数字段可能未被设置。partitionSummary:对于可分区资源池,提供按分区类型划分的可分配性 (参见分区摘要)。shareableSummary:对于包含可共享设备的资源池,提供聚合容量使用量 (参见共享摘要)。
conditions:包括Complete(成功)或Failed(错误)状况类型。
-
完成后删除该请求:
kubectl delete resourcepoolstatusrequest/check-gpus
ResourcePoolStatusRequest 对象由 kube-controller-manager 中的控制器处理一次。 规约一经创建便不可变,而一旦状态被填充,整个对象也会变为不可变。 要获取更新的可用性数据,请删除并重新创建该请求。 已完成的请求会在 1 小时后被自动清理。
此功能需要对 ResourcePoolStatusRequest 资源的显式 RBAC 权限。 没有任何默认的 ClusterRole 包含此权限。
资源池状态由 kube-apiserver 和 kube-controller-manager 中的
DRAResourcePoolStatus 特性门控控制。
分区摘要
More information about this feature
To use this feature, you (or a cluster administrator) will need to enable the DRAPartitionableDevicesType feature gate for all relevant components in your cluster.
See Enable Or Disable Feature Gates for more information.
GPU 等单个物理设备可能会被通告为多个分区类型
(例如完整 GPU 与半尺寸的 MIG 切片),这些分区类型从同一组共享计数器取用资源。
由于这些分区会竞争相同的底层容量,单纯的设备计数无法告诉你每种类型还有多少仍可分配。
对于可分区资源池,
partitionSummary 视图可以回答这个问题。对于每个分区类型,它会报告:
attribute:其值用于分组此条目的设备属性的完全限定名称。 它对应 ResourceSlice 的spec.partitionTypeAttribute; 当切片未声明该属性时,则对应请求的spec.defaultPartitionTypeAttribute。type:设备上该属性的值(例如Full或Half)。total:资源池中此分区类型的设备数量。allocatable:在当前共享计数器消耗情况下, 此分区类型还有多少额外设备仍可分配。
指定的属性必须是字符串属性。如果可分区设备的分区类型属性缺失,或者不是字符串 (例如整数、布尔值或版本值),则资源池会报告验证错误,而不是分区摘要。 对于列表类型属性没有特殊处理; 非字符串属性根本就不是有效的分区类型属性。
为了生成此视图,驱动为每个可分区设备打上一个字符串属性,
其值指明分区类型,并在 ResourceSlice 的 partitionTypeAttribute 字段中指定该属性:
apiVersion: resource.k8s.io/v1
kind: ResourceSlice
# ...
spec:
# 此切片中的每个可分区设备都带有该属性;
# 共享同一值的设备具有相同的共享计数器开销。
partitionTypeAttribute: gpu.example.com/profile
如果驱动尚未更新以声明 partitionTypeAttribute,
请求仍可以通过在规约中指定一个回退属性来获取分区摘要。
切片自身的 partitionTypeAttribute 始终优先;
请求级别的默认值只适用于其切片未声明该属性的设备:
apiVersion: resource.k8s.io/v1alpha3
kind: ResourcePoolStatusRequest
metadata:
name: check-gpu-partitions
spec:
driver: gpu.example.com
# 供自身未声明分组属性的切片使用的回退分组属性。
defaultPartitionTypeAttribute: gpu.example.com/profile
当切片和请求都未指定属性时,可分区资源池不会报告 partitionSummary。
partitionSummary 视图由 kube-apiserver 和 kube-controller-manager 中的
DRAPartitionableDevicesType 特性门控控制,
而该门控又要求启用
DRAResourcePoolStatus
和
DRAPartitionableDevices
特性门控。
共享摘要
对于包含可共享设备的资源池
(即设置了 allowMultipleAllocations、可被多个申领消耗的设备),shareableSummary 会报告整个资源池的聚合容量使用量:
fullyAvailableDevices:未消耗任何容量的可共享设备。partiallyAvailableDevices:消耗了部分但不是全部容量的可共享设备。capacity:按容量名称给出整个资源池中聚合的total、consumed和available(即total减去consumed,不会为负)数量。
只有当资源池中至少有一个设备可共享时,才会填充 shareableSummary。
它属于资源池状态功能
(DRAResourcePoolStatus 特性门控),
不要求启用 DRAPartitionableDevicesType;
它所汇总的可共享设备来自可消耗容量特性。
容器中的 DRA 设备元数据
DRA 驱动可以将设备元数据(例如设备属性(PCI 总线地址或中介设备 UUID) 和网络配置)以 JSON 文件的形式直接暴露给容器。 这让应用无需查询 Kubernetes API 或使用自定义控制器, 即可发现已分配设备的相关信息。
KEP-5304 定义了驱动必须遵循的设备元数据协议, 以便应用在不同驱动和集群之间看到一致的布局。 DRA kubelet 插件库实现了此协议。
设备元数据遵循与设备访问相同的规则:只有当容器请求该设备时,元数据才在该容器内可用。 有关细节请参阅使用 DRA 在工作负载中请求设备。
设备元数据协议
该协议由四条规则组成:
-
文件路径。 元数据文件位于容器内的
/var/run/kubernetes.io/dra-device-attributes下。 对于直接引用的 ResourceClaim,路径为resourceclaims/<claimName>/<requestName>/<driverName>-metadata.json。 对于由 ResourceClaimTemplate 创建的申领,路径为resourceclaimtemplates/<podClaimName>/<requestName>/<driverName>-metadata.json, 其中podClaimName为pod.spec.resourceClaims[].name。当请求使用优先级列表时,
<requestName>路径段只使用顶层请求名称。文件中的requests[].name字段包含完整的<request>/<subrequest>引用,例如gpu/high-memory。
- JSON API。 每个文件是一个或多个
DeviceMetadata对象的流。每个对象都有apiVersion和kind,遵循 Kubernetes API 约定。 同一份元数据会按驱动选择的顺序,为每个已配置的 API 版本各编码一次。 使用方会使用它们能够解码的第一个版本,并跳过未知版本。 已知版本中的格式错误对象会被视为错误。
- 代数。 初始文件的
metadata.generation设为1。 每次更新都会递增该代数,以便使用方能够检测到变化。
- 容器暴露。 DRA kubelet 插件库使用 CDI 将每个文件以只读方式绑定挂载。 其他实现可以使用不同的机制,只要文件出现在所需的路径上并且是只读的即可。
在驱动中启用设备元数据
设备元数据是驱动侧的功能。它没有 Kubernetes 特性门控, 并且在 DRA kubelet 插件库中默认禁用。 驱动必须启用该功能并明确选择要写入的版本:
kubeletplugin.EnableDeviceMetadata(true, []schema.GroupVersion{
metadatav1beta1.SchemeGroupVersion,
metadatav1alpha1.SchemeGroupVersion,
})
v1beta1 版本是必需的。驱动也可以写入 v1alpha1,以兼容较旧的使用方。
切片中的顺序就是元数据流中的顺序;框架不会对版本排序。驱动应将最新版本放在最前面。
启用了设备元数据但未提供版本、缺少 v1beta1 或使用未知版本,
都会导致插件在启动期间失败。
对于每个已准备的设备,驱动可以用
kubeletplugin.DeviceMetadata
填充
Device.Metadata。
驱动应在其 ResourceSlice 中包含它为该设备发布的属性,这样工作负载在运行时能看到相同的信息。
驱动也可以包含仅在运行时才有意义的属性。对于网络设备,驱动可以在 CNI 配置完成后,
通过调用
UpdateRequestMetadata
添加接口名称、IP 地址和硬件地址。
上面的 kubelet 插件 API 链接描述了面向驱动开发者的集成方式。 DRA 框架没有定义通用的命令行标志, 因此集群运维人员通过其驱动提供的部署配置来启用该功能。
启用后,DRA kubelet 插件库会在准备已分配设备时写入元数据文件。
它还会默认将 CDI 规范写入 /var/run/cdi。
容器运行时必须配置为从该目录发现 CDI 规范。
该库会为生成的每个规范确定所需的最低 CDI 规范版本。
当一个请求从多个 DRA 驱动分配设备时,每个驱动都会写入自己的元数据文件。
知道驱动名称的使用方应根据申领、请求和驱动名称构造确切的路径。
Go 使用方可以使用
ReadResourceClaimMetadata
或
ReadResourceClaimTemplateMetadata
来读取并合并某个请求的所有按驱动划分的文件。
元数据模式
元数据文件中的每个对象都符合
DeviceMetadata
API(metadata.resource.k8s.io/v1beta1)。
该模式包含:
- ResourceClaim 的标准对象元数据,包括其名称、名字空间、UID 和元数据代数。
- 对于由 ResourceClaimTemplate 生成的申领,可选的
podClaimName。 - 请求列表。每个请求有一个必需的名称和一个已分配设备的列表。
- 每个设备的驱动、资源池和名称。
- 可选的设备属性和网络数据。
属性值使用与 ResourceSlice 设备属性相同的表示形式。
每个属性恰好有一个标量值(int、bool、string 或 version)
或列表值(ints、bools、strings 或 versions)。
设备容量值不包含在设备元数据中。
网络数据可以包含 interfaceName、ips 和 hardwareAddress。
有关字段约束,请参阅
DeviceMetadata API 文档。
以下示例展示通过 ResourceClaimTemplate 分配的 GPU 设备在元数据流中的一个对象:
{
"kind": "DeviceMetadata",
"apiVersion": "metadata.resource.k8s.io/v1beta1",
"metadata": {
"name": "pod0-gpu-2kqrd",
"namespace": "gpu-test1",
"uid": "c7e7b22e-239b-4498-b27c-7f1344481e14",
"generation": 1
},
"podClaimName": "gpu",
"requests": [
{
"name": "gpu",
"devices": [
{
"driver": "gpu.example.com",
"pool": "worker-0",
"name": "gpu-0",
"attributes": {
"driverVersion": {
"version": "1.0.0"
},
"index": {
"int": 0
},
"model": {
"string": "LATEST-GPU-MODEL"
},
"uuid": {
"string": "gpu-18db0e85-99e9-c746-8531-ffeb86328b39"
}
}
}
]
}
]
}
DRA kubelet 插件在写入元数据之前不会对其进行验证。Go 使用方在解码流时可以选择启用生成的验证。 解码和验证的结果是分开的:验证错误不会阻止返回已成功解码的对象。有关用法, 请参阅访问 DRA 设备元数据。
即时元数据与延迟元数据
对于即时元数据,驱动在准备申领时提供属性或网络数据。
DRA kubelet 插件在使用该设备的容器启动之前写入代数 1 的文件。
对于延迟元数据,驱动可以在没有属性或网络数据的情况下准备设备。初始的代数 1 文件包含设备标识。
驱动稍后调用 UpdateRequestMetadata 以原子方式替换整个流并递增代数。
更新要求初始文件存在。如果设备准备为某个请求返回空设备,
框架不会为该请求创建元数据文件,也不会创建元数据 CDI 设备。
在每个使用该设备的容器的生命期内,元数据对该容器始终可用。 框架在申领被取消准备后移除元数据文件和 CDI 规范。
要了解如何在工作负载中使用设备元数据, 请参阅访问 DRA 设备元数据。
自定义驱动
不使用 DRA kubelet 插件库的自定义驱动必须自行实现设备元数据协议。
这包括在正确的路径下写入带版本的 DeviceMetadata 流、在每次更新时递增 metadata.generation,
以及通过 CDI 或等效机制以只读方式暴露文件。
2.4 - DRA 特性
本页面针对一些高级用例介绍可选的 DRA 特性。其中一些特性需要 DRA 驱动的支持。 每个特性都注明了其成熟度以及启用该特性的特性门控。
可分区设备
DRA 中表示的设备不一定必须是连接到单台机器的单个单元,也可以是由连接到多台机器的多个设备组成的逻辑设备。 这些设备可能会消耗底层物理设备的重叠资源,这意味着当分配一个逻辑设备时,其他设备将不再可用。
在 ResourceSlice API 中,这表示为命名 CounterSet 的列表,每个 CounterSet 包含一组命名计数器。 这些计数器表示物理设备上可用于通过 DRA 通告的逻辑设备的资源。
逻辑设备可以指定 ConsumesCounters 列表。 每个条目包含对一个 CounterSet 的引用,以及一组命名计数器及其将消耗的数量。 因此,要使设备可分配,被引用的计数器集必须具有足够的数量来满足设备引用的计数器需求。
CounterSet 必须在与设备不同的 ResourceSlice 中指定。 设备可以消耗与其位于同一资源池中的任何 CounterSet 定义的计数器。
以下是两个设备的示例,每个设备从一个具有 8Gi 内存的共享计数器中消耗 6Gi 内存。 因此,在任何时间点只能分配其中一个设备。 调度器会处理这种情况,并且对使用者是透明的,因为 ResourceClaim API 不受影响。
apiVersion: resource.k8s.io/v1
kind: ResourceSlice
metadata:
name: resourceslice-with-countersets
spec:
nodeName: worker-1
pool:
name: pool
generation: 1
resourceSliceCount: 2
driver: dra.example.com
sharedCounters:
- name: gpu-1-counters
counters:
memory:
value: 8Gi
---
apiVersion: resource.k8s.io/v1
kind: ResourceSlice
metadata:
name: resourceslice-with-devices
spec:
nodeName: worker-1
pool:
name: pool
generation: 1
resourceSliceCount: 2
driver: dra.example.com
devices:
- name: device-1
consumesCounters:
- counterSet: gpu-1-counters
counters:
memory:
value: 6Gi
- name: device-2
consumesCounters:
- counterSet: gpu-1-counters
counters:
memory:
value: 6Gi
可分区设备由 kube-apiserver 和 kube-scheduler 中的
DRAPartitionableDevices 特性门控控制。
设备兼容性组
More information about this feature
To use this feature, you (or a cluster administrator) will need to enable the DRADeviceCompatibilityGroups feature gate for all relevant components in your cluster.
See Enable Or Disable Feature Gates for more information.
设备兼容性组允许 DRA 驱动声明哪些分区设备可以在同一物理硬件上共同分配。 如果没有此特性,不兼容的设备组合只有在 kubelet 在节点上准备 Pod 时才会被检测到 —— 这会导致准备失败。 有了兼容性组,调度器会在调度时、任何节点端工作开始之前就拒绝不兼容的组合。
这对于支持互斥操作模式的硬件最为有用。例如,可以在 MIG 模式或 vGPU 模式下运行的 GPU:
处于 MIG 模式的设备和处于 vGPU 模式的设备不能共同分配,因为它们以不兼容的方式消耗重叠的物理资源。
通过声明 compatibilityGroups,驱动使此约束对调度器可见。
此特性建立在可分区设备的基础之上:
compatibilityGroups 字段位于 device.consumesCounters[] 条目上,而该条目仅存在于可分区设备中。
DRADeviceCompatibilityGroups 和 DRAPartitionableDevices 这两个特性门控都必须在
kube-apiserver 和 kube-scheduler 中启用。
工作原理
驱动为 ResourceSlice 中的每个 device.consumesCounters[] 条目定义一个 compatibilityGroups 列表。
该列表最多包含 2 个不透明的字符串名称,表示该设备在该特定计数器集上的操作模式或分区类型。
当调度器分配从同一计数器集获取资源的多个设备时,它会计算这些设备的 compatibilityGroups 的交集。
只有当该交集非空时 —— 即每个共同分配的设备至少共享一个共同的组名 —— 分配才会成功。
从不同计数器集获取资源的设备永远不会相互比较。
未声明任何组的设备(未设置、为 nil 或为空列表)被视为特殊情况:它只能与同一计数器集上其他没有组的设备共同分配。 它永远不能与声明了一个或多个组的设备共同分配。
此约束适用于在单个调度周期内分配的所有声明: 如果两个声明各自从同一计数器集分配一个设备,则跨声明的组交集也会被强制执行。
示例
考虑一个可以在 MIG 模式或 vGPU 模式下运行的 GPU。 驱动发布两个设备,每个设备从同一个 8 GiB 的共享内存计数器中消耗 4 GiB。 仅根据计数器容量,两个设备可以一起分配。每个设备将其操作模式声明为一个兼容性组,从而使两种模式互斥:
apiVersion: resource.k8s.io/v1
kind: ResourceSlice
metadata:
name: gpu-counters
spec:
nodeName: worker-1
pool:
name: gpu-pool
generation: 1
resourceSliceCount: 2
driver: gpu.example.com
sharedCounters:
- name: gpu-0-memory
counters:
memory:
value: 8Gi
---
apiVersion: resource.k8s.io/v1
kind: ResourceSlice
metadata:
name: gpu-devices
spec:
nodeName: worker-1
pool:
name: gpu-pool
generation: 1
resourceSliceCount: 2
driver: gpu.example.com
devices:
- name: gpu-0-mig
consumesCounters:
- counterSet: gpu-0-memory
counters:
memory:
value: 4Gi
compatibilityGroups:
- mig
- name: gpu-0-vgpu
consumesCounters:
- counterSet: gpu-0-memory
counters:
memory:
value: 4Gi
compatibilityGroups:
- vgpu
在此示例中:
gpu-0-mig属于mig组。gpu-0-vgpu属于vgpu组。
如果一个 Pod 或 PodGroup 从该资源池请求两个设备,
调度器会检查所选的两个设备在 gpu-0-memory 计数器集上是否共享一个共同的兼容性组。
由于 {"mig"} ∩ {"vgpu"} = ∅,该设备对会被拒绝——即使计数器集有足够的内存同时满足两者。
两个请求只能通过来自存在此类设备对的资源池中的两个 MIG 设备(或两个 vGPU 设备)来满足。
约束条件
- 每个
consumesCounters[]条目最多可以声明 2 个组名。 - 组名在单个条目内必须唯一。
- 组名对 Kubernetes 是不透明的;它们仅在发布驱动的资源池内有意义。
- 组按计数器集进行比较:一个计数器集上的组对另一个计数器集的共同分配决策没有影响。
版本偏差安全
当 DRADeviceCompatibilityGroups 特性门控被禁用时(Alpha 阶段的默认设置),
kube-apiserver 会从任何新的或更新的 ResourceSlice 中剥离 compatibilityGroups
字段——除非旧对象已经设置了该字段。
然后,调度器会将任何以前有分组设备的资源池中的设备视为属于不完整的资源池,并完全跳过它们。
只有非空列表才算作设置了该字段:compatibilityGroups: null 和 compatibilityGroups: []
被视为与省略该字段相同。
带有这些值的设备的行为与没有组的设备完全相同 —— 它们不会导致调度器将资源池视为不完整。
设备兼容性组由 kube-apiserver 和 kube-scheduler 中的
DRADeviceCompatibilityGroups 特性门控控制。
同时还必须启用
DRAPartitionableDevices 特性门控。
可消耗容量
可消耗容量特性允许多个独立的 ResourceClaim 消耗同一设备,由 Kubernetes 调度器管理每个声明消耗了多少设备容量。 这类似于 Pod 如何共享节点上的资源;ResourceClaim 可以共享设备上的资源。
设备驱动可以设置 ResourceSlice 的 .spec.devices 中新增的 allowMultipleAllocations 字段,
以允许将该设备分配给多个独立的 ResourceClaim 或一个 ResourceClaim 内的多个请求。
用户可以设置 ResourceClaim 的 spec.devices.requests 中新增的 capacity 字段,以指定每次分配的设备资源需求。
对于允许多次分配的设备,所请求的容量从其总容量中提取(即消耗),这一概念被称为可消耗容量。
然后,调度器确保所有声明的总消耗容量不超过设备的整体容量。
此外,驱动开发者可以对各个设备容量使用 requestPolicy 约束来控制这些容量的消耗方式。
例如,驱动开发者可以指定某个容量只能以 1Gi 的增量消耗。
下面是一个网络设备的示例,该设备允许多次分配并包含可消耗的带宽容量。
kind: ResourceSlice
apiVersion: resource.k8s.io/v1
metadata:
name: resourceslice
spec:
nodeName: worker-1
pool:
name: pool
generation: 1
resourceSliceCount: 1
driver: dra.example.com
devices:
- name: eth1
allowMultipleAllocations: true
attributes:
name:
string: "eth1"
capacity:
bandwidth:
requestPolicy:
default: "1M"
validRange:
min: "1M"
step: "8"
value: "10G"
可消耗容量的请求方式如下例所示。
apiVersion: resource.k8s.io/v1
kind: ResourceClaimTemplate
metadata:
name: bandwidth-claim-template
spec:
spec:
devices:
requests:
- name: req-0
exactly:
deviceClassName: resource.example.com
capacity:
requests:
bandwidth: 1G
分配结果将包含已消耗的容量和共享标识符。
apiVersion: resource.k8s.io/v1
kind: ResourceClaim
...
status:
allocation:
devices:
results:
- consumedCapacity:
bandwidth: 1G
device: eth1
shareID: "a671734a-e8e5-11e4-8fde-42010af09327"
在本例中,选择了一个可多次分配的设备。
然而,任何至少具有所请求 1G 带宽的 resource.example.com 设备都可以满足该需求。
如果选择了不可多次分配的设备,则分配将占用整个设备。
要强制只使用可多次分配的设备,可以使用 CEL 条件 device.allowMultipleAllocations == true。
DistinctAttribute 约束
在一个 ResourceClaim 中请求多个设备时,你可以使用 DistinctAttribute 约束来确保每个已分配设备在指定属性上具有不同的值。此约束是随可消耗容量特性一起引入的。
DistinctAttribute 约束在处理可多次分配的设备时特别有用。 它可以防止调度器在单个 ResourceClaim 内多次分配同一设备,即使该设备允许多次分配也是如此。
除了防止重复分配外,此约束还有助于通过确保设备根据其属性分布来优化性能。 例如,你可以使用它在不同的 NUMA 节点间分布设备,以优化内存带宽并减少争用。
细粒度状态授权
从 Kubernetes v1.36 开始,DRA 通过使用合成子资源和节点感知动词,对 ResourceClaim 状态的更新实施细粒度的授权检查。
有关安全加固指南(包括调度器和 DRA 驱动的 RBAC 示例), 请参见加固指南 - 动态资源分配。
有关集群管理员的分步操作流程,请参见在集群中加固动态资源分配。
可选节点操作
More information about this feature
To use this feature, you (or a cluster administrator) will need to enable the DRAOptionalNodeOperations feature gate for all relevant components in your cluster.
See Enable Or Disable Feature Gates for more information.
在动态资源分配(DRA)中,kubelet 通过 gRPC 与节点本地驱动协作,
在容器启动前准备已分配的设备(NodePrepareResources),并在 Pod 终止时取消准备(NodeUnprepareResources)。
虽然这种设置对于 GPU 或 FPGA 等节点本地硬件至关重要,但某些资源完全在控制平面中管理,不需要节点本地设置。
可选节点操作特性允许资源驱动声明可以跳过特定的节点本地 gRPC 操作。
配置后,kubelet 会绕过这些设备的驱动查找和 gRPC 调用,从而无需在每个工作节点上部署和维护空的节点本地驱动。
驱动配置
驱动开发者可以在 ResourceSlice 的 .spec.skipNodeOperations 中指定 skipNodeOperations 字段。
该字段是一个唯一字符串列表,指定要为该切片中所有设备绕过的节点本地操作。
有效值包括:
"NodePrepareResources":跳过NodePrepareResourcesgRPC 调用。 除非同时列出了"NodeUnprepareResources"(或指定了"*"),否则不能指定该值。 此限制避免了在缺少节点本地插件时 Pod 卡在 Terminating 状态的问题,因为当跳过准备操作时, Pod 启动期间不会检查插件。
-
"NodeUnprepareResources":跳过NodeUnprepareResourcesgRPC 调用。 -
"*":跳过所有节点本地资源操作。
以下是一个用于控制平面资源的 ResourceSlice 示例,该资源跳过所有节点本地操作:
apiVersion: resource.k8s.io/v1
kind: ResourceSlice
metadata:
name: control-plane-resources
spec:
nodeName: worker-1
pool:
name: central-pool
generation: 1
resourceSliceCount: 1
driver: control-plane.example.com
skipNodeOperations:
- "*"
devices:
- name: virtual-device-1
分配结果与执行
当 Kubernetes 调度器将设备分配给 ResourceClaim 时,
它会将 skipNodeOperations 列表从 ResourceSlice 复制到分配结果中:
apiVersion: resource.k8s.io/v1
kind: ResourceClaim
...
status:
allocation:
devices:
results:
- device: virtual-device-1
driver: control-plane.example.com
pool: central-pool
skipNodeOperations:
- "*"
当 Pod 在节点上运行时,kubelet 读取分配结果。
如果 ResourceClaim 中某个给定驱动的所有已分配设备都跳过某项特定操作,
则 kubelet 会完全绕过对该驱动的该 gRPC 钩子调用。
操作注意事项
原地驱动更新
由于 skipNodeOperations 设置是在分配时从 ResourceSlice 复制到 ResourceClaim 中的,
因此正在运行的 Pod 和活跃的分配会保留它们被调度时的设置。
如果驱动的节点操作要求被原地更新(例如,从需要节点操作变为跳过节点操作),现有的声明仍将使用之前的配置。 为避免出现问题 —— 例如终止中的 Pod 在等待已停用的节点插件时挂起 —— 集群管理员应在更改驱动的节点操作要求或移除节点本地驱动 DaemonSet 之前,确保该驱动没有活跃的声明存在。
节点声明特性集成
为了防止 Pod 被调度到 kubelet 不支持跳过 DRA 操作的节点上(这会导致 kubelet 在等待缺失的节点插件时失败),
该特性与节点声明特性集成。
当 Pod 使用配置了 skipNodeOperations 的 ResourceClaim 时,Kubernetes 调度器会在调度 Pod 之前,
验证目标节点是否在其 .status.declaredFeatures 中声明了对 DRAOptionalNodeOperations 特性的支持。
可选节点操作由 kube-apiserver、kube-scheduler 和 kubelet 中的
DRAOptionalNodeOperations
特性门控控制。
2.5 - 设备污点与容忍度
本页介绍 DRA 中的设备污点与容忍度,它们允许驱动和管理员阻止 Pod 使用特定设备,或驱逐已经在使用这些设备的 Pod。
设备污点与容忍度
More information about this feature
This is a stable feature in Kubernetes, and has been since version v1.37. It was first available in the v1.33 release. You can no longer disable or opt out of this feature or behavior (it is locked); if you explicitly set a value for the associated feature gate DRADeviceTaints, Kubernetes ignores it but does not report any error.
设备污点与节点污点类似:一个污点包含字符串键、字符串值以及效果。
该效果会应用到正在使用被污染设备的 ResourceClaim,以及引用该 ResourceClaim 的所有 Pod。
NoSchedule 效果会阻止这些 Pod 的调度。
在尝试分配 ResourceClaim 时,被污染的设备会被忽略,
因为使用它们会阻止 Pod 的调度。
NoExecute 效果暗含 NoSchedule,并且会额外驱逐所有已被调度的 Pod。
该驱逐由 kube-controller-manager 中的设备污点驱逐控制器通过删除受影响的 Pod 来实现。
调度器和驱逐控制器会忽略 None 效果。
DRA 驱动可以用它向管理员或其他控制器传达异常情况,
例如设备的健康状况下降。管理员也可以用它在 DeviceTaintRule
中执行 Pod 驱逐的试运行(更多见下文)。
ResourceClaim 可以容忍污点。如果某个污点被容忍,则其效果不会生效。 空的容忍度匹配所有污点。容忍度可以被限制为仅对某些效果生效, 和/或匹配某些键值对。容忍度可以检查某个键是否存在(无论其值是什么), 也可以检查键的特定值。有关这种匹配的更多信息, 请参阅节点污点概念。
通过在一段时间内容忍污点可以延迟驱逐。 该延迟从污点被添加到设备上的时间开始计算, 该时间记录在污点的一个字段中。
如上所述,污点同样适用于分配节点上"所有"设备的 ResourceClaim。 必须所有设备都未被打上污点,或者它们的所有污点都被容忍。 使用管理员访问(见上文的描述)分配设备同样不能豁免。 使用该模式的管理员必须显式容忍所有污点才能访问被污染的设备。
你可以通过使用 DeviceTaintRule API 种类,以以下方式向设备添加污点。
由驱动设置的污点
DRA 驱动可以向其在 ResourceSlice 中发布的设备信息添加污点。 请查阅 DRA 驱动的文档,了解该驱动是否使用污点以及其键和值是什么。
由管理员设置的污点
More information about this feature
This is a stable feature in Kubernetes, and has been since version v1.37. It was first available in the v1.35 release. You can no longer disable or opt out of this feature or behavior (it is locked); if you explicitly set a value for the associated feature gate DRADeviceTaintRules, Kubernetes ignores it but does not report any error.
管理员或控制平面组件可以直接为设备设置污点, 而无需告知 DRA 驱动在其 ResourceSlice 中的设备信息里包含污点。 他们通过创建 DeviceTaintRule 来实现。 每个 DeviceTaintRule 向匹配设备选择算符的设备添加一个污点。 如果没有选择算符,则不会有设备被污染。 这降低了因误操作遗漏选择算符而意外驱逐所有使用 ResourceClaim 的 Pod 的风险。
可以通过指定 DeviceClass、驱动、池和/或设备的名称来选择设备。 DeviceClass 会选择所有被该 DeviceClass 中的选择算符选中的设备。 仅使用驱动名称,管理员可以污染由该驱动管理的所有设备, 例如在整个集群中对该驱动进行某种维护时。 如果驱动管理节点本地的设备,添加池名称可以将污点限制到单个节点。
最后,添加设备名称可以选择一个特定设备。 如果需要,设备名称和池名称也可以单独使用。 例如,建议节点本地设备的驱动使用节点名称作为其池名称。 这样,使用该池名称设置污点会自动污染节点上的所有设备。
驱动可能使用像 "gpu-0" 这样的稳定名称, 隐藏了当前分配给该名称的是哪一个具体设备。 为了支持污染特定的硬件实例,如果驱动为其硬件提供了 特定于供应商的唯一 ID 属性,可以在 DeviceTaintRule 中使用 CEL 选择算符来匹配该属性。
只要 DeviceTaintRule 存在,污点就会生效。 它可以随时被修改和删除。 以下是一个针对虚构 DRA 驱动的 DeviceTaintRule 示例:
apiVersion: resource.k8s.io/v1
kind: DeviceTaintRule
metadata:
name: example
spec:
# 该特定驱动的整个硬件安装已损坏。
# 驱逐所有 Pod,且不调度新的 Pod。
deviceSelector:
driver: dra.example.com
taint:
key: dra.example.com/unhealthy
value: Broken
effect: NoExecute
kube-apiserver 通过设置 spec 中的 timeAdded 字段,
自动追踪该污点的创建时间。容忍期从该时间戳开始计算。
在变更效果的更新过程中(见下文的模拟驱逐流程),
kube-apiserver 会自动更新时间戳。用户可以在创建 DeviceTaintRule
时显式设置该字段,或在更新时改为其他值,从而显式控制时间戳。
status 中包含由驱逐控制器添加的一个条件:
kubectl describe devicetaintrules
Name: example
...
Spec:
Device Selector:
Driver: dra.example.com
Taint:
Effect: NoExecute
Key: dra.example.com/unhealthy
Time Added: 2025-11-05T18:15:37Z
Value: Broken
Status:
Conditions:
Last Transition Time: 2025-11-05T18:15:37Z
Message: 1 pod evicted since starting the controller.
Observed Generation: 1
Reason: Completed
Status: False
Type: EvictionInProgress
Events: <none>
Pod 通过被删除来驱逐。通常这会很快发生,
除非对污点的容忍度将其延迟一段时间,或者有非常多的 Pod 需要驱逐。
如果驱逐花费的时间较长,message 会提供关于当前状态的信息:
```
2 pods need to be evicted in 2 different namespaces. 1 pod evicted since starting the controller.
```
该状况可用于检查驱逐当前是否正在进行:
```
kubectl wait --for=condition=EvictionInProgress=false DeviceTaintRule/example
```
请注意调度器和控制器在不同时间观察到新污点可能引发的竞态条件:
这可能导致在控制器认为没有需要驱逐的 Pod 从而将此条件设置为
False 时,仍有 Pod 被调度。实际中,通过刻意延迟几秒钟才更新状态,
这种竞态条件出现的可能性非常低。
对于 effect: None,message 会提供关于受影响设备数量、
其中已分配的设备数量,以及如果效果为 NoExecute 将会被驱逐的 Pod
数量的信息。这可以在实际触发驱逐之前用作试运行:
- 使用所需的选择算符和
effect: None创建一个 DeviceTaintRule。
- 查看 message:
3 published devices selected. 1 allocated device selected.
1 pod would be evicted in 1 namespace if the effect was NoExecute.
This information will not be updated again. Recreate the DeviceTaintRule to trigger an update.
已发布的设备是 ResourceSlice 中列出的设备。为它们设置污点会阻止为新 Pod 分配。 只有已分配的设备才会导致使用它们的 Pod 被驱逐。
- 编辑 DeviceTaintRule,将效果更改为
NoExecute。
3 - Pod 级资源管理器
More information about this feature
To use this feature, you (or a cluster administrator) will need to enable the PodLevelResourceManagers feature gate for all relevant components in your cluster.
See Enable Or Disable Feature Gates for more information.
现有的资源管理器(拓扑、CPU 和内存)对 Pod 级资源的支持将其扩展为能够处理
Pod 级资源规约。当启用时(通过 PodLevelResources 和 PodLevelResourceManagers
特性门控),资源管理器可以直接使用 .spec.resources 作为分配决策的基础,从严格的按容器分配模型演进为以
Pod 为中心的模型。
这种分区方案引入了一种更灵活、更强大的资源管理模型,
尤其适用于对性能敏感的工作负载。它允许你定义混合分配模型,
其中 Pod 中的某些容器获得独占的、NUMA 对齐的资源,
而其他容器共享 Pod 级共享池中剩余的资源。
要练习使用 Pod 级资源设置 kubelet 资源管理器并亲身体验分配行为,
请遵循使用 Pod 级资源与 kubelet 资源管理器教程。
为了理解 Pod 级资源管理器,将它们与传统的以容器为中心的模型进行对比会很有帮助。
此前,kubelet 资源分配是严格的"全有或全无":
要为工作负载获得独占的 NUMA 对齐资源,Pod 中的每个容器都必须是
Guaranteed(为 CPU 和内存指定相等的 requests 和 limits)。
Pod 级资源管理器使用 .spec.resources,根据配置的拓扑管理器范围启用灵活的分区:
pod范围:kubelet基于.spec.resources为整个 Pod 分配并 NUMA 对齐一个单一的 Pod 资源单元(Pod bubble)。请求独占分配的容器会从此 Pod 资源泡中划分出专属的切片, 而所有其他容器在 Pod 隔离的共享池中共享剩余的资源泡容量。container范围: 启用混合分配模型。kubelet允许各个容器直接从节点的 可分配池中获得独占的、NUMA 对齐的资源,同时使用 Pod 的.spec.resources上限来限制总体消耗 —— 允许边车在常规节点共享池中运行, 而无需 Pod 中的每个容器都为 Guaranteed。
标准 Init 容器和可重启 Init 容器(边车)都完全受支持。 它们可以获得独占的资源切片或使用 Pod 的共享池, 并且 Pod 级资源管理器会遵守它们的生命周期规则 (例如,标准 Init 容器的可重用资源与边车的持久保留)。
术语表
- Pod 级资源规约
- 在 Pod 级别的
.spec.resources中定义的资源预算, 用于指定整个 Pod 的总体 requests 和 limits。
- Guaranteed 容器
- 为 CPU(独占 CPU 分配要求正整数值)和内存都指定了与 limits 相等的
resource requests 的容器。与现有的
kubelet行为一致, 这使容器有资格从资源管理器获得独占资源分配。
- 独占切片
- 专门分配给单个容器的专属资源部分(例如:特定 CPU 或内存页), 确保与其他容器隔离。
- Pod 共享池
- 在所有独占切片被保留后剩余的 Pod 已分配资源的子集。 这些资源由 Pod 中所有未获得独占分配的容器共享。 虽然此池中的容器彼此共享资源,但它们与独占切片和常规的节点级共享池严格隔离。
Pod 级资源管理器的工作原理
CPU 和内存资源管理器的运行方式因配置的拓扑管理器范围不同而异。
拓扑管理器的 Pod 范围和 Pod 级资源
当拓扑管理器范围设置为 pod 时,kubelet 基于 .spec.resources
中定义的资源预算为整个 Pod 执行单次 NUMA 对齐。
得到的 NUMA 对齐资源池随后被划分为:
- 独占切片: 指定了
Guaranteed资源(CPU 和内存的 requests 与 limits 相等,且 CPU request 为正整数)的容器从 Pod 的总分配中获得独占切片。 - Pod 共享池: 剩余资源形成一个共享池,供 Pod 中所有其他 未获得独占分配的容器使用。虽然此池中的容器彼此共享资源, 但它们与独占切片和常规的节点级共享池严格隔离。
请注意,当标准 Init 容器运行完成时,它们的资源会进入每个 Pod 的可重用集合, 而不是返回到节点的资源池。由于它们是按顺序运行的, 后续的应用容器可以重用这些资源(用于自己的独占切片或共享池)。
这允许你将需要独占资源的容器(例如高性能主应用)与不需要独占资源的容器 (例如日志或监控边车)放在同一个 NUMA 对齐的 Pod 中。
考虑以下 Pod 规约中的容器,其中拓扑管理器范围为 pod,Pod 的总预算为 4 个 CPU。
main-app 请求独占的 2 个 CPU 切片,而边车在 Pod 的共享池中共享剩余的
2 个 CPU:
apiVersion: v1
kind: Pod
metadata:
name: pod-scope-mixed
annotations:
kubernetes.io/description: "A pod demonstrating pod-level scope where one container gets exclusive resources and others share the remaining pod resources in a shared pool."
spec:
# 在 Pod 级别,该 Pod 的 CPU request 等于 limits,内存 request 也等于内存 limits。
# main-app 容器在容器级别满足 Guaranteed QoS 类的要求,而各边车容器未指定任何资源 request。
# 在 pod 范围下,这意味着 kubelet 可以静态地为整个 Pod 分配
# 4 个 CPU,其中 2 个独占分配给 main-app 容器,剩余的 2 个由边车在该 Pod 的共享池中共享。
resources:
requests:
cpu: "4"
memory: "4Gi"
limits:
cpu: "4"
memory: "4Gi"
initContainers:
- name: metrics-sidecar
# 注意:这是仅用于演示的占位镜像,并不是真正的 metrics 助手。
image: registry.k8s.io/pause:3.9
restartPolicy: Always
- name: logging-sidecar
# 注意:这是仅用于演示的占位镜像,并不是真正的日志代理。
image: registry.k8s.io/pause:3.9
restartPolicy: Always
containers:
- name: main-app
# 注意:这是仅用于演示的占位镜像。
image: registry.k8s.io/pause:3.9
resources:
requests:
cpu: "2"
memory: "2Gi"
limits:
cpu: "2"
memory: "2Gi"
重要注意事项:
当将 Pod 级资源与拓扑管理器的 pod 范围一起使用时,有一些重要注意事项:
-
空共享池限制: 如果存在需要共享池的容器,此配置不允许会产生空的 Pod 共享池的 Pod 规约。如果所有
Guaranteed容器的 resource requests 之和恰好等于总资源预算,并且至少有一个其他容器需要共享池,kubelet会在准入时拒绝该 Pod。例如,以下 Pod 请求的 Pod 级预算为 4 个 CPU。
main-app需要独占的 3 个 CPU,metrics-sidecar需要独占的 1 个 CPU。 由于共享池中没有剩余的 CPU 可供logging-sidecar使用,kubelet会拒绝此 Pod(相同的验证也适用于内存):
- 资源浪费: 使用
pod范围时超量分配的任何资源 (所有容器的 requests 总和小于 Pod 级预算且没有共享池容器, 或共享池容器未完全使用剩余量)仍保持分配并为 Pod 保留, 在整个 Pod 执行期间实际上被浪费。
- 持久池: Pod 的总资源池(NUMA 对齐和总保留容量)是持久的。 如果共享池中的容器崩溃并重启,Pod 的整体资源保留仍安全地锚定在节点上。 只有当整个 Pod 终止时,节点才会将资源释放回其常规池。
拓扑管理器的 Container 范围和 Pod 级资源
当拓扑管理器范围设置为 container 时,kubelet 会逐个评估每个容器以进行独占分配。
如果整个 Pod 达到 Guaranteed
QoS 类
(通过在 Pod 级别的 .spec.resources 中指定适当的值),
你可以混合搭配容器:
- 具有自己的
Guaranteedrequests 的容器获得独占的 NUMA 对齐资源。 - Pod 中其他未指定
Guaranteedrequests 的容器在节点的共享池中运行。 - 所有容器的总体资源消耗仍由 Pod 的
.spec.resourceslimits 强制执行。
当你有一个基础设施边车需要为设备访问对齐到特定 NUMA 节点, 而主工作负载可以在常规节点共享池中运行时,此范围非常有用。
考虑以下 Pod 规约中的容器,其中拓扑管理器范围为 container,
Pod 代表一个包含基础设施边车和两个应用工作器的工作负载,总预算为 4 个 CPU。
infrastructure-sidecar 获得独占的、NUMA 对齐的 2 个 CPU 切片。
两个应用工作器(worker-1 和 worker-2)在常规的节点级共享池中运行:
apiVersion: v1
kind: Pod
metadata:
name: container-scope-mixed
annotations:
kubernetes.io/description: "A pod demonstrating container-level scope where one container gets exclusive resources and others run in the node's shared pool."
spec:
# 在 Pod 级别,该 Pod 的 CPU request 等于 limits,内存 request 也等于内存 limits。
# infrastructure-sidecar 容器在容器级别满足 Guaranteed QoS 类的要求,而各 worker
# 容器未指定任何资源 request。在 container 范围下,kubelet 会逐个评估容器以进行独占分配。
# 这意味着 infrastructure-sidecar 获得独占的 2 个 CPU 切片,
# 而 worker 容器在节点的常规共享池中运行,同时所有容器的总量仍受 Pod 级限额约束。
resources:
requests:
cpu: "4"
memory: "4Gi"
limits:
cpu: "4"
memory: "4Gi"
initContainers:
- name: infrastructure-sidecar
# 注意:这是仅用于演示的占位镜像,并不是真正的基础设施助手。
image: registry.k8s.io/pause:3.9
restartPolicy: Always
resources:
requests:
cpu: "2"
memory: "2Gi"
limits:
cpu: "2"
memory: "2Gi"
containers:
- name: worker-1
# 注意:这是仅用于演示的占位镜像。
image: registry.k8s.io/pause:3.9
- name: worker-2
# 注意:这是仅用于演示的占位镜像。
image: registry.k8s.io/pause:3.9
CPU 配额(CFS)
在 Pod 内运行混合工作负载时,kubelet 根据分配方式以不同方式强制执行隔离:
- 独占容器: 具有独占 CPU 切片的容器禁用了其 CPU CFS 配额强制执行, 允许它们在 Linux 调度器下运行时不被节流。
- Pod 共享池容器: Pod 共享池中的容器启用 CPU CFS 配额, 确保它们消耗的资源不超过剩余的 Pod 预算, 并防止它们干扰独占容器。
持久池和重启
Pod 的总资源池(NUMA 对齐和总保留容量)是持久的。 如果 Pod 共享池中的容器崩溃并重启,Pod 的整体资源保留仍安全地锚定在节点上。 只有当整个 Pod 终止时,节点才会将资源释放回其常规池。
kubelet 降级和状态检查点
在 Kubernetes 1.36 中,启用 PodLevelResourceManagers 会将内部
kubelet 状态检查点文件(cpu_manager_state 和 memory_manager_state)
更新为旧版 kubelet 无法加载的格式。
如果你在活跃使用后降级 1.36 的 kubelet,旧版 kubelet 将无法启动;
你必须腾空节点、删除这些检查点文件并重启 kubelet。
在 Kubernetes 1.37 中,检查点文件使用前向兼容的格式来防止降级期间的启动失败,
尽管 1.36 版本的 kubelet 不会恢复活跃的 Pod 级资源分配。
有关检查点格式和恢复的完整详细信息,请参阅
Pod 级资源管理器参考。
可观测性和指标
你可以使用以下 kubelet 指标(通过 PodLevelResourceManagers 特性门控启用)
监控容器级和 Pod 级分配中资源管理器的行为和健康状况:
resource_manager_allocations_total:统计管理器执行的独占资源分配总数。source标签("pod" 或 "node")区分了从节点级池与从预分配的 Pod 级池中提取的分配。resource_manager_allocation_errors_total:统计在独占资源分配期间遇到的错误, 按预期分配的source("pod" 或 "node")区分。resource_manager_container_assignments:跟踪将被授予特定类型资源分配的容器累积数量。assignment_type标签("node_exclusive"、"pod_exclusive"、 "pod_shared")提供了对有多少容器使用(来自节点或 Pod 池的) 独占资源与 Pod 级共享池运行的可见性。
PodResources API
在 Kubernetes 1.37 中,当启用 PodLevelResourceManagers 时,
kubelet 的节点本地 PodResources gRPC API 包含 Pod 级资源分配。
节点本地监控代理和设备插件可以查询顶级 Pod 分配(cpu_ids 和 memory),
同时避免重复计算容器级分配。
有关完整的 API 模式、字段掩码和各范围报告表,请参阅 Pod 级资源管理器参考。
限制和注意事项
- 该功能仅针对
staticCPU 管理器策略和Static内存管理器策略实现。 请注意,BestEffort策略不受内存管理器支持。 - 此功能仅在 Linux 节点上受支持。在 Windows 节点上, 资源管理器对 Pod 级分配将作为空操作(no-op)。
接下来
- 了解如何分配 Pod 级 CPU 和内存资源。
- 遵循使用 Pod 级资源与
kubelet资源管理器教程。 - 阅读节点资源管理器。