• 阿里云在k8s中实现了在容器间对GPU的资源共享,具体实现为:
    aliyun/gpushare-scheduler-extender
    aliyun/gpushare-device-plugin
  • scheduler-extender与default scheduler的关联:
  1. 在default scheduler的默认启动参数中添加参数--config
    在这里插入图片描述
  2. extender-config.yaml中为
    在这里插入图片描述
  3. extender-polilcy.json中包含了定义的extender详细拓展接口,扩产verb动词支持filter、predicate、prioritize、preemption、bind
    在这里插入图片描述
  4. default scheduler 发送给extender scheduler的请求参数为:其中包含被调度的pod与候选节点。
    在这里插入图片描述
    extender根据verb会有不同的请求参数,例如bind的请求参数为
    在这里插入图片描述
  • 核心代码解析
  1. 关键问题:如何去限制GPU的算力?目前没有很好的方法去限制,但是可以控制GPU内存使用上限。cuda中有一个cuMemGetInfo接口可以得到GPU的空余资源数量;
    sharedegpu-device-plugin的代码中定义了一个MemoryUnit去分割一个GPU的资源实现调度层的虚拟化。
    在这里插入图片描述

代码中可以看到getGPUMemory其实就是被分割的GPUmemory的份数。
虚拟后的GPU个数=物理GPU数*单个GPU分割后的份数。

for i := uint(0); i < deviceNumber; i++ {
	for j := uint(0); j < getGPUMemory(i); j++{
		devs = append(devs, &pluginapi.Device{
				ID:     fakeID,
				Health: pluginapi.Healthy,
			}
	}
}
  1. Allocate的主要逻辑
    作者获取pending的pod,对pending的pod进行与allocate的pod资源进行对比,如果一致就分配资源。
    这里存在两个问题:1.为啥还要自己手动的去获取pod?2.allocate的pod与pending的pod只要分配资源数就当成一个,显然不正确。这种设计有些多此一举?

  2. scheduler-extender的核心代码:
    (1)predicate Handler:gpushare-scheduler-extender/pkg/scheduler/gpushare-predicate.go包下是predicate的主要逻辑:
    函数输入为: func(pod *v1.Pod, nodeName string, c *cache.SchedulerCache)

// 1. 获取node的详细信息
	nodeInfo, err := GetNodeInfo(nodeName)
// 2. 判断当前节点是否有GPU资源
	utils.IsGPUSharingNode(nodeInfo.GetNode()
// 3. 判断pod能否分配在这个节点上 
allocatable := nodeInfo.Assume(pod)

predicate的逻辑比较简单,需要注意的是使用了一个SchedulerCache缓存对nodeinfo进行缓存,在读取nodeinfo的时候需要加锁sync.RWMutex,防止数据不一致。

(2)bind Handler:主要逻辑为

pod = getPod(podId)
nodeInfo = GetNodeInfo(nodeName)
nodeInfo.Allocate(pod)

Allocate中包含的逻辑有:

//1. 更新pod的annotation信息,以供后续scheduler-extender使用
utils.GetUpdatedPodAnnotationSpec(pod, devId, n.GetTotalGPUMemory()/n.GetGPUCount())
// 2. 绑定节点
clientset.CoreV1().Pods(pod.Namespace).Bind(binding)
// 3. 绑定成功后更新设备信息
dev.addPod(newPod)
Logo

K8S/Kubernetes社区为您提供最前沿的新闻资讯和知识内容

更多推荐