HAMi Webhook 架构与流程说明
近期在梳理 HAMi Webhook 模块时,发现许多人对它的处理范围及具体细节理解不够清晰。基于 pkg/scheduler/webhook.go 的当前实现,本文整理了 Webhook 的准入流程与几个关键变更点,希望帮助大家全面掌握这一逻辑。
1. Webhook 处理范围
HAMi Webhook 并非拦截所有 Pod——它仅关注以下三类 Pod:
spec.schedulerName为空的 Pod- 已显式指定 HAMi 调度器的 Pod
schedulerName为default-scheduler,且开启了ForceOverwriteDefaultScheduler=true的 Pod
一旦 Pod 已被指派给其他调度器,Webhook 会直接放行,不再执行任何与 HAMi 相关的变更。这个筛选门槛相当清晰:不属于你的职责范围,就不要干预。
2. Webhook 工作时序图
3. 以 NVIDIA 设备为例的变更点
以 NVIDIA 设备实现为例,MutateAdmission 不仅检查资源是否存在,还会直接修改 Pod 和 Container 的内容。核心逻辑如下:
func (dev *NvidiaGPUDevices) MutateAdmission(ctr *corev1.Container, p *corev1.Pod) (bool, error) {/*gpu related */priority, ok := ctr.Resources.Limits[corev1.ResourceName(dev.config.ResourcePriority)]if ok { ctr.Env = append(ctr.Env, corev1.EnvVar{Name:util.TaskPriority,Value: fmt.Sprint(priority.Value()), })}if dev.config.GPUCorePolicy != "" && dev.config.GPUCorePolicy != DefaultCorePolicy { ctr.Env = append(ctr.Env, corev1.EnvVar{Name:util.CoreLimitSwitch,Value: string(dev.config.GPUCorePolicy), })}hasResource := dev.mutateContainerResource(ctr)if dev.defaultExclusiveCoreIfNeeded(ctr) { hasResource = true}if hasResource { // Set runtime class name if it is not set by user and the runtime class name is configured if p.Spec.RuntimeClassName == nil && dev.config.RuntimeClassName != "" {p.Spec.RuntimeClassName = &dev.config.RuntimeClassName }}if !hasResource && dev.config.OverwriteEnv { ctr.Env = append(ctr.Env, corev1.EnvVar{Name:"NVIDIA_VISIBLE_DEVICES",Value: "none", })}return hasResource, nil}这段代码主要完成了以下操作:
- 注入任务优先级相关的环境变量
- 根据
GPUCorePolicy的配置,注入核心策略对应的环境变量 - 若容器仅声明了
core/memory/memoryPercentage而未指定 GPU 数量,可能会自动补上ResourceCountName - 当容器使用 GPU 资源且用户未设置
RuntimeClassName时,Webhook 自动补全该字段 - 反之,如果容器未使用此类设备资源,且
OverwriteEnv功能开启,则注入NVIDIA_VISIBLE_DEVICES=none
4. 思考
4.1 特权容器不会直接导致整个 Pod 被拒绝
当前实现中,遇到特权容器时,处理方式是“跳过该容器的设备变更逻辑”,而非“直接拒绝整个 Pod”。更准确的表述是:
- 特权容器不会执行设备注入及相关修改
- Pod 不会因为某个容器为特权模式而被整体驳回
4.2 未识别到 HAMi 设备资源的 Pod 通常会放行
如果所有设备实现都判定 Pod 未使用 HAMi 管理的资源,那么 Webhook 既不会将其调度器改为 HAMi,也不会因“未申请 vGPU”而直接拒绝。简言之,不涉及自身资源就不做干预。
4.3 nodeName 拒绝是有前提条件的
Webhook 会在 nodeName 已设置时拒绝 Pod,但这一操作有严格前提:必须同时满足两个条件:
hasResource = true(表明 Pod 确实使用了 HAMi 设备)- 已配置 HAMi 的
SchedulerName
换句话说,并非所有带 nodeName 的 Pod 都会被拦截。
4.4 配额校验当前主要针对 NVIDIA
查看 fitResourceQuota() 的实现,内部注释写明“Only supports NVIDIA”。因此资源配额检查目前主要面向 NVIDIA 资源,其他设备暂未纳入统一校验范围。
5. Webhook 功能总结
整个 Webhook 在 Pod 创建阶段介入准入流程,首先判断 Pod 是否申请了 HAMi 管理的设备资源,然后按需补充资源字段、注入环境变量、设置 RuntimeClassName,最后将需要 HAMi 接管调度的 Pod 改写为 HAMi 调度器处理。
对于已指定其他调度器的 Pod,Webhook 直接放行,不进行干涉;对于使用了 HAMi 设备资源但显式设置了 nodeName 的 Pod,Webhook 会直接拒绝,以防绕过 HAMi 的调度策略。
资源配额检查通过后,Webhook 生成 Patch 返回给 apiserver,随后 Pod 进入后续调度流程。
