Kubelet之Topology Manager分析

简介: Topology Manager是kubelet的一个组件,在kubernetes 1.16加入,而kubernetes 1.18中该feature变为beta版。本篇文档将分析Topology Manager的具体工作原理。1.为什么需要Topology Manager现代计算机的CPU架构多采用NUMA(Non-Uniform Memory Access,非统一内存)架构。NUMA就是将cpu

Topology Manager是kubelet的一个组件,在kubernetes 1.16加入,而kubernetes 1.18中该feature变为beta版。本篇文档将分析Topology Manager的具体工作原理。

1.为什么需要Topology Manager

现代计算机的CPU架构多采用NUMA(Non-Uniform Memory Access,非统一内存)架构。NUMA就是将cpu资源分开,以node 为单位进行分组,每个node都有着独有的cpu、memory等资源,当一个NUMA节点内的资源相交互时,性能将会有很大的提升;但是,如果是两个NUMA节点之间的资源交互将会变得很慢。

下面这幅图中有两个NUMA节点存在:

  • NUMA0:由cpu0、cpu1、cpu2、cpu3以及gpu0、nic0和一块本地内存组成
  • NUMA1:由cpu4、cpu5、cpu6、cpu7以及gpu1、nic1和一块本地内存组成

假设某个pod需要的资源清单如下:

  • 4个CPU
  • 200MB内存
  • 1个GPU
  • 1个NIC

我们知道,在kubelet中cpu和其他外围设备(比如GPU)的分配由不同的组件完成,cpu的分配由CPU Manager完成,外围设备由Device Manager完成。它们在给pod分配设备时,都是独立工作的,不会有一个全局观念,这会造成一个什么问题呢?在这个例子中,对于该pod而言比较好的资源组合有两个:

  • 组合1:cpu0、cpu1、cpu2、cpu3、gpu0、nic0
  • 组合2:cpu4、cpu5、cpu6、cpu7、gpu1、nic1

之所以称为比较好的组合,因为这些资源都在一个NUMA节点内。但是CPU Manager和Device Manager是独立工作的,它们不会感知对方给出的分配方案与自己给出的分配方案是不是最优的组合,于是就有可能出现下面这种组合:

  • 组合3:cpu0、cpu1、cpu2、cpu3、gpu1、nic1

这个分配方案就不是我们想要的。Topology Manager就是为了解决这个问题而设计的,它的目标就是要找到我们例子中的组合1和组合2。

2.什么是TopologyHint

TopologyHint用中文描述为“拓扑提示”,在Topology Manager中,TopologyHint的定义如下:

type TopologyHint struct {
    NUMANodeAffinity bitmask.BitMask
    Preferred bool
}

其中NUMANodeAffinity是用bitmask表示的NUMA节点的组合。举个例子,假设有两个NUMA节点(编号分别为0和1),那么可能出现的组合为:[0]、[1]、[0,1],用bitmask表示为:01,10,11(从右往左开始,组合中有哪一个NUMA节点,那一位就是1)。

Preferred代表这个NUMA节点组合对于某个pod而言是不是“优先考虑的”,某个TopologyHint对于pod而言是不是“优先考虑的”需要遵循如下的规则:在满足申请资源个数的前提下,选择的资源所涉及的NUMA节点个数最少,就是“优先考虑的”。怎么理解这句话?我们举个例子——假设现在有两个NUMA节点(编号为0和1),每个NUMA节点上都有两个cpu,如果某个pod需要请求两个cpu,那么TopologyHint有如下几个:

  • {01: True}代表从NUMA0上分配两个cpu给pod,这两个cpu都在一个NUMA节点上,涉及的NUMA节点个数最少(为1),所以是“优先考虑的”。
  • {10: True}代表从NUMA1上分配两个cpu给pod,这两个cpu也在一个NUMA节点上,涉及的NUMA节点个数也最少(为1),所以是“优先考虑的”。
  • {11: False}代表从NUMA0和NUMA1上各取一个cpu,涉及的NUMA节点个数为2,所以不是“优先考虑的”。

那么,是不是所分配的资源必须在一个NUMA节点内,这个方案对于pod而言才是“优先考虑的”呢?——当然不是,比如现在有两个NUMA节点,每个NUMA节点都只有1块GPU,而某个pod申请了2个GPU,此时{11: True}这个TopologyHint就是“优先考虑的”,因为在满足申请资源个数的前提下,最少要涉及到2个NUMA节点。

3.Topology Manager的四种策略

Topology Manager提供了四种策略供用户组合各个资源的TopologyHint。这四种策略是:

  • none: 什么也不做,与没有开启Topology Manager的效果一样。
  • best-effort:  允许Topology Manager通过组合各个资源提供的TopologyHint,而找到一个最优的TopologyHint,如果没有找到也没关系,节点也会接纳这个Pod。
  • restricted: 允许Topology Manager通过组合各个资源提供的TopologyHint,而找到一个最优的TopologyHint,如果没有找到,那么节点会拒绝接纳这个Pod,如果Pod遭到节点拒绝,其状态将变为Terminated。
  • single-numa-node: 允许Topology Manager通过组合各个资源提供的TopologyHint,而找到一个最优的TopologyHint,并且这个最优的TopologyHint所涉及的NUMA节点个数是1。如果没有找到,那么节点会拒绝接纳这个Pod,如果Pod遭到节点拒绝,其状态将变为Terminated。

至于Topology Manager是怎样组合各个资源提供的TopologyHint,并且找到一个最优的TopologyHint这个问题,我们会在后面详细阐述。

4.怎样开启Topology Manager

如果kubernetes版本为1.18及其以上的版本,直接在kubelet的启动项中添加:

--topology-manager-policy=
    [none | best-effort | restricted | single-numa-node]

如果kubernetes版本为1.16到1.18之间,还需要在kubelet启动项中添加:

--feature-gates="...,TopologyManager=<true|false>"

5.什么是HintProvider

在kubelet源码中,HintProvider的定义如下:

type HintProvider interface {
    // 根据container请求的资源数产生一组TopologyHint
    GetTopologyHints(*v1.Pod, *v1.Container) map[string][]TopologyHint
    // 根据container请求的资源数为container分配具体的资源
    Allocate(*v1.Pod, *v1.Container) error
}

其中GetTopologyHints这个函数用于为某个container产生某种或多种资源的TopologyHint数组。举个例子,假设有两个NUMA节点(编号为0和1),NUMA0上有cpu1和cpu2,NUMA1上有cpu3和cpu4,某个pod请求两个cpu。那么CPU Manager这个HintProvider会调用GetTopologyHints产生如下的TopologyHint:

  • {01: True}代表从NUMA0取2个cpu,并且是“优先考虑的”。
  • {10: True}代表从NUMA1取2个cpu,并且是“优先考虑的”。
  • {11: False}代表从NUM0和NUMA1各取一个cpu,不是“优先考虑的”。

当前在kubelet中充当HintProvider的总共有两个组件:一个是CPU Manager,另外一个是Device Manager,这两个组件都实现了HintProvider这个接口的两个方法,后续会把HugePages组件加入进来。

另外需要注意的是:GetTopologyHints(*v1.Pod, *v1.Container) map[string][]TopologyHint函数的返回类型是map[string][]TopologyHint,为什么会是这种类型呢?这是为Device Manager设计的,因为Device Manager需要组合多种资源(比如GPU、NIC),每种资源都返回一组TopologyHint。

6.Topology Manager工作原理

下面这段伪代码说明了Topology Manager的主要工作原理:

for container := range append(InitContainers, Containers...) {
    // 遍历每一个HintProvider
    for provider := range HintProviders {
        // 对每一个HintProvider,调用GetTopologyHints获取一组或多组TopologyHint
        hints += provider.GetTopologyHints(container)
    }
    // 将所有的TopologyHint进行合并操作
    bestHint := policy.Merge(hints)
    // 通过合并找到最优的TopologyHint,然后代入每一个HintProvider的Allocate函数中
    // 为container分配资源
    for provider := range HintProviders {
        provider.Allocate(container, bestHint)
    }
}

用一幅图说明一下其原理:

  • 遍历pod中的每一个容器
  • 对于每一个容器,使用所有的HintProvider的GetTopologyHints方法产生TopologyHint
  • 对这些TopologyHint做合并操作,寻求一个最优的TopologyHint
  • 每个HintProvider通过最优的TopologyHint给容器分配相应的资源
  • 根据设置的不同的策略,是否允许节点接纳这个pod

接下来对每个阶段进行详细说明。

6.1 CPU Manager的GetTopologyHints实现

前面说过,目前可作为HintProvider的组件有两个:CPU Manager和Device Manager。那么这两个组件是如何为给定的pod产生一组(或多组)TopologyHint的呢?本节首先分析CPU Manager。

CPU Manager的GetTopologyHints方法主要是调用了其policy的GetTopologyHints方法。而CPU Manager的static policy对该方法的实现如下:

func (p *staticPolicy) GetTopologyHints(s state.State, pod *v1.Pod, container *v1.Container) map[string][]topologymanager.TopologyHint {
  // 省略其他非关键性代码
  ......
  // 产生TopologyHint的主要逻辑由这个函数完成
	cpuHints := p.generateCPUTopologyHints(available, reusable, requested)
  // 可以看到,只返回了一种资源的TopologyHint,那就是cpu
	return map[string][]topologymanager.TopologyHint{
		string(v1.ResourceCPU): cpuHints,
	}
}

主要的逻辑都是由generateCPUTopologyHints这个函数完成,generateCPUTopologyHints内容如下:

func (p *staticPolicy) generateCPUTopologyHints(availableCPUs cpuset.CPUSet, reusableCPUs cpuset.CPUSet, request int) []topologymanager.TopologyHint {
  // 在满足容器申请资源数的前提下,TopologyHint涉及到的最少的NUMA节点个数
  // 初始值为k8s节点上所有NUMA节点的个数。
	minAffinitySize := p.topology.CPUDetails.NUMANodes().Size()
	// 在满足容器申请资源数的前提下,TopologyHint涉及到的最少的Socket个数
  // 初始值为k8s节点上所有Socket的个数。
	minSocketsOnMinAffinity := p.topology.CPUDetails.Sockets().Size()

	// 用于保存所有TopologyHint
	hints := []topologymanager.TopologyHint{}
  // bitmask.IterateBitMasks这个函数用于将k8s节点上所有的NUMA节点求组合,然后通过回调函数处理这个组合。
  // 例如某个k8s节点上有3个NUMA节点(编号为0,1,2),那么所有组合有
  // [[0],[1],[2],[0,1],[0,2],[1,2],[0,1,2]]
	bitmask.IterateBitMasks(p.topology.CPUDetails.NUMANodes().ToSlice(), func(mask bitmask.BitMask) {
    // 取出NUMA节点组合(以bitmask形式表示)中所涉及到的cpu
		cpusInMask := p.topology.CPUDetails.CPUsInNUMANodes(mask.GetBits()...).Size()
    // 取出NUMA节点组合(以bitmask形式表示)中所涉及到的Socket
		socketsInMask := p.topology.CPUDetails.SocketsInNUMANodes(mask.GetBits()...).Size()
    // 如果NUMA节点组合中所涉及到的cpu个数比请求的cpu数大,并且这个组合所涉及的NUMA节点个数
    // 是目前为止所有组合中最小的,那么就更新它。
		if cpusInMask >= request && mask.Count() < minAffinitySize {
			minAffinitySize = mask.Count()
			if socketsInMask < minSocketsOnMinAffinity {
				minSocketsOnMinAffinity = socketsInMask
			}
		}

	  // 下面这两个for循环用户统计当前k8s节点可用的cpu中,有哪些是属于当前正在处理的NUMA节点组合
		numMatching := 0
		for _, c := range reusableCPUs.ToSlice() {
			// Disregard this mask if its NUMANode isn't part of it.
			if !mask.IsSet(p.topology.CPUDetails[c].NUMANodeID) {
				return
			}
			numMatching++
		}
		for _, c := range availableCPUs.ToSlice() {
			if mask.IsSet(p.topology.CPUDetails[c].NUMANodeID) {
				numMatching++
			}
		}

		// 如果当前组合中可用的cpu数比请求的cpu小,那么就直接返回
		if numMatching < request {
			return
		}
    // 否则就创建一个TopologyHint,并把它加入到hints这个slice中
		hints = append(hints, topologymanager.TopologyHint{
			NUMANodeAffinity: mask,
			Preferred:        false,
		})
	})

  // 这一步表示拿到所有的TopologyHint后,开始对哪些TopologyHint标注“Preferred = true”
  // 这些TopologyHint会被标注为“Preferred = true”:
  // (1)涉及到的NUMA节点个数最少
  // (2)涉及到的socket个数最少
	for i := range hints {
		if hints[i].NUMANodeAffinity.Count() == minAffinitySize {
			nodes := hints[i].NUMANodeAffinity.GetBits()
			numSockets := p.topology.CPUDetails.SocketsInNUMANodes(nodes...).Size()
			if numSockets == minSocketsOnMinAffinity {
				hints[i].Preferred = true
			}
		}
	}

	return hints
}

总结一下这个函数:

  • 创建一个存放TopologyHint的数组,名称为hints。
  • 根据k8s节点上所有的NUMA节点ID求所有的NUMA节点组合。
  • 找出这些组合中涉及NUMA节点个数的最小值,将这个值设置为minAffinitySize。
  • 找出这些组合中涉及到Socket个数的最小值,将这个值设置为minSocketsOnMinAffinity。
  • 对每个组合,检查当前k8s节点上可用的cpu与该组合所涉及的cpu的交集的个数是否大于容器申请的cpu数,如果比容器申请的cpu数小,那么就不创建TopologyHint,否则就创建一个TopologyHint,并放入hints中。
  • 检查hints中所有的TopologyHint,如果该TopologyHint涉及到的NUMA节点数与minAffinitySize值相同,并且该TopologyHint所涉及到的Socket数与minSocketsOnMinAffinity相同,那么将该TopologyHint的Preferred设置为true。

以一张图来说明一下整个流程,图中有3个NUMA节点,每个节点有2个cpu,假设某个pod请求2个cpu以及已知当前k8s节点上空闲的cpu,寻找TopologyHint过程如图:

6.2 Device Manager的GetTopologyHints实现

DeviceManager的GetTopologyHint函数实现与CPU Manager的GetTopologyHint函数实现基本一致,该函数主要调用generateDeviceTopologyHints这个函数,generateDeviceTopologyHints函数内容如下:

func (m *ManagerImpl) generateDeviceTopologyHints(resource string, available sets.String, reusable sets.String, request int) []topologymanager.TopologyHint {
	// 初始化minAffinitySize为k8s节点中NUMA节点个数
	minAffinitySize := len(m.numaNodes)

	// 获取所有NUMA节点组合
	hints := []topologymanager.TopologyHint{}
	bitmask.IterateBitMasks(m.numaNodes, func(mask bitmask.BitMask) {
        // 对每一个NUMA组合做如下处理
		// First, update minAffinitySize for the current request size.
        // devicesInMask用于统计该NUMA组合涉及到device个数
		devicesInMask := 0
        // 获取某种资源下的所有设备(比如获取gpu资源的所有GPU卡),并检查该device是否在当前NUMA组合中
        // 如果在,devicesInMask值加1
		for _, device := range m.allDevices[resource] {
			if mask.AnySet(m.getNUMANodeIds(device.Topology)) {
				devicesInMask++
			}
		}
        // 如果当前NUMA组合涉及到的device数量比request当,并且当前NUMA组合中包含的NUMA个数
        // 比minAffinitySize还小,那么更新minAffinitySize的值。
		if devicesInMask >= request && mask.Count() < minAffinitySize {
			minAffinitySize = mask.Count()
		}

		// numMatching用于获取当前NUMA组合中空闲的device数
		numMatching := 0
		for d := range reusable {
			// Skip the device if it doesn't specify any topology info.
			if m.allDevices[resource][d].Topology == nil {
				continue
			}
			// Otherwise disregard this mask if its NUMANode isn't part of it.
            // 如果reusable中的device的NUMA节点ID不在当前这个NUMA组合中,那么直接返回
            // 不对这个NUMA组合创建TopologyHint,这样做的原因是保证reusable中的device
            // 优先被使用完
			if !mask.AnySet(m.getNUMANodeIds(m.allDevices[resource][d].Topology)) {
				return
			}
			numMatching++
		}

		// Finally, check to see if enough available devices remain on the
		// current NUMA node combination to satisfy the device request.
		for d := range available {
			if mask.AnySet(m.getNUMANodeIds(m.allDevices[resource][d].Topology)) {
				numMatching++
			}
		}

		// 如果当前NUMA组合中可用的device比请求的device数还少,那么直接返回
		if numMatching < request {
			return
		}
        // 创建TopologyHint
		hints = append(hints, topologymanager.TopologyHint{
			NUMANodeAffinity: mask,
			Preferred:        false,
		})
	})
    // 如果某个TopologyHint所涉及的NUMA数最少,那么将该TopologyHint的Preferred设置为true
	for i := range hints {
		if hints[i].NUMANodeAffinity.Count() == minAffinitySize {
			hints[i].Preferred = true
		}
	}

	return hints
}

稍微总结一下:

  • 创建一个存放TopologyHint的数组,名称为hints。
  • 根据k8s节点上所有的NUMA节点ID求所有的NUMA节点组合。
  • 找出这些组合中涉及NUMA节点个数的最小值,将这个值设置为minAffinitySize。
  • 对每个组合,检查当前k8s节点上某种资源(比如GPU)可用的设备数与该组合所涉及的该资源的设备数的交集的个数是否大于容器申请的设备数,如果比容器申请的设备数小,那么就不创建TopologyHint,否则就创建一个TopologyHint,并放入hints中。
  • 检查hints中所有的TopologyHint,如果该TopologyHint涉及到的NUMA节点数与minAffinitySize值相同,那么将该TopologyHint的Preferred设置为true。

6.3 TopologyHint的merge操作

前面已经说到了CPU Manager和Device Manager会产生多组TopologyHint。那么如何合并这些TopologyHint,找到最优的那个TopologyHint呢?来看看是怎样实现的。

以下面这幅图做说明,在这幅图中总共有3个NUMA节点,对于某个容器而言,CPU Manager找出了CPU资源的一组TopologyHint,Device Manager找出了GPU和NIC的TopologyHint。整个merge流程如下:

  • 从每一组资源类型中拿出一个TopologyHint组合成一个新的TopologyHint组合。
  • 在这个新的TopologyHint组合内,寻找它们公共的NUMA节点。并且只有当这个组合内所有的TopologyHint的Preferred域都为true时,合并后的TopologyHint的Preferred域才为True。
  • 从合并后的TopologyHint中寻找最优的TopologyHint(即TopologyHint的Preferred域为True)。

前面提到过Topology Manager的四种策略,现在重点说一下四种策略中的后面三种:

  • best-effort:  结合上图来说,如果没有找到最优的TopologyHint(即图中的TH6),k8s节点也会接纳这个Pod。
  • restricted: 结合上图来说,如果没有找到最优的TopologyHint(即图中的TH6),那么节点会拒绝接纳这个Pod,如果Pod遭到节点拒绝,其状态将变为Terminated。
  • single-numa-node: 结合上图来说,如果没有找到最优的TopologyHint(即图中的TH6,并且NUMA节点个数为1),那么节点会拒绝接纳这个Pod,如果Pod遭到节点拒绝,其状态将变为Terminated。

另外需要说明的是,在为容器分配相应的资源时,CPU Manager和Device Manager会优先考虑在最优的TopologyHint所涉及的NUMA节点上为容器分配资源,如果这些NUMA节点上的资源不够,还会从其他NUMA节点上为容器分配。

6.4 何时会进行分配操作

也就是说这些HintProvider何时会执行其Allocate函数为容器分配资源?在Topology Manager中有一个Admit函数,会遍历所有的HintProvider,执行HintProvider的Allocate函数。而Topology Manager的Admit函数会在kubelet判断一个pod是否被节点接纳的时候执行(kubelet调用所有的PodAdmitHandler,只要有一个PodAdmitHandler给出拒绝意见,那么节点将不会接纳该pod),因为Topology Manager也是一个PodAdmitHandler。

7.参考文档

https://kubernetes.io/blog/2020/04/01/kubernetes-1-18-feature-topoloy-manager-beta/

https://github.com/kubernetes/enhancements/pull/1121

相关实践学习
深入解析Docker容器化技术
Docker是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的容器中,然后发布到任何流行的Linux机器上,也可以实现虚拟化,容器是完全使用沙箱机制,相互之间不会有任何接口。Docker是世界领先的软件容器平台。开发人员利用Docker可以消除协作编码时“在我的机器上可正常工作”的问题。运维人员利用Docker可以在隔离容器中并行运行和管理应用,获得更好的计算密度。企业利用Docker可以构建敏捷的软件交付管道,以更快的速度、更高的安全性和可靠的信誉为Linux和Windows Server应用发布新功能。 在本套课程中,我们将全面的讲解Docker技术栈,从环境安装到容器、镜像操作以及生产环境如何部署开发的微服务应用。本课程由黑马程序员提供。 &nbsp; &nbsp; 相关的阿里云产品:容器服务 ACK 容器服务 Kubernetes 版(简称 ACK)提供高性能可伸缩的容器应用管理能力,支持企业级容器化应用的全生命周期管理。整合阿里云虚拟化、存储、网络和安全能力,打造云端最佳容器化应用运行环境。 了解产品详情: https://www.aliyun.com/product/kubernetes
目录
相关文章
|
弹性计算 容器 RDMA
在Kubernetes上使用RDMA
### RDMA RDMA(全称RemoteDirect Memory Access) 它为了解决网络传输中服务器端数据处理的延迟而产生。 它的原理是将待传输的数据从一台计算机的内存,直接传输到另一台计算机的内存,整个传输过程无需操作系统和协议栈的介入。
10417 0
|
存储 Kubernetes Cloud Native
一文读懂容器存储接口 CSI
在《一文读懂 K8s 持久化存储流程》一文我们重点介绍了 K8s 内部的存储流程,以及 PV、PVC、StorageClass、Kubelet 等之间的调用关系。接下来本文将将重点放在 CSI(Container Storage Interface)容器存储接口上,探究什么是 CSI 及其内部工作原理。
一文读懂容器存储接口 CSI
|
存储 Dragonfly 缓存
Nydus:开源的下一代容器镜像加速服务
让更多的容器用户能够体验到容器快速启动和安全加载方面的能力。
7930 0
Nydus:开源的下一代容器镜像加速服务
|
Kubernetes Linux API
[没接触过kubevirt?]15分钟快速入门kubevirt
什么是kubevirt? kubevirt是一个容器方式运行虚拟机的项目。`kubevirt`是附加`kubernetes`集群上的,它是通过 `CustomResourceDefinition(CRD)`部署到`Kubernetes API`变成资源对象。使用方式类似创建`deploy、pod`......这些资源清单。
5455 0
[没接触过kubevirt?]15分钟快速入门kubevirt
|
JSON Kubernetes 数据格式
K8S client-go Patch example
我在本文中主要会介绍使用client-go的Patch方式,主要包括strategic merge patch和json-patch
|
存储 弹性计算 资源调度
K8S下一代设备管理机制:DRA
背景Kubernetes从1.8开始引入了Device Plugin机制,用于第三方设备厂商以插件化的方式将设备资源(GPU、RDMA、FPGA、InfiniBand等)接入Kubernetes集群中。用户无需修改Kubernetes代码,只需在集群中以DaemonSet方式部署设备厂商提供的插件,然后在Pod中申明使用该资源的使用量,容器在启动成功后,便可在容器中发现该设备。然而,随着Kuber
3941 2
K8S下一代设备管理机制:DRA
|
Prometheus Kubernetes 监控
NVIDIA GPU Operator分析六:NVIDIA GPU Operator原理分析
背景我们知道,如果在Kubernetes中支持GPU设备调度,需要做如下的工作:节点上安装nvidia驱动节点上安装nvidia-docker集群部署gpu device plugin,用于为调度到该节点的pod分配GPU设备。除此之外,如果你需要监控集群GPU资源使用情况,你可能还需要安装DCCM exporter结合Prometheus输出GPU资源监控信息。要安装和管理这么多的组件,对于运维
3354 0
|
Kubernetes API 调度
Container Runtime CDI与NRI介绍
CDI介绍什么是CDICDI(Container Device Interface)是Container Runtimes支持挂载第三方设备(比如:GPU、FPGA等)机制。它引入了设备作为资源的抽象概念,这类设备由一个完全限定的名称唯一指定,该名称由设备商ID,设备类别与一个设备类别下的一个唯一名称组成,格式如下:vendor.com/class=unique_name设备商ID和设备类型(ve
6027 1
Container Runtime CDI与NRI介绍
|
开发者 异构计算 容器
NRI - 在容器的生命周期焊接“芯片”
个人调研containerd 1.7版本新特性NRI,欢迎指正
819 1
|
Kubernetes 测试技术 API
快速体验NVIDIA在k8s中下一代设备管理插件
背景Kubernetes 1.26开始引入DRA( Dynamic Resource Allocation,动态资源分配),用于解决Kubernetes现有Device Plugin机制的不足。相比于现有的Device Plugin机制,DRA更加开放和自主,能够满足一些复杂的使用场景。NVIDIA、Intel这些设备厂商也基于DRA开放自己下一代Device Plugin,以期满足更复杂的业务场
1685 0

热门文章

最新文章