// pkg/scheduler/webhook.go#L52func(h*webhook)Handle(_context.Context,reqadmission.Request)admission.Response{pod:=&corev1.Pod{}err:=h.decoder.Decode(req,pod)iferr!=nil{klog.Errorf("Failed to decode request: %v",err)returnadmission.Errored(http.StatusBadRequest,err)}iflen(pod.Spec.Containers)==0{klog.Warningf(template+" - Denying admission as pod has no containers",req.Namespace,req.Name,req.UID)returnadmission.Denied("pod has no containers")}klog.Infof(template,req.Namespace,req.Name,req.UID)hasResource:=falseforidx,ctr:=rangepod.Spec.Containers{c:=&pod.Spec.Containers[idx]ifctr.SecurityContext!=nil{ifctr.SecurityContext.Privileged!=nil&&*ctr.SecurityContext.Privileged{klog.Warningf(template+" - Denying admission as container %s is privileged",req.Namespace,req.Name,req.UID,c.Name)continue}}for_,val:=rangedevice.GetDevices(){found,err:=val.MutateAdmission(c)iferr!=nil{klog.Errorf("validating pod failed:%s",err.Error())returnadmission.Errored(http.StatusInternalServerError,err)}hasResource=hasResource||found}}if!hasResource{klog.Infof(template+" - Allowing admission for pod: no resource found",req.Namespace,req.Name,req.UID)//return admission.Allowed("no resource found")}elseiflen(config.SchedulerName)>0{pod.Spec.SchedulerName=config.SchedulerName}marshaledPod,err:=json.Marshal(pod)iferr!=nil{klog.Errorf(template+" - Failed to marshal pod, error: %v",req.Namespace,req.Name,req.UID,err)returnadmission.Errored(http.StatusInternalServerError,err)}returnadmission.PatchResponseFromRaw(req.Object.Raw,marshaledPod)}
逻辑比较简单:
1)判断 Pod 是否需要使用 HAMi-Scheduler 进行调度
2)需要的话就修改 Pod 的 SchedulerName 字段为 hami-scheduler(名字可配置)
至此,核心部分就是如何判断该 Pod 是否需要使用 hami-scheduler 进行调度呢?
如何判断是否使用 hami-scheduler
Webhook 中主要根据 Pod 是否申请 vGPU 资源来确定,不过也有一些特殊逻辑。
特权模式 Pod
首先对于特权模式的 Pod,HAMi 是直接忽略的
1
2
3
4
5
6
ifctr.SecurityContext!=nil{ifctr.SecurityContext.Privileged!=nil&&*ctr.SecurityContext.Privileged{klog.Warningf(template+" - Denying admission as container %s is privileged",req.Namespace,req.Name,req.UID,c.Name)continue}}
因为开启特权模式之后,Pod 可以访问宿主机上的所有设备,再做限制也没意义了,因此这里直接忽略。
具体判断逻辑
然后根据 Pod 中的 Resource 来判断是否需要使用 hami-scheduler 进行调度:
1
2
3
4
5
6
7
8
for_,val:=rangedevice.GetDevices(){found,err:=val.MutateAdmission(c)iferr!=nil{klog.Errorf("validating pod failed:%s",err.Error())returnadmission.Errored(http.StatusInternalServerError,err)}hasResource=hasResource||found}
如果 Pod Resource 中有申请 HAMi 这边支持的 vGPU 资源则,那么就需要使用 HAMi-Scheduler 进行调度。
func(dev*NvidiaGPUDevices)MutateAdmission(ctr*corev1.Container)(bool,error){/*gpu related */priority,ok:=ctr.Resources.Limits[corev1.ResourceName(ResourcePriority)]ifok{ctr.Env=append(ctr.Env,corev1.EnvVar{Name:api.TaskPriority,Value:fmt.Sprint(priority.Value()),})}_,resourceNameOK:=ctr.Resources.Limits[corev1.ResourceName(ResourceName)]ifresourceNameOK{returnresourceNameOK,nil}_,resourceCoresOK:=ctr.Resources.Limits[corev1.ResourceName(ResourceCores)]_,resourceMemOK:=ctr.Resources.Limits[corev1.ResourceName(ResourceMem)]_,resourceMemPercentageOK:=ctr.Resources.Limits[corev1.ResourceName(ResourceMemPercentage)]ifresourceCoresOK||resourceMemOK||resourceMemPercentageOK{ifconfig.DefaultResourceNum>0{ctr.Resources.Limits[corev1.ResourceName(ResourceName)]=*resource.NewQuantity(int64(config.DefaultResourceNum),resource.BinarySI)resourceNameOK=true}}if!resourceNameOK&&OverwriteEnv{ctr.Env=append(ctr.Env,corev1.EnvVar{Name:"NVIDIA_VISIBLE_DEVICES",Value:"none",})}returnresourceNameOK,nil}
首先判断如果 Pod 申请的 Resource 中有对应的 ResourceName 就直接返回 true
对于上述满足条件的 Pod,需要由 HAMi-Scheduler 进行调度,Webhook 中会将 Pod 的 spec.schedulerName 改成 hami-scheduler。
具体如下:
1
2
3
4
5
6
if!hasResource{klog.Infof(template+" - Allowing admission for pod: no resource found",req.Namespace,req.Name,req.UID)//return admission.Allowed("no resource found")}elseiflen(config.SchedulerName)>0{pod.Spec.SchedulerName=config.SchedulerName}
这样该 Pod 就会由 HAMi-Scheduler 进行调度了,接下来就是 HAMi-Scheduler 开始工作了。
这里也有一个特殊逻辑:如果创建时直接指定了 nodeName,那 Webhook 就会直接拒绝,因为指定 nodeName 说明 Pod 都不需要调度了,会直接到指定节点启动,但是没经过调度,可能该节点并没有足够的资源。
1
2
3
4
ifpod.Spec.NodeName!=""{klog.Infof(template+" - Pod already has node assigned",req.Namespace,req.Name,req.UID)returnadmission.Denied("pod has node assigned")}
3. 小结
该 Webhook 的作用为:将申请了 vGPU 资源的 Pod 的调度器修改为 hami-scheduler,后续使用 hami-scheduler 进行调度。
也存在一些特殊情况:
对于开启特权模式的 Pod Webhook 会忽略,不会将其切换到 hami-scheduler 进行调度,而是依旧使用 default-scheduler。
对于直接指定了 nodeName 的 Pod, Webhook 会直接拒绝,拦截掉 Pod 的创建。
基于以上特殊情况,可能会出现以下问题,也是社区中多次有同学反馈的:
特权模式 Pod 申请了 gpucore、gpumem 等资源,创建后一直处于 Pending 状态, 无法调度,提示节点上没有 gpucore、gpumem 等资源。