动态资源分配是 Kubernetes 中用于请求和共享资源的 API,它可以在 Pod 之间和 Pod 内部容器之间进行。它是对通用资源提供的持久卷 API 的泛化,可以支持不同类型的资源定义和初始化,并且可以通过任意参数进行配置。第三方资源驱动程序负责跟踪和分配这些资源。
Kubernetes v1.28 引入了集群级 API 支持动态资源分配,但需要显式启用。此外,必须为该 API 管理的特定资源安装相应的资源驱动程序。
一、API
resource.k8s.io/v1alpha2 API 组提供四种类型:
1、ResourceClass
定义由哪个资源驱动程序处理某种资源,并为其提供通用参数, 集群管理员在安装资源驱动程序时创建 ResourceClass。
2、ResourceClaim
定义工作负载所需的特定资源实例, 由用户创建(手动管理生命周期,可以在不同的 Pod 之间共享), 或者由控制平面基于 ResourceClaimTemplate 为特定 Pod 创建 (自动管理生命周期,通常仅由一个 Pod 使用)。
3、ResourceClaimTemplate
定义用于创建 ResourceClaim 的 spec 和一些元数据,部署工作负载时由用户创建。
4、PodSchedulingContext
供控制平面和资源驱动程序内部使用, 在需要为 Pod 分配 ResourceClaim 时协调 Pod 调度。
ResourceClass 和 ResourceClaim 的参数存储在单独的对象中,通常使用安装资源驱动程序时创建的 CRD 所定义的类型。
core/v1 的 PodSpec 在 resourceClaims 字段中定义 Pod 所需的 ResourceClaim。 该列表中的条目引用 ResourceClaim 或 ResourceClaimTemplate。 当引用 ResourceClaim 时,使用此 PodSpec 的所有 Pod (例如 Deployment 或 StatefulSet 中的 Pod)共享相同的 ResourceClaim 实例。 引用 ResourceClaimTemplate 时,每个 Pod 都有自己的实例。
容器资源的 resources.claims 列表定义容器可以访问的资源实例, 从而可以实现在一个或多个容器之间共享资源。下面是一个虚构的资源驱动程序的示例。 该示例将为此 Pod 创建两个 ResourceClaim 对象,每个容器都可以访问其中一个。
apiVersion: resource.k8s.io/v1alpha2 kind: ResourceClass name: resource.example.com driverName: resource-driver.example.com --- apiVersion: cats.resource.example.com/v1 kind: ClaimParameters name: large-black-cat-claim-parameters spec: color: black size: large --- apiVersion: resource.k8s.io/v1alpha2 kind: ResourceClaimTemplate metadata: name: large-black-cat-claim-template spec: spec: resourceClassName: resource.example.com parametersRef: apiGroup: cats.resource.example.com kind: ClaimParameters name: large-black-cat-claim-parameters –-- apiVersion: v1 kind: Pod metadata: name: pod-with-cats spec: containers: - name: container0 image: ubuntu:20.04 command: ["sleep", "9999"] resources: claims: - name: cat-0 - name: container1 image: ubuntu:20.04 command: ["sleep", "9999"] resources: claims: - name: cat-1 resourceClaims: - name: cat-0 source: resourceClaimTemplateName: large-black-cat-claim-template - name: cat-1 source: resourceClaimTemplateName: large-black-cat-claim-template
二、调度
与原生资源(如 CPU、RAM)和扩展资源(由设备插件管理并由 kubelet 公布)不同,调度器不知道集群中有哪些动态资源,也不知道如何将它们拆分以满足特定的 ResourceClaim 要求。这些任务由资源驱动程序负责。资源驱动程序在为 ResourceClaim 保留资源后将其标记为“已分配(Allocated)”,然后告诉调度器可用的 ResourceClaim 的位置。
ResourceClaim 可以在创建时就进行分配(“立即分配”),不用考虑哪些 Pod 将使用它。 默认情况下采用延迟分配,直到需要 ResourceClaim 的 Pod 被调度时 (即“等待第一个消费者”)再进行分配。在这种模式下,调度器检查 Pod 所需的所有 ResourceClaim,并创建一个 PodScheduling 对象, 通知负责这些 ResourceClaim 的资源驱动程序,告知它们调度器认为适合该 Pod 的节点。
资源驱动程序通过排除没有足够剩余资源的节点来响应调度器, 一旦调度器有了这些信息,它就会选择一个节点,并将该选择存储在 PodScheduling 对象中,然后资源驱动程序为分配其 ResourceClaim,以便资源可用于该节点,完成后,Pod 就会被调度。
作为此过程的一部分,ResourceClaim 会为 Pod 保留。 目前,ResourceClaim 可以由单个 Pod 独占使用或不限数量的多个 Pod 使用。
除非 Pod 的所有资源都已分配和保留,否则 Pod 不会被调度到节点,这是一个重要特性。 这避免了 Pod 被调度到一个节点但无法在那里运行的情况, 这种情况很糟糕,因为被挂起 Pod 也会阻塞为其保留的其他资源,如 RAM 或 CPU。
三、监控资源
kubelet 提供了一个 gRPC 服务,以便发现正在运行的 Pod 的动态资源。
四、预调度的Pod
当(或别的 API 客户端)创建设置了 spec.nodeName 的 Pod 时,调度器将被绕过。 如果 Pod 所需的某个 ResourceClaim 尚不存在、未被分配或未为该 Pod 保留,那么 kubelet 将无法运行该 Pod,并会定期重新检查,因为这些要求可能在以后得到满足。
这种情况也可能发生在 Pod 被调度时调度器中未启用动态资源分配支持的时候(原因可能是版本偏差、配置、特性门控等)。 kube-controller-manager 能够检测到这一点,并尝试通过触发分配和/或预留所需的 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 字段,并改为使用节点选择算符。
五、启用动态资源分配
动态资源分配是一个 alpha 特性,只有在启用 DynamicResourceAllocation 特性门控 和 resource.k8s.io/v1alpha1 API 组 时才启用。 kube-scheduler、kube-controller-manager 和 kubelet 也需要设置该特性门控。快速检查 Kubernetes 集群是否支持该功能的方法是列出 ResourceClass 对象:
kubectl get resourceclasses
如果集群支持动态资源分配,则响应是 ResourceClass 对象列表或:
No resources found
如果不支持,则会输出如下错误:
error: the server doesn't have a resource type "resourceclasses"
kube-scheduler 的默认配置仅在启用特性门控且使用 v1 配置 API 时才启用 “DynamicResources” 插件。 自定义配置可能需要被修改才能启用它。
除了在集群中启用该功能外,还必须安装资源驱动程序。