apiVersion:apps/v1kind:Deploymentmetadata:name:time-slicing-verificationlabels:app:time-slicing-verificationspec:replicas:2selector:matchLabels:app:time-slicing-verificationtemplate:metadata:labels:app:time-slicing-verificationspec:tolerations:- key:nvidia.com/gpuoperator:Existseffect:NoSchedulehostPID:truecontainers:- name:cuda-sample-vector-addimage:"nvcr.io/nvidia/k8s/cuda-sample:vectoradd-cuda11.7.1-ubuntu20.04"command:["/bin/bash","-c","--"]args:- while true; do /cuda-samples/vectorAdd; doneresources:limits:nvidia.com/gpu:1
会启动 5 个 Pod,
查看情况
1
2
3
4
5
6
7
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
time-slicing-verification-7cdc7f87c5-lkd9d 1/1 Running 0 23s
time-slicing-verification-7cdc7f87c5-rrzq7 1/1 Running 0 23s
time-slicing-verification-7cdc7f87c5-s8qwk 1/1 Running 0 23s
time-slicing-verification-7cdc7f87c5-xhmb7 1/1 Running 0 23s
time-slicing-verification-7cdc7f87c5-zsncp 1/1 Running 0 23s
5 个 Pod 都启动了,说明时间片时成功的。
随便查看一个 Pod 的日志
1
2
3
4
5
6
7
8
9
10
11
12
13
$ kubectl logs deploy/time-slicing-verification
Found 5 pods, using pod/time-slicing-verification-7cdc7f87c5-s8qwk
[Vector addition of 50000 elements]Copy input data from the host memory to the CUDA device
CUDA kernel launch with 196 blocks of 256 threads
Copy output data from the CUDA device to the host memory
Test PASSED
Done
[Vector addition of 50000 elements]Copy input data from the host memory to the CUDA device
CUDA kernel launch with 196 blocks of 256 threads
Copy output data from the CUDA device to the host memory
...
有 Test PASSED 则说明成功了。
说明 TimeSlicing 配置生效了。
4. 使用 Node 级别的单独配置
前面只创建了一个名称为 any 的配置,并在 clusterpolicy 中指明了使用该配置为默认配置,因此集群中的全部节点都会使用该配置来做时间片。
// api/config/v1/config.go#L32// Config is a versioned struct used to hold configuration information.typeConfigstruct{Versionstring`json:"version" yaml:"version"`FlagsFlags`json:"flags,omitempty" yaml:"flags,omitempty"`ResourcesResources`json:"resources,omitempty" yaml:"resources,omitempty"`SharingSharing`json:"sharing,omitempty" yaml:"sharing,omitempty"`}
// internal/rm/device_map.go#L282// updateDeviceMapWithReplicas returns an updated map of resource names to devices with replica// information from the active replicated resources config.funcupdateDeviceMapWithReplicas(replicatedResources*spec.ReplicatedResources,oDevicesDeviceMap)(DeviceMap,error){devices:=make(DeviceMap)// Begin by walking replicatedResources.Resources and building a map of just the resource names.names:=make(map[spec.ResourceName]bool)for_,r:=rangereplicatedResources.Resources{names[r.Name]=true}// Copy over all devices from oDevices without a resource reference in TimeSlicing.Resources.forr,ds:=rangeoDevices{if!names[r]{devices[r]=ds}}// Walk shared Resources and update devices in the device map as appropriate.for_,resource:=rangereplicatedResources.Resources{r:=resource// Get the IDs of the devices we want to replicate from oDevicesids,err:=oDevices.getIDsOfDevicesToReplicate(&r)iferr!=nil{returnnil,fmt.Errorf("unable to get IDs of devices to replicate for '%v' resource: %v",r.Name,err)}// Skip any resources not matched in oDevicesiflen(ids)==0{continue}// Add any devices we don't want replicated directly into the device map.for_,d:=rangeoDevices[r.Name].Difference(oDevices[r.Name].Subset(ids)){devices.insert(r.Name,d)}// Create replicated devices add them to the device map.// Rename the resource for replicated devices as requested.name:=r.Nameifr.Rename!=""{name=r.Rename}for_,id:=rangeids{fori:=0;i<r.Replicas;i++{annotatedID:=string(NewAnnotatedID(id,i))replicatedDevice:=*(oDevices[r.Name][id])replicatedDevice.ID=annotatedIDreplicatedDevice.Replicas=r.Replicasdevices.insert(name,&replicatedDevice)}}}returndevices,nil}
可以看到,这里是双层 for 循环,对 device 数量进行了一个复制的操作,这样每张 GPU 都可以被使用 Replicas 次了。
其他属性都没变,只是把 deviceID 进行了处理,便于区分
1
2
3
4
// NewAnnotatedID creates a new AnnotatedID from an ID and a replica number.funcNewAnnotatedID(idstring,replicaint)AnnotatedID{returnAnnotatedID(fmt.Sprintf("%s::%d",id,replica))}
然后在真正挂载时则进行 split 拿到 id 和 replicas 信息
1
2
3
4
5
6
7
8
9
// Split splits a AnnotatedID into its ID and replica number parts.func(rAnnotatedID)Split()(string,int){split:=strings.SplitN(string(r),"::",2)iflen(split)!=2{returnstring(r),0}replica,_:=strconv.ParseInt(split[1],10,0)returnsplit[0],int(replica)}