动态资源分配
Kubernetes v1.34 [stable] (enabled by default: true)
本页描述 Kubernetes 中的 动态资源分配(DRA)。
关于 DRA
DRA 是 kubernetes 提供的一项特性,允许你在多个 Pod 之间请求和共享资源。
这些资源通常是挂接的设备,例如硬件加速器。
借助 DRA,设备驱动和集群管理员可以定义设备的类别,这些类别可供工作负载中的 Pod 申领。 Kubernetes 会将匹配的设备分配给特定的申领,并将相应的 Pod 调度到能够访问这些已分配设备的节点上。
使用 DRA 来分配资源的体验与动态卷制备类似, 你可以使用 PersistentVolumeClaim 基于存储类来申领存储容量,并在 Pod 中请求这些已申领的容量。
DRA 的好处
DRA 为集群中的设备提供了一种灵活的方式来进行分类、请求和使用。 使用 DRA 具有以下好处:
- 灵活地过滤设备:使用 Common Expression Language (CEL) 对特定设备属性进行细粒度过滤。
- 设备共享:通过引用相应的资源声明,可以让多个容器或 Pod 共享同一个资源。
- 集中化的设备分类:设备驱动和集群管理员可以使用设备类, 来为应用运维人员提供针对不同使用场景优化的硬件类别。 例如,你可以创建一个面向通用工作负载的成本优化型设备类,以及一个面向关键任务的高性能设备类。
- 简化 Pod 的资源请求:使用 DRA 后,应用运维人员无需在 Pod 的资源请求中明确指定设备的规格。 相反,Pod 只需引用一个资源申领,这个申领中的设备配置将会自动应用到该 Pod。
这些好处相较于设备插件, 在设备分配的流程中带来了显著的改进。设备插件需要为每个容器单独请求设备,且不支持设备共享, 也无法基于表达式进行对设备进行过滤。
DRA 用户的类型
使用 DRA 进行设备分配的工作流程里通常涉及到以下几类用户:
-
设备所有者: 为设备负责。设备的所有者可以是商业厂商、集群运营者,或其他实体。 若要使用 DRA,设备必须具备兼容 DRA 的驱动程序,该驱动需完成以下工作:
- 创建 ResourceSlice,向 Kubernetes 提供节点及资源的信息;
- 当集群中的资源容量发生变化时,更新相应的 ResourceSlice;
- 可选地,创建 DeviceClass 以供工作负载运维人员申领设备。
-
集群管理员:负责集群与节点配置、设备挂接、驱动安装等相关工作。 若要使用 DRA,集群管理员需要执行以下操作:
- 将设备挂接到节点上;
- 安装支持 DRA 的设备驱动;
- 可选地,创建 DeviceClass 以供工作负载运维人员用来申领设备。
-
工作负载运维人员:负责在集群中部署和管理工作负载。 若要使用 DRA 为 Pod 分配设备,工作负载运维人员需要执行以下操作:
- 创建 ResourceClaim 或 ResourceClaimTemplate,以便于基于指定的 DeviceClass 请求特定的设备配置;
- 部署引用这些 ResourceClaim 或 ResourceClaimTemplate 的工作负载。
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 调度到该节点从而使得 Pod 能够访问到特定资源。
DeviceClass
DeviceClass 允许集群管理员或设备驱动程序定义集群中的设备类别。 这些 DeviceClass 告诉运维人员他们可以使用什么设备以及他们能够如何请求这些设备。 你可以使用 通用表达式语言(CEL) 来按照特定属性选择设备。 随后,引用该 DeviceClass 的 ResourceClaim 就可以请求该类别的设备配置。
要创建 DeviceClass,请参阅在集群中安装 DRA.
ResourceClaim 和 ResourceClaimTemplate
ResourceClaim 定义某个工作负载所需的资源。每个 ResourceClaim 都包含一个或多个引用了某个 DeviceClass 并从其中选择具体的设备的 request。
ResourceClaim 也可以使用 selectors 来筛选满足特定条件的设备,并通过 constraints 来限制可以满足请求的设备。ResourceClaim 可以被工作负载运维人员创建,也可以由 Kubernetes 根据 ResourceClaimTemplate 生成。ResourceClaimTemplate 定义了一个模版来让 Kubernetes 能根据它为 Pod 自动生成 ResourceClaim。
ResourceClaim 和 ResourceClaimTemplate 的使用场景
使用方式取决于你的需求,例如:
- ResourceClaim: 你希望多个 Pod 对某个特定设备进行共享访问。你可以创建 ResourceClaim 并对其生命周期进行手动管理。
- ResourceClaimTemplate:你希望 Pod 能够有对独立但有相似配置的设备进行独立访问。 Kubernetes 可以基于 ResourceClaimTemplate 中的定义生成 ResourceClaim,而每个生成的 ResourceClaim 的生命周期都与其所对应的 Pod 的生命周期是相绑定的。
当你在定义一个工作负载时,你可以使用 通用表达式语言(CEL) 来针对特定设备的属性和容量进行过滤。这些可用于过滤的参数则取决于具体的设备与其驱动程序。
如果你直接将一个特定的 ResourceClaim 关联到一个 Pod,则这个 ResourceClaim 必须已存在于与该 Pod 相同的命名空间中。如果这个 ResourceClaim 在该命名空间不存在, 该 Pod 将不会被调度。 这个行为类似于 PersistentVolumeClaim: 被 Pod 引用的 PersistentVolumeClaim 必须存在于与该 Pod 相同的命名空间中。
你能够在 Pod 中引用一个自动生成的 ResourceClaim,但并不推荐这样做。因为自动生成的 ResourceClaim 是和触发它生成的 Pod 的生命周期相绑定的。
要了解如何使用这种方式申领资源, 请参阅使用 DRA 为工作负载分配设备。
按优先级排序的列表
Kubernetes v1.34 [beta] (enabled by default: true)
你可以在 ResourceClaim 中为请求提供按优先级排序的子请求列表。调度器将选择第一个能够分配的子请求。 这使得用户能够在首选设备不可用时指定工作负载可以使用的替代设备。
在下面的示例中,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 是 ReplicaSet 或其他类似组中的一员的时候, 你不能假定该组中的所有成员都会选择相同的子请求。你的工作负载必须能够适应这种情况。
按优先级排序的列表是一个 Beta 特性,
在 kube-apiserver 和 kube-scheduler 中通过 DRAPrioritizedList
特性门控 默认启用。
ResourceSlice
每个 ResourceSlice 代表资源池中的一个或多个设备。 该资源池由设备驱动程序管理,它负责创建并维护这些 ResourceSlice。 资源池中的资源可以由单个 ResourceSlice 表示,也可以分布在多个 ResourceSlice 中。
ResourceSlice 为设备使用者和调度器提供了有用的信息,是实现动态资源分配的关键组成部分。 每个 ResourceSlice 都必须包含以下信息:
- 资源池: 一组由驱动程序管理的一个或多个资源。 一个资源池可以跨越多个 ResourceSlice。当资源池中的资源发生变化时, 必须将这些变更同步到该资源池内的所有 ResourceSlice。 负责管理该资源池的设备驱动程序应确保这一同步过程的正确执行。
- 设备: 那些在被管理的资源池内的设备。一个 ResourceSlice 可以列出资源池中的所有设备, 也可以仅列出其中的一部分。ResourceSlice 定义了设备的一系列信息,例如属性、版本以及容量等。 设备使用者可以在 ResourceClaim 或 DeviceClass 中通过筛选这些设备信息来选择要分配的设备。
- 节点:能够访问这些资源的节点。驱动程序可以自行决定哪些节点可访问这些资源, 可以是集群中的所有节点、某个特定名称的节点,或者是那些具有特定节点标签的节点。
驱动程序使用 控制器, 将集群中的 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 中进一步筛选特定的设备。
DRA 资源分配的工作原理
接下来的一节将介绍在动态资源分配过程中,多种 DRA 用户的类型以及 Kubernetes 系统各自的工作流程。
用户工作流程
- 创建驱动程序: 设备的所有者或第三方实体会创建那些能够在集群内创建并管理 ResourceSlice 的驱动程序。这些驱动程序还可以创建那些用于定义设备类别和请求方式的 DeviceClass。
- 配置集群: 集群管理员创建集群,将设备挂接到节点上,并安装支持 DRA 的设备驱动程序。 集群管理员可以创建那些用于定义设备类别和请求方式的 DeviceClass。
- 资源申领: 工作负载运维人员创建 ResourceClaimTemplate 或 ResourceClaim, 以请求指定 DeviceClass 所提供的特定设备配置。同时,应用运维人员通过修改其 Kubernetes 清单以在工作负载中引用这些 ResourceClaimTemplate 或 ResourceClaim。
Kubernetes 工作流程
- 创建ResourceSlice:集群中的驱动程序负责创建 ResourceSlice, 用于表示在受管控的相似设备资源池中一个或多个设备。
-
创建工作负载:集群控制面检查那些引用了 ResourceClaimTemplate 或特定 ResourceClaim 的工作负载。
- 如果这个工作负载使用了一个 ResourceClaimTemplate,那么一个被叫做
resourceclaim-controller的控制器会为这个工作负载中的每个 Pod 生成 ResourceClaim。 - 如果这个工作负载使用了一个特定的 ResourceClaim, 那么 Kubernetes 将会检查这个 ResourceClaim 在集群中是否存在。如果 ResourceClaim 不存在,则 Pod 将不会被部署。
- 如果这个工作负载使用了一个 ResourceClaimTemplate,那么一个被叫做
-
过滤 ResourceSlice:对于任意一个 Pod,Kubernetes 会检查集群中的 ResourceSlice 以找到一个满足所有以下条件的设备:
- 能够访问该资源的节点必须符合运行该 Pod 的条件;
- 该 ResourceSlice 中存在尚未分配的资源,并且这些资源符合该 Pod 所引用的 ResourceClaim 的要求。
- 分配资源:在为 Pod 的 ResourceClaim 找到符合条件的 ResourceSlice 之后, Kubernetes 调度器会将分配的详细信息更新在 ResourceClaim 上。
- 调度Pod:当资源完成分配后,调度器将 Pod 放在可以访问该资源的节点上。 节点上的设备驱动程序以及 kubelet 将对设备进行配置,从而使得 Pod 能够访问到该设备。
动态资源的可观测性
你可以使用以下任意方式来检查动态分配资源的状态:
kubelet 设备指标
kubelet 的 PodResourcesLister gRPC 服务可以对在使用的设备进行监控。
DynamicResource 提供了与动态资源分配相关的特定信息,例如设备名称和声明名称。
更多细节请参阅监控设备插件资源.
ResourceClaim 设备状态
Kubernetes v1.33 [beta] (enabled by default: true)
DRA 驱动程序可以在 ResourceClaim 的 status.devices
字段中为已分配的设备上报特定于驱动的设备状态数据。
例如,驱动程序可以在其中列出分配给某个网络接口设备的 IP 地址。
上报到 ResourceClaim 的 status.devices 字段上的信息的准确性取决于驱动程序。
请对所用驱动进行评估,从而判断能否将此字段作为设备信息的唯一来源。
如果你禁用了DRAResourceClaimDeviceStatus
特性门控,
那么 status.devices 字段会在 ResourceClaim 被保存时被自动清理。
ResourceClaim 的设备状态的支持,需要 DRA 驱动程序能够对设置了 status.devices
字段的存量 ResourceClaim 对象进行更新。
更多status.devices字段的详细信息,请参阅
ResourceClaim 的 API 参考。
设备健康监控
Kubernetes v1.31 [alpha] (enabled by default: false)
作为一种 Alpha 特性,Kubernetes 提供了一种机制用于监控和上报动态分配的基础设施资源的健康状况。
对于跑在专用硬件上的有状态的应用而言,了解设备何时发生故障或变得不健康是至关重要的。
同时,获知设备是否恢复也同样有助于维护应用的稳定性。
要开启这个功能,ResourceHealthStatus
特性门控
必须启用的同时,设备驱动程序必须实现了 DRAResourceHealth gRPC 服务。
当一个 DRA 驱动程序发现某个已分配的设备变为不健康,他要将这个状态汇报回 kubelet。
这些健康状态的信息会直接暴露在 Pod 的状态中。 kubelet 会在每个容器的状态中填充
allocatedResourcesStatus 字段,以详细描述分配给该容器的每个设备的健康状况。
这为用户和控制器提供了关键的可观测性,使其能够及时响应硬件故障。 对于处于失败状态的 Pod,可以通过检查该状态信息来判断故障是否与某个不健康的设备有关。
预调度的 Pod
当你(或别的 API 客户端)创建设置了 spec.nodeName 的 Pod 时,调度器将被绕过。
如果 Pod 所需的某个 ResourceClaim 尚不存在、未被分配或未为该 Pod 保留,那么 kubelet
将无法运行该 Pod,并会定期重新检查,因为这些要求可能在以后得到满足。
这种情况也可能发生在 Pod 被调度时调度器中未启用动态资源分配支持的时候(原因可能是版本偏差、配置、特性门控等)。 kube-controller-manager 能够检测到这一点,并尝试通过预留所需的一些 ResourceClaim 来使 Pod 可运行。 然而,这只有在这些 ResourceClaim 已经被调度器为其他 Pod 分配的情况下才有效。
绕过调度器并不是一个好的选择,因为分配给节点的 Pod 会锁住一些正常的资源(RAM、CPU), 而这些资源在 Pod 被卡住时无法用于其他 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 字段,并改为使用节点选择算符。
DRA Beta 特性
以下各小节阐述了可以在 Beta 特性门控 中获取到的 DRA 特性。
欲了解更多信息, 请参阅在集群中安装 DRA。
管理性质的访问
Kubernetes v1.34 [beta] (enabled by default: true)
你可以在 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
如果此特性被禁用,创建此类 ResourceClaim 时将自动移除 adminAccess 字段。
管理性质访问是一种特权模式,在多租户集群中不应该对普通用户开放。
从 Kubernetes v1.33 开始,在标有 resource.k8s.io/admin-access: "true"(区分大小写)
的命名空间中只有被授权创建 ResourceClaim 或 ResourceClaimTemplate
对象的用户才能使用 adminAccess 字段。这确保了非管理员用户不能滥用此特性。
从 Kubernetes v1.34 开始,此标签已被更新为 resource.kubernetes.io/admin-access: "true"。
DRA Alpha 特性
以下各小节描述可供使用的 Alpha 阶段 DRA 特性。 要使用这些特性,你还必须开启 DynamicResourceAllocation 特性门控和 DRA API 组 以在集群中安装 DRA。
更多信息请参阅在集群中安装 DRA。
使用 DRA 分配扩展资源
Kubernetes v1.34 [alpha] (enabled by default: false)
你可以为 DeviceClass 提供一个扩展资源名称。对于此扩展资源的请求, 此后调度器会从该类的设备中选择匹配的设备。这允许用户在 Pod 中继续使用扩展资源请求通过设备插件来申请扩展资源,或是 DRA 设备。 在集群的单个节点上,同一个扩展资源要么通过设备插件,要么通过 DRA 提供。 在同一个集群内,同一个扩展资源在某些节点上可以由设备插件提供,而在其他节点上由 DRA 提供。
在下面的例子中,该 DeviceClass 的 extendedResourceName 被赋值为 example.com/gpu。
那么如果一个 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 将包含一个请求,要求按照 ExactCount 模式,
从该 DeviceClass 中分配指定数量的设备。
通过 DRA 来分配扩展资源是一个 Alpha 特性,它只有当
kube-apiserver,kube-scheduler 和 kubelet 中启用了 DRAExtendedResource
特性门控
时,该特性才会被启用。
可切分设备
Kubernetes v1.33 [alpha] (enabled by default: false)
DRA 中表示的设备不一定必须是连接到单个机器的单个单元, 也可以是由连接到多个机器的多个设备组成的逻辑设备。 这些设备可能会消耗底层物理设备的重叠资源,这意味着当一个逻辑设备被分配时, 其他设备将不再可用。
在 ResourceSlice API 中,这类设备表示为命名 CounterSet 列表,每个 CounterSet 包含一组命名计数器。 计数器表示物理设备上可供通过 DRA 发布的逻辑设备使用的资源。
逻辑设备可以指定 ConsumesCounter 列表。每个条目包含对某个 CounterSet 的引用和一组命名计数器及其消耗量。 因此,要使设备可被分配,所引用的 CounterSet 必须具有设备引用的计数器所需的足够数量。
以下是两个设备的示例,每个设备从具有 8Gi 内存的共享计数器中消耗 6Gi 内存。 因此,在任何时间点只能分配其中一个设备。调度器处理这种情况, 对使用者来说是透明的,因为 ResourceClaim API 不受影响。
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
sharedCounters:
- name: gpu-1-counters
counters:
memory:
value: 8Gi
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
可切分设备是一个 Alpha 特性,它只有当
kube-apiserver 和 kube-scheduler 中启用了 DRAPartitionableDevices
特性门控
时,该特性才会被启用。
可消耗容量
Kubernetes v1.34 [alpha] (enabled by default: false)
可消耗容量特性允许同一台设备被多个独立的 ResourceClaim 同时使用, 由 Kubernetes 调度器负责管理每个声明消耗了多少设备容量。 这一机制类似于多个 Pod 可以共享同一节点上的资源, 多个 ResourceClaim 也可以共享同一设备上的资源。
设备驱动程序可以在 ResourceSlice 的 .spec.devices 中设置
allowMultipleAllocations 字段,以允许将该设备分配给多个独立的 ResourceClaim,
或分配给同一 ResourceClaim 中的多个请求。
用户可以在 ResourceClaim 的 spec.devices.requests 中新增的 capacity 字段进行设置,
以指定每次分配所需的设备资源容量。
对于允许多次分配的设备,请求的容量将从设备的总容量中提取或消耗,
这一机制被称为可消耗容量(Consumable 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。
设备污点和容忍度
Kubernetes v1.33 [alpha] (enabled by default: false)
设备污点类似于节点污点:污点具有字符串形式的键、字符串形式的值和效果。 效果应用于使用带污点设备的 ResourceClaim 以及引用该 ResourceClaim 的所有 Pod。 "NoSchedule" 效果会阻止调度这些 Pod。 在尝试分配 ResourceClaim 时会忽略带污点的设备, 因为使用它们会阻止 Pod 的调度。
"NoExecute" 效果隐含 "NoSchedule" 效果,此外还会导致已调度的所有 Pod 被驱逐。 这种驱逐是通过 kube-controller-manager 中的设备污点驱逐控制器删除受影响的 Pod 来实现的。
ResourceClaim 可以容忍污点。如果污点被容忍,其效果将不会生效。 空容忍度匹配所有污点。容忍度可以限制为特定效果和/或匹配特定键/值对。 容忍度可以检查某个键是否存在,无论其值是什么,也可以检查某个键是否具有特定值。 有关此匹配机制的更多信息,请参阅节点污点概念。
通过容忍污点一段时间可以延迟驱逐。该延迟从污点添加到设备时开始, 并被记录在污点的字段中。
如上所述,污点也适用于在节点上分配"所有"设备的 ResourceClaim。 所有设备必须不带污点,或者必须容忍其所有污点。 分配具有管理员访问权限的设备(上文所述)也不例外。 使用该模式的管理员必须明确容忍所有污点才能访问带污点的设备。
设备污点和容忍度是一个 Alpha 特性,它只有当
kube-apiserver、kube-controller-manager 和 kube-scheduler 中启用了 DRADeviceTaints
特性门控
时,该特性才会被启用。
要使用 DeviceTaintRules,必须启用 resource.k8s.io/v1alpha3 API 版本。
你可以通过以下方式使用 DeviceTaintRule API 类型向设备添加污点。
由驱动程序设置的污点
DRA 驱动程序可以为其在 ResourceSlice 中发布的设备信息添加污点。 请查阅 DRA 驱动程序的文档,了解驱动程序是否使用污点以及它们的键和值是什么。
由管理员设置的污点
管理员或控制平面组件可以在不告诉 DRA 驱动程序在其 ResourceSlice 中的设备信息中包含污点的情况下为设备添加污点。他们通过创建 DeviceTaintRule 来实现这一点。 每个 DeviceTaintRule 为匹配设备选择算符的设备添加一个污点。 如果没有指定这样的选择算符,则不会为任何设备添加污点。这使得在错误地遗漏选择算符时, 意外驱逐所有使用 ResourceClaim 的 Pod 变得更加困难。
可以通过提供 DeviceClass、驱动程序(driver)、资源池(pool)和/或设备的名称来选择设备。 DeviceClass 选择该 DeviceClass 中的选择算符所选择的所有设备。 通过仅使用驱动程序名称,管理员可以为该驱动程序管理的所有设备添加污点, 例如在对整个集群中的该驱动程序进行某种维护时。 如果驱动程序管理节点本地设备,添加池名称可以将污点限制为单个节点。
最后,添加设备名称可以选择一个特定设备。如果需要,设备名称和池名称也可以单独使用。 例如,鼓励负责制备节点本地设备的驱动程序使用节点名称作为其池名称。 然后使用该池名称添加污点会自动为节点上的所有设备添加污点。
驱动程序可能使用像 "gpu-0" 这样的稳定名称, 这些名称隐藏了当前分配给该名称的特定设备。 为了支持为特定硬件实例添加污点, 可以在 DeviceTaintRule 中使用 CEL 选择算符来匹配特定于供应商的唯一 ID 属性, 前提是驱动程序支持硬件对应的这类属性。
只要 DeviceTaintRule 存在,污点就会生效。它可以随时被修改和删除。 以下是一个虚构的 DRA 驱动程序的 DeviceTaintRule 示例:
apiVersion: resource.k8s.io/v1alpha3
kind: DeviceTaintRule
metadata:
name: example
spec:
# 这个特定驱动程序的整个硬件安装已损坏。
# 驱逐所有 Pod 并且不调度新的 Pod。
deviceSelector:
driver: dra.example.com
taint:
key: dra.example.com/unhealthy
value: Broken
effect: NoExecute
设备绑定状况
Kubernetes v1.34 [alpha] (enabled by default: false)
设备绑定状况允许 Kubernetes 调度器在确认外部资源 (例如光纤挂接下的 GPU 或可重编程的 FPGA)确认就绪之前延迟对 Pod 的绑定操作。
这种等待机制是在调度框架的 PreBind 阶段 实现的。 在该阶段,调度器会在继续执行绑定之前检查所有所需的设备状况是否已满足。
这种机制通过避免过早绑定以及支持与外部设备控制器进行协调的方式,提高了调度的可靠性。
要使用此特性,设备驱动程序(通常由驱动程序所有者管理)必须在 ResourceSlice 的
Device 部分中发布以下字段。 此外为了让调度器能够考虑这些字段,集群管理员必须启用
DRADeviceBindingConditions 和 DRAResourceClaimDeviceStatus 特性门控。
-
bindingConditions:一个状况类型的列表,在 Pod 能被绑定之前, 所关联的 ResourceClaim 的 status.conditions 字段中的这些状况类型必须被设置为 True。 这些状况通常表示设备就绪信号,例如 "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)
对这些状况进行更新。
调度器会等待bindingConditions 变为 True,但最长不超过 600秒。
如果发生超时或者任意一个 bindingFailureConditions 变为 True,
那么调度器将清除当前的分配并重新调度该 Pod。
apiVersion: resource.k8s.io/v1
kind: ResourceSlice
metadata:
name: gpu-slice
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 中的节点选择器, 以在选定节点上执行特定于该节点的初始化或配置操作。
接下来
- 在集群中安装 DRA
- 使用 DRA 为工作负载分配设备
- 了解更多该设计的信息, 参阅使用结构化参数的动态资源分配 KEP。