【NVIDIA】k8s-device-plugin v0.19.3 NVIDIA k8s-device-plugin v0.19.3 核心插件模块深度分析之一

NVIDIA k8s-device-plugin v0.19.3 核心插件模块深度分析

一、模块定位

1.1 业务职责

NVIDIA k8s-device-plugin 的核心插件模块是整个项目的心脏,负责:

  • 设备发现:通过 NVML 或 Tegra 策略发现节点上的 GPU/MIG 设备
  • 资源注册:将 GPU 资源注册到 Kubernetes kubelet,使其成为可调度的 Extended Resource
  • 设备分配:响应 kubelet 的 Allocate 请求,为容器分配 GPU 设备
  • 健康监控:持续监控 GPU 健康状态,不健康设备自动标记为 Unhealthy
  • CDI 支持:生成 CDI (Container Device Interface) 规范,支持多种设备列表策略
  • MPS 支持:支持 MPS (Multi-Process Service) 共享 GPU 资源
  • IMEX 支持:支持 IMEX 通道注入

1.2 在系统中的位置

#mermaid-svg-t856rToyw3ngUs1s{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-t856rToyw3ngUs1s .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-t856rToyw3ngUs1s .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-t856rToyw3ngUs1s .error-icon{fill:#552222;}#mermaid-svg-t856rToyw3ngUs1s .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-t856rToyw3ngUs1s .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-t856rToyw3ngUs1s .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-t856rToyw3ngUs1s .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-t856rToyw3ngUs1s .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-t856rToyw3ngUs1s .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-t856rToyw3ngUs1s .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-t856rToyw3ngUs1s .marker{fill:#333333;stroke:#333333;}#mermaid-svg-t856rToyw3ngUs1s .marker.cross{stroke:#333333;}#mermaid-svg-t856rToyw3ngUs1s svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-t856rToyw3ngUs1s p{margin:0;}#mermaid-svg-t856rToyw3ngUs1s .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-t856rToyw3ngUs1s .cluster-label text{fill:#333;}#mermaid-svg-t856rToyw3ngUs1s .cluster-label span{color:#333;}#mermaid-svg-t856rToyw3ngUs1s .cluster-label span p{background-color:transparent;}#mermaid-svg-t856rToyw3ngUs1s .label text,#mermaid-svg-t856rToyw3ngUs1s span{fill:#333;color:#333;}#mermaid-svg-t856rToyw3ngUs1s .node rect,#mermaid-svg-t856rToyw3ngUs1s .node circle,#mermaid-svg-t856rToyw3ngUs1s .node ellipse,#mermaid-svg-t856rToyw3ngUs1s .node polygon,#mermaid-svg-t856rToyw3ngUs1s .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-t856rToyw3ngUs1s .rough-node .label text,#mermaid-svg-t856rToyw3ngUs1s .node .label text,#mermaid-svg-t856rToyw3ngUs1s .image-shape .label,#mermaid-svg-t856rToyw3ngUs1s .icon-shape .label{text-anchor:middle;}#mermaid-svg-t856rToyw3ngUs1s .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-t856rToyw3ngUs1s .rough-node .label,#mermaid-svg-t856rToyw3ngUs1s .node .label,#mermaid-svg-t856rToyw3ngUs1s .image-shape .label,#mermaid-svg-t856rToyw3ngUs1s .icon-shape .label{text-align:center;}#mermaid-svg-t856rToyw3ngUs1s .node.clickable{cursor:pointer;}#mermaid-svg-t856rToyw3ngUs1s .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-t856rToyw3ngUs1s .arrowheadPath{fill:#333333;}#mermaid-svg-t856rToyw3ngUs1s .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-t856rToyw3ngUs1s .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-t856rToyw3ngUs1s .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-t856rToyw3ngUs1s .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-t856rToyw3ngUs1s .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-t856rToyw3ngUs1s .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-t856rToyw3ngUs1s .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-t856rToyw3ngUs1s .cluster text{fill:#333;}#mermaid-svg-t856rToyw3ngUs1s .cluster span{color:#333;}#mermaid-svg-t856rToyw3ngUs1s div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-t856rToyw3ngUs1s .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-t856rToyw3ngUs1s rect.text{fill:none;stroke-width:0;}#mermaid-svg-t856rToyw3ngUs1s .icon-shape,#mermaid-svg-t856rToyw3ngUs1s .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-t856rToyw3ngUs1s .icon-shape p,#mermaid-svg-t856rToyw3ngUs1s .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-t856rToyw3ngUs1s .icon-shape .label rect,#mermaid-svg-t856rToyw3ngUs1s .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-t856rToyw3ngUs1s .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-t856rToyw3ngUs1s .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-t856rToyw3ngUs1s :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 外部依赖
Kubernetes Node
nvidia-device-plugin (DaemonSet)
gRPC (Unix Socket)
Kubelet
main.go

入口 + 生命周期管理
plugin-manager.go

插件工厂
root.go

路径解析工具
server.go

gRPC 服务 + Device Plugin API
factory.go

插件构造器
api.go

Interface 定义
mps.go

MPS 选项
options.go

Option 模式配置
go-nvml

NVIDIA 驱动接口
container-device-interface

CDI 规范生成
internal/rm

资源管理器
internal/watch

文件/信号监控
api/config/v1

配置规范

核心插件模块处于 kubelet ↔ GPU 硬件 的中间层:向上通过 gRPC 与 kubelet 通信,向下通过 NVML/设备文件与 GPU 硬件交互。


二、模块整体结构

2.1 接口定义与类型体系

#mermaid-svg-LDyOHGGWYdH93N3z{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-LDyOHGGWYdH93N3z .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-LDyOHGGWYdH93N3z .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-LDyOHGGWYdH93N3z .error-icon{fill:#552222;}#mermaid-svg-LDyOHGGWYdH93N3z .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-LDyOHGGWYdH93N3z .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-LDyOHGGWYdH93N3z .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-LDyOHGGWYdH93N3z .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-LDyOHGGWYdH93N3z .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-LDyOHGGWYdH93N3z .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-LDyOHGGWYdH93N3z .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-LDyOHGGWYdH93N3z .marker{fill:#333333;stroke:#333333;}#mermaid-svg-LDyOHGGWYdH93N3z .marker.cross{stroke:#333333;}#mermaid-svg-LDyOHGGWYdH93N3z svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-LDyOHGGWYdH93N3z p{margin:0;}#mermaid-svg-LDyOHGGWYdH93N3z g.classGroup text{fill:#9370DB;stroke:none;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:10px;}#mermaid-svg-LDyOHGGWYdH93N3z g.classGroup text .title{font-weight:bolder;}#mermaid-svg-LDyOHGGWYdH93N3z .cluster-label text{fill:#333;}#mermaid-svg-LDyOHGGWYdH93N3z .cluster-label span{color:#333;}#mermaid-svg-LDyOHGGWYdH93N3z .cluster-label span p{background-color:transparent;}#mermaid-svg-LDyOHGGWYdH93N3z .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-LDyOHGGWYdH93N3z .cluster text{fill:#333;}#mermaid-svg-LDyOHGGWYdH93N3z .cluster span{color:#333;}#mermaid-svg-LDyOHGGWYdH93N3z .nodeLabel,#mermaid-svg-LDyOHGGWYdH93N3z .edgeLabel{color:#131300;}#mermaid-svg-LDyOHGGWYdH93N3z .edgeLabel .label rect{fill:#ECECFF;}#mermaid-svg-LDyOHGGWYdH93N3z .label text{fill:#131300;}#mermaid-svg-LDyOHGGWYdH93N3z .labelBkg{background:#ECECFF;}#mermaid-svg-LDyOHGGWYdH93N3z .edgeLabel .label span{background:#ECECFF;}#mermaid-svg-LDyOHGGWYdH93N3z .classTitle{font-weight:bolder;}#mermaid-svg-LDyOHGGWYdH93N3z .node rect,#mermaid-svg-LDyOHGGWYdH93N3z .node circle,#mermaid-svg-LDyOHGGWYdH93N3z .node ellipse,#mermaid-svg-LDyOHGGWYdH93N3z .node polygon,#mermaid-svg-LDyOHGGWYdH93N3z .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-LDyOHGGWYdH93N3z .divider{stroke:#9370DB;stroke-width:1;}#mermaid-svg-LDyOHGGWYdH93N3z g.clickable{cursor:pointer;}#mermaid-svg-LDyOHGGWYdH93N3z g.classGroup rect{fill:#ECECFF;stroke:#9370DB;}#mermaid-svg-LDyOHGGWYdH93N3z g.classGroup line{stroke:#9370DB;stroke-width:1;}#mermaid-svg-LDyOHGGWYdH93N3z .classLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.5;}#mermaid-svg-LDyOHGGWYdH93N3z .classLabel .label{fill:#9370DB;font-size:10px;}#mermaid-svg-LDyOHGGWYdH93N3z .relation{stroke:#333333;stroke-width:1;fill:none;}#mermaid-svg-LDyOHGGWYdH93N3z .dashed-line{stroke-dasharray:3;}#mermaid-svg-LDyOHGGWYdH93N3z .dotted-line{stroke-dasharray:1 2;}#mermaid-svg-LDyOHGGWYdH93N3z #compositionStart,#mermaid-svg-LDyOHGGWYdH93N3z .composition{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-LDyOHGGWYdH93N3z #compositionEnd,#mermaid-svg-LDyOHGGWYdH93N3z .composition{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-LDyOHGGWYdH93N3z #dependencyStart,#mermaid-svg-LDyOHGGWYdH93N3z .dependency{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-LDyOHGGWYdH93N3z #dependencyStart,#mermaid-svg-LDyOHGGWYdH93N3z .dependency{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-LDyOHGGWYdH93N3z #extensionStart,#mermaid-svg-LDyOHGGWYdH93N3z .extension{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-LDyOHGGWYdH93N3z #extensionEnd,#mermaid-svg-LDyOHGGWYdH93N3z .extension{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-LDyOHGGWYdH93N3z #aggregationStart,#mermaid-svg-LDyOHGGWYdH93N3z .aggregation{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-LDyOHGGWYdH93N3z #aggregationEnd,#mermaid-svg-LDyOHGGWYdH93N3z .aggregation{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-LDyOHGGWYdH93N3z #lollipopStart,#mermaid-svg-LDyOHGGWYdH93N3z .lollipop{fill:#ECECFF!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-LDyOHGGWYdH93N3z #lollipopEnd,#mermaid-svg-LDyOHGGWYdH93N3z .lollipop{fill:#ECECFF!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-LDyOHGGWYdH93N3z .edgeTerminals{font-size:11px;line-height:initial;}#mermaid-svg-LDyOHGGWYdH93N3z .classTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-LDyOHGGWYdH93N3z .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-LDyOHGGWYdH93N3z .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-LDyOHGGWYdH93N3z :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} implements
contains
creates
creates
<<interface>>
Interface
+Devices() : rm.Devices
+Start(socket string) : error
+Stop() : error
nvidiaDevicePlugin
-ctx context.Context
-rm rm.ResourceManager
-config *spec.Config
-deviceListStrategies spec.DeviceListStrategies
-cdiHandler cdi.Interface
-cdiAnnotationPrefix string
-socket string
-server *grpc.Server
-health chan *rm.Device
-stop chan interface
-imexChannels imex.Channels
-mps mpsOptions
+Initialize()
+Cleanup()
+Serve() : error
+Register(kubeletSocket) : error
+ListAndWatch(e, s) : error
+GetPreferredAllocation(ctx, r)(*PreferredAllocationResponse, error)
+Allocate(ctx, reqs)(*AllocateResponse, error)
+PreStartContainer(ctx, req)(*PreStartContainerResponse, error)
-getAllocateResponse(ids)(*ContainerAllocateResponse, error)
-updateResponseForCDI(resp, id, ids) : error
-updateResponseForMPS(resp)
-updateResponseForDeviceListEnvVar(resp, ids)
-updateResponseForDeviceMounts(resp, ids)
-apiDeviceSpecs(devRoot, ids) : \[\]*DeviceSpec
-dial(socket, timeout)(*ClientConn, error)
options
-infolib info.Interface
-nvmllib nvml.Interface
-devicelib device.Interface
-failOnInitError bool
-cdiHandler cdi.Interface
-config *spec.Config
-deviceListStrategies spec.DeviceListStrategies
-deviceDiscoveryStrategy string
-imexChannels imex.Channels
+getResourceManagers()(\[\]ResourceManager, error)
+devicePluginForResource(ctx, rm)(Interface, error)
+getMPSOptions(rm)(mpsOptions, error)
mpsOptions
+enabled bool
+resourceName spec.ResourceName
+daemon *mps.Daemon
+hostRoot mps.Root
+waitForDaemon() : error
+updateReponse(resp)
<<type alias: string>>
root
+join(parts) : string
+getDevRoot() : string
+isDevRoot() : bool
+tryResolveLibrary(name) : string

2.2 核心方法清单

文件 方法 作用
main.go main() 程序入口,定义 CLI 参数,启动应用
main.go start() 主事件循环,管理插件生命周期
main.go startPlugins() 加载配置、创建并启动所有插件
main.go stopPlugins() 停止所有插件
main.go validateFlags() 校验 CLI/配置参数合法性
main.go loadConfig() 从 CLI 上下文加载配置
plugin-manager.go GetPlugins() 工厂方法:创建 CDI Handler + Plugin 集合
plugin-manager.go resolveStrategy() 将 "auto" 策略解析为具体的 nvml/tegra
root.go join() 路径拼接
root.go getDevRoot() 获取设备根路径
root.go tryResolveLibrary() 在 driver root 下解析动态库路径
server.go Start() 启动 gRPC 服务 + 注册到 kubelet + 启动健康检查
server.go Stop() 停止 gRPC 服务 + 清理 socket 文件
server.go Serve() 启动 gRPC server(含崩溃重启逻辑)
server.go Register() 向 kubelet 注册设备插件
server.go ListAndWatch() 持续推送设备列表和健康状态
server.go Allocate() 处理设备分配请求
server.go GetPreferredAllocation() 返回推荐分配方案
factory.go New() 插件工厂入口:创建所有插件实例
factory.go getResourceManagers() 根据策略创建资源管理器
mps.go getMPSOptions() 构造 MPS 选项
mps.go waitForDaemon() 等待 MPS 守护进程就绪
mps.go updateReponse() 向分配响应注入 MPS 挂载和环境变量

2.3 内部调用关系

#mermaid-svg-JG3bEus4zgm8KNNa{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-JG3bEus4zgm8KNNa .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-JG3bEus4zgm8KNNa .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-JG3bEus4zgm8KNNa .error-icon{fill:#552222;}#mermaid-svg-JG3bEus4zgm8KNNa .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-JG3bEus4zgm8KNNa .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-JG3bEus4zgm8KNNa .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-JG3bEus4zgm8KNNa .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-JG3bEus4zgm8KNNa .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-JG3bEus4zgm8KNNa .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-JG3bEus4zgm8KNNa .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-JG3bEus4zgm8KNNa .marker{fill:#333333;stroke:#333333;}#mermaid-svg-JG3bEus4zgm8KNNa .marker.cross{stroke:#333333;}#mermaid-svg-JG3bEus4zgm8KNNa svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-JG3bEus4zgm8KNNa p{margin:0;}#mermaid-svg-JG3bEus4zgm8KNNa .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-JG3bEus4zgm8KNNa .cluster-label text{fill:#333;}#mermaid-svg-JG3bEus4zgm8KNNa .cluster-label span{color:#333;}#mermaid-svg-JG3bEus4zgm8KNNa .cluster-label span p{background-color:transparent;}#mermaid-svg-JG3bEus4zgm8KNNa .label text,#mermaid-svg-JG3bEus4zgm8KNNa span{fill:#333;color:#333;}#mermaid-svg-JG3bEus4zgm8KNNa .node rect,#mermaid-svg-JG3bEus4zgm8KNNa .node circle,#mermaid-svg-JG3bEus4zgm8KNNa .node ellipse,#mermaid-svg-JG3bEus4zgm8KNNa .node polygon,#mermaid-svg-JG3bEus4zgm8KNNa .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-JG3bEus4zgm8KNNa .rough-node .label text,#mermaid-svg-JG3bEus4zgm8KNNa .node .label text,#mermaid-svg-JG3bEus4zgm8KNNa .image-shape .label,#mermaid-svg-JG3bEus4zgm8KNNa .icon-shape .label{text-anchor:middle;}#mermaid-svg-JG3bEus4zgm8KNNa .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-JG3bEus4zgm8KNNa .rough-node .label,#mermaid-svg-JG3bEus4zgm8KNNa .node .label,#mermaid-svg-JG3bEus4zgm8KNNa .image-shape .label,#mermaid-svg-JG3bEus4zgm8KNNa .icon-shape .label{text-align:center;}#mermaid-svg-JG3bEus4zgm8KNNa .node.clickable{cursor:pointer;}#mermaid-svg-JG3bEus4zgm8KNNa .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-JG3bEus4zgm8KNNa .arrowheadPath{fill:#333333;}#mermaid-svg-JG3bEus4zgm8KNNa .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-JG3bEus4zgm8KNNa .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-JG3bEus4zgm8KNNa .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-JG3bEus4zgm8KNNa .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-JG3bEus4zgm8KNNa .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-JG3bEus4zgm8KNNa .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-JG3bEus4zgm8KNNa .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-JG3bEus4zgm8KNNa .cluster text{fill:#333;}#mermaid-svg-JG3bEus4zgm8KNNa .cluster span{color:#333;}#mermaid-svg-JG3bEus4zgm8KNNa div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-JG3bEus4zgm8KNNa .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-JG3bEus4zgm8KNNa rect.text{fill:none;stroke-width:0;}#mermaid-svg-JG3bEus4zgm8KNNa .icon-shape,#mermaid-svg-JG3bEus4zgm8KNNa .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-JG3bEus4zgm8KNNa .icon-shape p,#mermaid-svg-JG3bEus4zgm8KNNa .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-JG3bEus4zgm8KNNa .icon-shape .label rect,#mermaid-svg-JG3bEus4zgm8KNNa .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-JG3bEus4zgm8KNNa .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-JG3bEus4zgm8KNNa .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-JG3bEus4zgm8KNNa :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} nvml
tegra
main()
start()
watch.Files()
watch.Signals()
startPlugins()
loadConfig()
validateFlags()
rm.AddDefaultResourcesToConfig()
GetPlugins()
resolveStrategy()
cdi.New()
plugin.New()
getResourceManagers()
rm.NewNVMLResourceManagers()
rm.NewTegraResourceManagers()
devicePluginForResource()
getMPSOptions()
cdiHandler.CreateSpecFile()
p.Start()
mps.waitForDaemon()
Serve()
Register()
rm.CheckHealth()
stopPlugins()
p.Stop()

2.4 数据流入流出

#mermaid-svg-7RN5oT4ORYp1A4HD{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-7RN5oT4ORYp1A4HD .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-7RN5oT4ORYp1A4HD .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-7RN5oT4ORYp1A4HD .error-icon{fill:#552222;}#mermaid-svg-7RN5oT4ORYp1A4HD .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-7RN5oT4ORYp1A4HD .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-7RN5oT4ORYp1A4HD .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-7RN5oT4ORYp1A4HD .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-7RN5oT4ORYp1A4HD .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-7RN5oT4ORYp1A4HD .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-7RN5oT4ORYp1A4HD .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-7RN5oT4ORYp1A4HD .marker{fill:#333333;stroke:#333333;}#mermaid-svg-7RN5oT4ORYp1A4HD .marker.cross{stroke:#333333;}#mermaid-svg-7RN5oT4ORYp1A4HD svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-7RN5oT4ORYp1A4HD p{margin:0;}#mermaid-svg-7RN5oT4ORYp1A4HD .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-7RN5oT4ORYp1A4HD .cluster-label text{fill:#333;}#mermaid-svg-7RN5oT4ORYp1A4HD .cluster-label span{color:#333;}#mermaid-svg-7RN5oT4ORYp1A4HD .cluster-label span p{background-color:transparent;}#mermaid-svg-7RN5oT4ORYp1A4HD .label text,#mermaid-svg-7RN5oT4ORYp1A4HD span{fill:#333;color:#333;}#mermaid-svg-7RN5oT4ORYp1A4HD .node rect,#mermaid-svg-7RN5oT4ORYp1A4HD .node circle,#mermaid-svg-7RN5oT4ORYp1A4HD .node ellipse,#mermaid-svg-7RN5oT4ORYp1A4HD .node polygon,#mermaid-svg-7RN5oT4ORYp1A4HD .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-7RN5oT4ORYp1A4HD .rough-node .label text,#mermaid-svg-7RN5oT4ORYp1A4HD .node .label text,#mermaid-svg-7RN5oT4ORYp1A4HD .image-shape .label,#mermaid-svg-7RN5oT4ORYp1A4HD .icon-shape .label{text-anchor:middle;}#mermaid-svg-7RN5oT4ORYp1A4HD .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-7RN5oT4ORYp1A4HD .rough-node .label,#mermaid-svg-7RN5oT4ORYp1A4HD .node .label,#mermaid-svg-7RN5oT4ORYp1A4HD .image-shape .label,#mermaid-svg-7RN5oT4ORYp1A4HD .icon-shape .label{text-align:center;}#mermaid-svg-7RN5oT4ORYp1A4HD .node.clickable{cursor:pointer;}#mermaid-svg-7RN5oT4ORYp1A4HD .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-7RN5oT4ORYp1A4HD .arrowheadPath{fill:#333333;}#mermaid-svg-7RN5oT4ORYp1A4HD .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-7RN5oT4ORYp1A4HD .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-7RN5oT4ORYp1A4HD .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-7RN5oT4ORYp1A4HD .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-7RN5oT4ORYp1A4HD .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-7RN5oT4ORYp1A4HD .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-7RN5oT4ORYp1A4HD .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-7RN5oT4ORYp1A4HD .cluster text{fill:#333;}#mermaid-svg-7RN5oT4ORYp1A4HD .cluster span{color:#333;}#mermaid-svg-7RN5oT4ORYp1A4HD div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-7RN5oT4ORYp1A4HD .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-7RN5oT4ORYp1A4HD rect.text{fill:none;stroke-width:0;}#mermaid-svg-7RN5oT4ORYp1A4HD .icon-shape,#mermaid-svg-7RN5oT4ORYp1A4HD .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-7RN5oT4ORYp1A4HD .icon-shape p,#mermaid-svg-7RN5oT4ORYp1A4HD .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-7RN5oT4ORYp1A4HD .icon-shape .label rect,#mermaid-svg-7RN5oT4ORYp1A4HD .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-7RN5oT4ORYp1A4HD .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-7RN5oT4ORYp1A4HD .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-7RN5oT4ORYp1A4HD :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 输出
处理
输入
CLI 参数 / 环境变量
config.yaml
NVML 库 / GPU 硬件
kubelet gRPC 请求
loadConfig
validateFlags
ResourceManager
nvidiaDevicePlugin
CDI Handler
Unix Socket

(gRPC 服务)
RegisterResponse
ListAndWatchResponse
AllocateResponse

(Envs/Mounts/Devices/Annotations)
CDI Spec File


三、核心业务逻辑深度解析

3.1 程序入口:main()

go 复制代码
func main() {
    c := cli.NewApp()
    o := &options{}
    c.Name = "NVIDIA Device Plugin"
    c.Usage = "NVIDIA device plugin for Kubernetes"
    c.Version = info.GetVersionString()
    c.Action = func(ctx *cli.Context) error {
        return start(ctx, o)
    }
    // ... 定义 CLI flags ...
    o.flags = c.Flags
    err := c.Run(os.Args)
    if err != nil {
        klog.Error(err)
        os.Exit(1)
    }
}

逐行解析:

  1. c := cli.NewApp() --- 创建 urfave/cli 应用实例,用于处理命令行参数
  2. o := &options{} --- 创建 options 结构体,用于在 mainstart 之间传递 CLI 特有参数(kubeletSocketconfigFilecdiFeatureFlags),这些参数没有 config file 等价物
  3. c.Action = func(ctx *cli.Context) error { return start(ctx, o) } --- 设置主 action 为 start,传入 CLI 上下文和 options
  4. c.Flags = []cli.Flag{...} --- 定义所有 CLI flags,每个 flag 都支持环境变量,主要包括:
    • --mig-strategy:MIG 策略(none/single/mixed)
    • --fail-on-init-error:初始化失败时是否直接退出
    • --driver-root / --dev-root:NVIDIA 驱动/设备根路径
    • --pass-device-specs:是否传递 DeviceSpecs 到 kubelet
    • --device-list-strategy:设备列表传递策略(envvar/volume-mounts/cdi-annotations/cdi-cri)
    • --device-id-strategy:设备 ID 策略(uuid/index)
    • --gdrcopy-enabled / --gds-enabled / --mofed-enabled:高级特性开关
    • --config-file:配置文件路径
    • --mps-root:MPS 根路径
    • --device-discovery-strategy:设备发现策略(auto/nvml/tegra)
    • --imex-channel-ids / --imex-required:IMEX 通道配置
  5. c.Run(os.Args) --- 执行 CLI 应用,触发 start

3.2 主事件循环:start()

#mermaid-svg-ciXTiHbJLLSkKTVA{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-ciXTiHbJLLSkKTVA .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-ciXTiHbJLLSkKTVA .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-ciXTiHbJLLSkKTVA .error-icon{fill:#552222;}#mermaid-svg-ciXTiHbJLLSkKTVA .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-ciXTiHbJLLSkKTVA .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-ciXTiHbJLLSkKTVA .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-ciXTiHbJLLSkKTVA .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-ciXTiHbJLLSkKTVA .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-ciXTiHbJLLSkKTVA .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-ciXTiHbJLLSkKTVA .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-ciXTiHbJLLSkKTVA .marker{fill:#333333;stroke:#333333;}#mermaid-svg-ciXTiHbJLLSkKTVA .marker.cross{stroke:#333333;}#mermaid-svg-ciXTiHbJLLSkKTVA svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-ciXTiHbJLLSkKTVA p{margin:0;}#mermaid-svg-ciXTiHbJLLSkKTVA defs #statediagram-barbEnd{fill:#333333;stroke:#333333;}#mermaid-svg-ciXTiHbJLLSkKTVA g.stateGroup text{fill:#9370DB;stroke:none;font-size:10px;}#mermaid-svg-ciXTiHbJLLSkKTVA g.stateGroup text{fill:#333;stroke:none;font-size:10px;}#mermaid-svg-ciXTiHbJLLSkKTVA g.stateGroup .state-title{font-weight:bolder;fill:#131300;}#mermaid-svg-ciXTiHbJLLSkKTVA g.stateGroup rect{fill:#ECECFF;stroke:#9370DB;}#mermaid-svg-ciXTiHbJLLSkKTVA g.stateGroup line{stroke:#333333;stroke-width:1;}#mermaid-svg-ciXTiHbJLLSkKTVA .transition{stroke:#333333;stroke-width:1;fill:none;}#mermaid-svg-ciXTiHbJLLSkKTVA .stateGroup .composit{fill:white;border-bottom:1px;}#mermaid-svg-ciXTiHbJLLSkKTVA .stateGroup .alt-composit{fill:#e0e0e0;border-bottom:1px;}#mermaid-svg-ciXTiHbJLLSkKTVA .state-note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-ciXTiHbJLLSkKTVA .state-note text{fill:black;stroke:none;font-size:10px;}#mermaid-svg-ciXTiHbJLLSkKTVA .stateLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.5;}#mermaid-svg-ciXTiHbJLLSkKTVA .edgeLabel .label rect{fill:#ECECFF;opacity:0.5;}#mermaid-svg-ciXTiHbJLLSkKTVA .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ciXTiHbJLLSkKTVA .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-ciXTiHbJLLSkKTVA .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ciXTiHbJLLSkKTVA .edgeLabel .label text{fill:#333;}#mermaid-svg-ciXTiHbJLLSkKTVA .label div .edgeLabel{color:#333;}#mermaid-svg-ciXTiHbJLLSkKTVA .stateLabel text{fill:#131300;font-size:10px;font-weight:bold;}#mermaid-svg-ciXTiHbJLLSkKTVA .node circle.state-start{fill:#333333;stroke:#333333;}#mermaid-svg-ciXTiHbJLLSkKTVA .node .fork-join{fill:#333333;stroke:#333333;}#mermaid-svg-ciXTiHbJLLSkKTVA .node circle.state-end{fill:#9370DB;stroke:white;stroke-width:1.5;}#mermaid-svg-ciXTiHbJLLSkKTVA .end-state-inner{fill:white;stroke-width:1.5;}#mermaid-svg-ciXTiHbJLLSkKTVA .node rect{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-ciXTiHbJLLSkKTVA .node polygon{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-ciXTiHbJLLSkKTVA #statediagram-barbEnd{fill:#333333;}#mermaid-svg-ciXTiHbJLLSkKTVA .statediagram-cluster rect{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-ciXTiHbJLLSkKTVA .cluster-label,#mermaid-svg-ciXTiHbJLLSkKTVA .nodeLabel{color:#131300;}#mermaid-svg-ciXTiHbJLLSkKTVA .statediagram-cluster rect.outer{rx:5px;ry:5px;}#mermaid-svg-ciXTiHbJLLSkKTVA .statediagram-state .divider{stroke:#9370DB;}#mermaid-svg-ciXTiHbJLLSkKTVA .statediagram-state .title-state{rx:5px;ry:5px;}#mermaid-svg-ciXTiHbJLLSkKTVA .statediagram-cluster.statediagram-cluster .inner{fill:white;}#mermaid-svg-ciXTiHbJLLSkKTVA .statediagram-cluster.statediagram-cluster-alt .inner{fill:#f0f0f0;}#mermaid-svg-ciXTiHbJLLSkKTVA .statediagram-cluster .inner{rx:0;ry:0;}#mermaid-svg-ciXTiHbJLLSkKTVA .statediagram-state rect.basic{rx:5px;ry:5px;}#mermaid-svg-ciXTiHbJLLSkKTVA .statediagram-state rect.divider{stroke-dasharray:10,10;fill:#f0f0f0;}#mermaid-svg-ciXTiHbJLLSkKTVA .note-edge{stroke-dasharray:5;}#mermaid-svg-ciXTiHbJLLSkKTVA .statediagram-note rect{fill:#fff5ad;stroke:#aaaa33;stroke-width:1px;rx:0;ry:0;}#mermaid-svg-ciXTiHbJLLSkKTVA .statediagram-note rect{fill:#fff5ad;stroke:#aaaa33;stroke-width:1px;rx:0;ry:0;}#mermaid-svg-ciXTiHbJLLSkKTVA .statediagram-note text{fill:black;}#mermaid-svg-ciXTiHbJLLSkKTVA .statediagram-note .nodeLabel{color:black;}#mermaid-svg-ciXTiHbJLLSkKTVA .statediagram .edgeLabel{color:red;}#mermaid-svg-ciXTiHbJLLSkKTVA #dependencyStart,#mermaid-svg-ciXTiHbJLLSkKTVA #dependencyEnd{fill:#333333;stroke:#333333;stroke-width:1;}#mermaid-svg-ciXTiHbJLLSkKTVA .statediagramTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-ciXTiHbJLLSkKTVA :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Watching
main()
kubelet socket 创建 / SIGHUP
fsnotify error (仅日志)
SIGINT/SIGTERM/SIGQUIT
restartTimeout (30s)
watcher + signals 就绪
Init
StartPlugins
EventLoop
Exiting

go 复制代码
func start(c *cli.Context, o *options) error {
    klog.InfoS(fmt.Sprintf("Starting %s", c.App.Name), "version", c.App.Version)

    // 1. 创建文件系统 watcher,监控 kubelet socket 目录
    kubeletSocketDir := filepath.Dir(o.kubeletSocket)
    watcher, err := watch.Files(kubeletSocketDir)
    // ...
    defer watcher.Close()

    // 2. 创建 OS 信号 watcher
    sigs := watch.Signals(syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT)

    var started bool
    var restartTimeout <-chan time.Time
    var plugins []plugin.Interface

restart:
    // 3. 如果是重启,先停止旧插件
    if started {
        err := stopPlugins(plugins)
        // ...
    }

    // 4. 启动插件
    plugins, restartPlugins, err := startPlugins(c, o)
    // ...
    started = true

    // 5. 如果部分插件启动失败,设置 30s 重试定时器
    if restartPlugins {
        restartTimeout = time.After(30 * time.Second)
    }

    // 6. 无限循环,监听多个事件源
    for {
        select {
        case <-restartTimeout:          // 重试超时 → 重启
            goto restart
        case event := <-watcher.Events: // kubelet socket 重建 → 重启
            if o.kubeletSocket != "" && event.Name == o.kubeletSocket &&
               event.Op&fsnotify.Create == fsnotify.Create {
                goto restart
            }
        case err := <-watcher.Errors:   // fsnotify 错误 → 仅记录日志
            klog.Infof("inotify: %s", err)
        case s := <-sigs:               // OS 信号
            switch s {
            case syscall.SIGHUP:        // SIGHUP → 重启
                goto restart
            default:                    // 其他信号 → 退出
                goto exit
            }
        }
    }
exit:
    err = stopPlugins(plugins)
    return err
}

设计意图分析:

  • kubelet 重启检测 :kubelet 重启时会重新创建 socket 文件,通过 inotify 检测到 Create 事件后自动重启插件,确保设备插件与 kubelet 始终保持连接
  • SIGHUP 热重启:支持通过 SIGHUP 信号触发插件重启,用于配置变更后无需重启 Pod 即可生效
  • 崩溃退避restartPlugins 标志 + 30s 延迟,避免在 kubelet 未就绪时频繁重试
  • 优雅退出:SIGINT/SIGTERM/SIGQUIT 触发优雅停止,先停止所有插件再退出

3.3 插件启动:startPlugins()

go 复制代码
func startPlugins(c *cli.Context, o *options) ([]plugin.Interface, bool, error) {
    // Step 1: 加载配置
    config, err := loadConfig(c, o.flags)
    // ...
    spec.DisableResourceNamingInConfig(config)

    // Step 2: 构造 NVML/device/info 库实例
    driverRoot := root(*config.Flags.Plugin.ContainerDriverRoot)
    nvmllib := nvml.New(
        nvml.WithLibraryPath(driverRoot.tryResolveLibrary("libnvidia-ml.so.1")),
    )
    devicelib := device.New(nvmllib)
    infolib := nvinfo.New(
        nvinfo.WithRoot(string(driverRoot)),
        nvinfo.WithNvmlLib(nvmllib),
        nvinfo.WithDeviceLib(devicelib),
    )

    // Step 3: 校验 flags
    err = validateFlags(infolib, config)
    // ...

    // Step 4: 添加默认资源到配置
    err = rm.AddDefaultResourcesToConfig(infolib, nvmllib, devicelib, config)
    // ...

    // Step 5: 打印配置
    configJSON, _ := json.MarshalIndent(config, "", "  ")
    klog.Infof("\nRunning with config:\n%v", string(configJSON))

    // Step 6: 获取插件集合
    plugins, err := GetPlugins(c.Context, infolib, nvmllib, devicelib, config, o)
    // ...

    // Step 7: 遍历插件,启动有设备的插件
    started := 0
    for _, p := range plugins {
        if len(p.Devices()) == 0 {
            continue  // 无设备的插件跳过
        }
        if err := p.Start(o.kubeletSocket); err != nil {
            return plugins, true, nil  // 启动失败,返回需要重试
        }
        started++
    }

    if started == 0 {
        klog.Info("No devices found. Waiting indefinitely.")
    }

    return plugins, false, nil
}

关键设计点:

  • driverRoot.tryResolveLibrary("libnvidia-ml.so.1") --- 在 driver root 下搜索 NVML 库,支持 driver container 场景(如 /run/nvidia/driver/usr/lib64/libnvidia-ml.so.1
  • spec.DisableResourceNamingInConfig(config) --- 禁用资源命名特性(可能与某些 K8s 版本不兼容)
  • rm.AddDefaultResourcesToConfig() --- 根据硬件自动添加默认资源(如 nvidia.com/gpu, nvidia.com/mig-1g.10gb 等)
  • 只启动有设备的插件,无设备的插件被跳过(但仍然返回,以便统一管理生命周期)

3.4 配置校验:validateFlags()

go 复制代码
func validateFlags(infolib nvinfo.Interface, config *spec.Config) error {
    // 1. 解析并校验 device-list-strategy
    deviceListStrategies, err := spec.NewDeviceListStrategies(*config.Flags.Plugin.DeviceListStrategy)
    if err != nil {
        return fmt.Errorf("invalid --device-list-strategy option: %v", err)
    }

    // 2. CDI 策略需要 NVML 支持
    hasNvml, _ := infolib.HasNvml()
    if deviceListStrategies.AnyCDIEnabled() && !hasNvml {
        return fmt.Errorf("CDI --device-list-strategy options are only supported on NVML-based systems")
    }

    // 3. 校验 device-id-strategy(只能是 uuid 或 index)
    if *config.Flags.Plugin.DeviceIDStrategy != spec.DeviceIDStrategyUUID &&
       *config.Flags.Plugin.DeviceIDStrategy != spec.DeviceIDStrategyIndex {
        return fmt.Errorf("invalid --device-id-strategy option: %v", ...)
    }

    // 4. MPS 共享策略校验
    if config.Sharing.SharingStrategy() == spec.SharingStrategyMPS {
        if *config.Flags.MigStrategy == spec.MigStrategyMixed {
            return fmt.Errorf("using --mig-strategy=mixed is not supported with MPS")
        }
        if config.Flags.MpsRoot == nil || *config.Flags.MpsRoot == "" {
            return fmt.Errorf("using MPS requires --mps-root to be specified")
        }
    }

    // 5. 校验 device-discovery-strategy
    switch *config.Flags.DeviceDiscoveryStrategy {
    case "auto", "nvml", "tegra": // 合法值
    default:
        return fmt.Errorf("invalid --device-discovery-strategy option %v", ...)
    }

    // 6. 校验 mig-strategy
    switch *config.Flags.MigStrategy {
    case spec.MigStrategyNone, spec.MigStrategySingle, spec.MigStrategyMixed:
    default:
        return fmt.Errorf("unknown MIG strategy: %v", ...)
    }

    // 7. 校验 IMEX channel IDs
    if err := spec.AssertChannelIDsValid(config.Imex.ChannelIDs); err != nil {
        return fmt.Errorf("invalid IMEX channel IDs: %w", err)
    }

    return nil
}

校验规则矩阵:

参数 合法值 约束条件
device-list-strategy envvar, volume-mounts, cdi-annotations, cdi-cri CDI 策略需 NVML
device-id-strategy uuid, index ---
mig-strategy none, single, mixed mixed 不兼容 MPS
device-discovery-strategy auto, nvml, tegra ---
mps-root 非空字符串 MPS 共享时必填
imex-channel-ids 有效 channel ID ---

3.5 插件工厂:GetPlugins() (plugin-manager.go)

go 复制代码
func GetPlugins(ctx context.Context, infolib info.Interface, nvmllib nvml.Interface,
    devicelib device.Interface, config *spec.Config, o *options) ([]plugin.Interface, error) {

    driverRoot := root(*config.Flags.Plugin.ContainerDriverRoot)

    // 1. 解析设备列表策略
    deviceListStrategies, err := spec.NewDeviceListStrategies(*config.Flags.Plugin.DeviceListStrategy)
    // ...

    // 2. 获取 IMEX 通道
    imexChannels, err := imex.GetChannels(config, driverRoot.getDevRoot())
    // ...

    // 3. 解析设备发现策略(auto → nvml/tegra)
    resolvedStrategy := resolveStrategy(*config.Flags.DeviceDiscoveryStrategy, infolib)

    // 4. 构造 CDI Handler
    cdiHandler, err := cdi.New(infolib, nvmllib, devicelib,
        cdi.WithDeviceListStrategies(deviceListStrategies),
        cdi.WithDriverRoot(string(driverRoot)),
        cdi.WithDevRoot(driverRoot.getDevRoot()),
        cdi.WithTargetDriverRoot(*config.Flags.NvidiaDriverRoot),
        cdi.WithTargetDevRoot(*config.Flags.NvidiaDevRoot),
        cdi.WithNvidiaCTKPath(*config.Flags.Plugin.NvidiaCTKPath),
        cdi.WithDeviceIDStrategy(*config.Flags.Plugin.DeviceIDStrategy),
        cdi.WithVendor("k8s.device-plugin.nvidia.com"),
        cdi.WithGdrcopyEnabled(*config.Flags.GDRCopyEnabled),
        cdi.WithGdsEnabled(*config.Flags.GDSEnabled),
        cdi.WithMofedEnabled(*config.Flags.MOFEDEnabled),
        cdi.WithImexChannels(imexChannels),
        cdi.WithFeatureFlags(o.cdiFeatureFlags.Value()...),
        cdi.WithDeviceDiscoveryStrategy(resolvedStrategy),
    )
    // ...

    // 5. 构造插件集合
    plugins, err := plugin.New(ctx, infolib, nvmllib, devicelib,
        plugin.WithCDIHandler(cdiHandler),
        plugin.WithConfig(config),
        plugin.WithDeviceListStrategies(deviceListStrategies),
        plugin.WithFailOnInitError(*config.Flags.FailOnInitError),
        plugin.WithImexChannels(imexChannels),
        plugin.WithDeviceDiscoveryStrategy(resolvedStrategy),
    )
    // ...

    // 6. 生成 CDI Spec 文件
    if err := cdiHandler.CreateSpecFile(); err != nil {
        return nil, fmt.Errorf("unable to create cdi spec file: %v", err)
    }

    return plugins, nil
}

resolveStrategy() 自动检测逻辑:

go 复制代码
func resolveStrategy(strategy string, infolib info.Interface) string {
    if strategy != "" && strategy != "auto" {
        return strategy  // 用户显式指定,直接返回
    }
    platform := infolib.ResolvePlatform()
    switch platform {
    case info.PlatformNVML, info.PlatformWSL:
        return "nvml"
    case info.PlatformTegra:
        return "tegra"
    }
    return "nvml"  // 默认回退到 nvml
}

#mermaid-svg-ngpAmEbFLaQN9wad{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-ngpAmEbFLaQN9wad .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-ngpAmEbFLaQN9wad .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-ngpAmEbFLaQN9wad .error-icon{fill:#552222;}#mermaid-svg-ngpAmEbFLaQN9wad .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-ngpAmEbFLaQN9wad .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-ngpAmEbFLaQN9wad .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-ngpAmEbFLaQN9wad .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-ngpAmEbFLaQN9wad .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-ngpAmEbFLaQN9wad .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-ngpAmEbFLaQN9wad .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-ngpAmEbFLaQN9wad .marker{fill:#333333;stroke:#333333;}#mermaid-svg-ngpAmEbFLaQN9wad .marker.cross{stroke:#333333;}#mermaid-svg-ngpAmEbFLaQN9wad svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-ngpAmEbFLaQN9wad p{margin:0;}#mermaid-svg-ngpAmEbFLaQN9wad .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-ngpAmEbFLaQN9wad .cluster-label text{fill:#333;}#mermaid-svg-ngpAmEbFLaQN9wad .cluster-label span{color:#333;}#mermaid-svg-ngpAmEbFLaQN9wad .cluster-label span p{background-color:transparent;}#mermaid-svg-ngpAmEbFLaQN9wad .label text,#mermaid-svg-ngpAmEbFLaQN9wad span{fill:#333;color:#333;}#mermaid-svg-ngpAmEbFLaQN9wad .node rect,#mermaid-svg-ngpAmEbFLaQN9wad .node circle,#mermaid-svg-ngpAmEbFLaQN9wad .node ellipse,#mermaid-svg-ngpAmEbFLaQN9wad .node polygon,#mermaid-svg-ngpAmEbFLaQN9wad .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-ngpAmEbFLaQN9wad .rough-node .label text,#mermaid-svg-ngpAmEbFLaQN9wad .node .label text,#mermaid-svg-ngpAmEbFLaQN9wad .image-shape .label,#mermaid-svg-ngpAmEbFLaQN9wad .icon-shape .label{text-anchor:middle;}#mermaid-svg-ngpAmEbFLaQN9wad .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-ngpAmEbFLaQN9wad .rough-node .label,#mermaid-svg-ngpAmEbFLaQN9wad .node .label,#mermaid-svg-ngpAmEbFLaQN9wad .image-shape .label,#mermaid-svg-ngpAmEbFLaQN9wad .icon-shape .label{text-align:center;}#mermaid-svg-ngpAmEbFLaQN9wad .node.clickable{cursor:pointer;}#mermaid-svg-ngpAmEbFLaQN9wad .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-ngpAmEbFLaQN9wad .arrowheadPath{fill:#333333;}#mermaid-svg-ngpAmEbFLaQN9wad .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-ngpAmEbFLaQN9wad .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-ngpAmEbFLaQN9wad .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ngpAmEbFLaQN9wad .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-ngpAmEbFLaQN9wad .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ngpAmEbFLaQN9wad .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-ngpAmEbFLaQN9wad .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-ngpAmEbFLaQN9wad .cluster text{fill:#333;}#mermaid-svg-ngpAmEbFLaQN9wad .cluster span{color:#333;}#mermaid-svg-ngpAmEbFLaQN9wad div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-ngpAmEbFLaQN9wad .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-ngpAmEbFLaQN9wad rect.text{fill:none;stroke-width:0;}#mermaid-svg-ngpAmEbFLaQN9wad .icon-shape,#mermaid-svg-ngpAmEbFLaQN9wad .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ngpAmEbFLaQN9wad .icon-shape p,#mermaid-svg-ngpAmEbFLaQN9wad .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-ngpAmEbFLaQN9wad .icon-shape .label rect,#mermaid-svg-ngpAmEbFLaQN9wad .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ngpAmEbFLaQN9wad .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-ngpAmEbFLaQN9wad .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-ngpAmEbFLaQN9wad :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} No
Yes
NVML / WSL
Tegra
Unknown
resolveStrategy()
strategy == 'auto'?
返回 strategy
infolib.ResolvePlatform()
Platform?
返回 'nvml'
返回 'tegra'
默认返回 'nvml'

3.6 插件构造器:plugin.New() (factory.go)

go 复制代码
func New(ctx context.Context, infolib info.Interface, nvmllib nvml.Interface,
    devicelib device.Interface, opts ...Option) ([]Interface, error) {
    // 1. 初始化 options,应用所有 Option 函数
    o := &options{
        infolib:   infolib,
        nvmllib:   nvmllib,
        devicelib: devicelib,
    }
    for _, opt := range opts {
        opt(o)
    }

    // 2. 校验 config
    if o.config == nil {
        klog.Warning("no config provided, returning a null manager")
        return nil, nil
    }

    // 3. 默认 CDI Handler
    if o.cdiHandler == nil {
        o.cdiHandler = cdi.NewNullHandler()
    }

    // 4. 校验 discovery strategy
    if o.deviceDiscoveryStrategy == "" {
        return nil, fmt.Errorf("device discovery strategy not set")
    }

    // 5. 获取资源管理器集合
    resourceManagers, err := o.getResourceManagers()
    // ...

    // 6. 为每个资源管理器创建一个插件实例
    var plugins []Interface
    for _, resourceManager := range resourceManagers {
        plugin, err := o.devicePluginForResource(ctx, resourceManager)
        // ...
        plugins = append(plugins, plugin)
    }
    return plugins, nil
}

getResourceManagers() --- 设备发现核心:

go 复制代码
func (o *options) getResourceManagers() ([]rm.ResourceManager, error) {
    strategy := o.deviceDiscoveryStrategy
    switch strategy {
    case "nvml":
        // 初始化 NVML 库
        ret := o.nvmllib.Init()
        if ret != nvml.SUCCESS {
            // 打印详细的错误帮助信息
            klog.Errorf("Failed to initialize NVML: %v.", ret)
            klog.Errorf("If this is a GPU node, did you set the docker default runtime to `nvidia`?")
            // ...
            if o.failOnInitError {
                return nil, fmt.Errorf("nvml init failed: %v", ret)
            }
            klog.Warningf("nvml init failed: %v", ret)
            return nil, nil  // 不崩溃,返回空(等待重试)
        }
        defer func() {
            _ = o.nvmllib.Shutdown()
        }()
        return rm.NewNVMLResourceManagers(o.infolib, o.nvmllib, o.devicelib, o.config)

    case "tegra":
        return rm.NewTegraResourceManagers(o.config)

    default:
        // 不支持的策略
        if o.failOnInitError {
            return nil, fmt.Errorf("invalid device discovery strategy")
        }
        return nil, nil
    }
}

failOnInitError 的双重行为:

  • true(默认):NVML 初始化失败 → 返回错误 → 30s 后重试
  • false:返回 nil → 无设备 → 等待(适用于非 GPU 节点)

3.7 Option 模式配置 (options.go)

go 复制代码
type Option func(*options)

func WithCDIHandler(handler cdi.Interface) Option {
    return func(m *options) { m.cdiHandler = handler }
}
func WithConfig(config *spec.Config) Option {
    return func(m *options) { m.config = config }
}
// ... 其他 With* 函数

采用经典的 Functional Options Pattern ,每个 With* 函数返回一个闭包,修改 options 结构体的对应字段。这使得 plugin.New() 的 API 可扩展且向后兼容。
#mermaid-svg-vq1EwXxxVLPUDBMd{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-vq1EwXxxVLPUDBMd .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-vq1EwXxxVLPUDBMd .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-vq1EwXxxVLPUDBMd .error-icon{fill:#552222;}#mermaid-svg-vq1EwXxxVLPUDBMd .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-vq1EwXxxVLPUDBMd .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-vq1EwXxxVLPUDBMd .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-vq1EwXxxVLPUDBMd .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-vq1EwXxxVLPUDBMd .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-vq1EwXxxVLPUDBMd .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-vq1EwXxxVLPUDBMd .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-vq1EwXxxVLPUDBMd .marker{fill:#333333;stroke:#333333;}#mermaid-svg-vq1EwXxxVLPUDBMd .marker.cross{stroke:#333333;}#mermaid-svg-vq1EwXxxVLPUDBMd svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-vq1EwXxxVLPUDBMd p{margin:0;}#mermaid-svg-vq1EwXxxVLPUDBMd .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-vq1EwXxxVLPUDBMd .cluster-label text{fill:#333;}#mermaid-svg-vq1EwXxxVLPUDBMd .cluster-label span{color:#333;}#mermaid-svg-vq1EwXxxVLPUDBMd .cluster-label span p{background-color:transparent;}#mermaid-svg-vq1EwXxxVLPUDBMd .label text,#mermaid-svg-vq1EwXxxVLPUDBMd span{fill:#333;color:#333;}#mermaid-svg-vq1EwXxxVLPUDBMd .node rect,#mermaid-svg-vq1EwXxxVLPUDBMd .node circle,#mermaid-svg-vq1EwXxxVLPUDBMd .node ellipse,#mermaid-svg-vq1EwXxxVLPUDBMd .node polygon,#mermaid-svg-vq1EwXxxVLPUDBMd .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-vq1EwXxxVLPUDBMd .rough-node .label text,#mermaid-svg-vq1EwXxxVLPUDBMd .node .label text,#mermaid-svg-vq1EwXxxVLPUDBMd .image-shape .label,#mermaid-svg-vq1EwXxxVLPUDBMd .icon-shape .label{text-anchor:middle;}#mermaid-svg-vq1EwXxxVLPUDBMd .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-vq1EwXxxVLPUDBMd .rough-node .label,#mermaid-svg-vq1EwXxxVLPUDBMd .node .label,#mermaid-svg-vq1EwXxxVLPUDBMd .image-shape .label,#mermaid-svg-vq1EwXxxVLPUDBMd .icon-shape .label{text-align:center;}#mermaid-svg-vq1EwXxxVLPUDBMd .node.clickable{cursor:pointer;}#mermaid-svg-vq1EwXxxVLPUDBMd .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-vq1EwXxxVLPUDBMd .arrowheadPath{fill:#333333;}#mermaid-svg-vq1EwXxxVLPUDBMd .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-vq1EwXxxVLPUDBMd .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-vq1EwXxxVLPUDBMd .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-vq1EwXxxVLPUDBMd .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-vq1EwXxxVLPUDBMd .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-vq1EwXxxVLPUDBMd .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-vq1EwXxxVLPUDBMd .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-vq1EwXxxVLPUDBMd .cluster text{fill:#333;}#mermaid-svg-vq1EwXxxVLPUDBMd .cluster span{color:#333;}#mermaid-svg-vq1EwXxxVLPUDBMd div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-vq1EwXxxVLPUDBMd .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-vq1EwXxxVLPUDBMd rect.text{fill:none;stroke-width:0;}#mermaid-svg-vq1EwXxxVLPUDBMd .icon-shape,#mermaid-svg-vq1EwXxxVLPUDBMd .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-vq1EwXxxVLPUDBMd .icon-shape p,#mermaid-svg-vq1EwXxxVLPUDBMd .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-vq1EwXxxVLPUDBMd .icon-shape .label rect,#mermaid-svg-vq1EwXxxVLPUDBMd .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-vq1EwXxxVLPUDBMd .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-vq1EwXxxVLPUDBMd .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-vq1EwXxxVLPUDBMd :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} plugin.New()
Option 函数们
WithCDIHandler()
WithConfig()
WithDeviceListStrategies()
WithFailOnInitError()
WithImexChannels()
WithDeviceDiscoveryStrategy()
WithNVML()
WithInfoLib()
o := &options{infolib, nvmllib, devicelib}
for _, opt := range opts { opt(o) }
o 完全配置

3.8 插件实例创建:devicePluginForResource()

go 复制代码
func (o *options) devicePluginForResource(ctx context.Context,
    resourceManager rm.ResourceManager) (Interface, error) {
    // 1. 获取 MPS 选项
    mpsOptions, err := o.getMPSOptions(resourceManager)
    // ...

    // 2. 构造 nvidiaDevicePlugin 实例
    plugin := nvidiaDevicePlugin{
        ctx:                  ctx,
        rm:                   resourceManager,
        config:               o.config,
        deviceListStrategies: o.deviceListStrategies,
        cdiHandler:          o.cdiHandler,
        cdiAnnotationPrefix: *o.config.Flags.Plugin.CDIAnnotationPrefix,
        imexChannels:        o.imexChannels,
        mps:                 mpsOptions,
        socket:              getPluginSocketPath(resourceManager.Resource()),
        // 以下字段在每次 Start 时重新初始化
        server: nil,
        health: nil,
        stop:   nil,
    }
    return &plugin, nil
}

func getPluginSocketPath(resource spec.ResourceName) string {
    _, name := resource.Split()
    pluginName := "nvidia-" + name
    return filepath.Join(pluginapi.DevicePluginPath, pluginName) + ".sock"
}

Socket 路径规则: /var/lib/kubelet/device-plugins/nvidia-<resource-name>.sock

  • nvidia.com/gpu/var/lib/kubelet/device-plugins/nvidia-gpu.sock
  • nvidia.com/mig-1g.10gb/var/lib/kubelet/device-plugins/nvidia-mig-1g.10gb.sock

每个资源对应一个独立的插件实例和 gRPC server,kubelet 通过不同的 socket 与各资源类型的插件通信。

3.9 插件启动:Start()

Health Checker gRPC Server nvidiaDevicePlugin Kubelet Health Checker gRPC Server nvidiaDevicePlugin Kubelet #mermaid-svg-RoL1WQE17O8ZLOaq{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-RoL1WQE17O8ZLOaq .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-RoL1WQE17O8ZLOaq .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-RoL1WQE17O8ZLOaq .error-icon{fill:#552222;}#mermaid-svg-RoL1WQE17O8ZLOaq .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-RoL1WQE17O8ZLOaq .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-RoL1WQE17O8ZLOaq .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-RoL1WQE17O8ZLOaq .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-RoL1WQE17O8ZLOaq .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-RoL1WQE17O8ZLOaq .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-RoL1WQE17O8ZLOaq .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-RoL1WQE17O8ZLOaq .marker{fill:#333333;stroke:#333333;}#mermaid-svg-RoL1WQE17O8ZLOaq .marker.cross{stroke:#333333;}#mermaid-svg-RoL1WQE17O8ZLOaq svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-RoL1WQE17O8ZLOaq p{margin:0;}#mermaid-svg-RoL1WQE17O8ZLOaq .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-RoL1WQE17O8ZLOaq text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-RoL1WQE17O8ZLOaq .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-RoL1WQE17O8ZLOaq .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-RoL1WQE17O8ZLOaq .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-RoL1WQE17O8ZLOaq .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-RoL1WQE17O8ZLOaq #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-RoL1WQE17O8ZLOaq .sequenceNumber{fill:white;}#mermaid-svg-RoL1WQE17O8ZLOaq #sequencenumber{fill:#333;}#mermaid-svg-RoL1WQE17O8ZLOaq #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-RoL1WQE17O8ZLOaq .messageText{fill:#333;stroke:none;}#mermaid-svg-RoL1WQE17O8ZLOaq .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-RoL1WQE17O8ZLOaq .labelText,#mermaid-svg-RoL1WQE17O8ZLOaq .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-RoL1WQE17O8ZLOaq .loopText,#mermaid-svg-RoL1WQE17O8ZLOaq .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-RoL1WQE17O8ZLOaq .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-RoL1WQE17O8ZLOaq .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-RoL1WQE17O8ZLOaq .noteText,#mermaid-svg-RoL1WQE17O8ZLOaq .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-RoL1WQE17O8ZLOaq .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-RoL1WQE17O8ZLOaq .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-RoL1WQE17O8ZLOaq .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-RoL1WQE17O8ZLOaq .actorPopupMenu{position:absolute;}#mermaid-svg-RoL1WQE17O8ZLOaq .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-RoL1WQE17O8ZLOaq .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-RoL1WQE17O8ZLOaq .actor-man circle,#mermaid-svg-RoL1WQE17O8ZLOaq line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-RoL1WQE17O8ZLOaq :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 创建 grpc.Server创建 health channel创建 stop channel 监听 Unix socket注册 DevicePluginServer启动崩溃重启 goroutine 发送 RegisterRequest(Version, Endpoint, ResourceName, Options) 持续监控 GPU 健康通过 health channel 推送状态 initialize()mps.waitForDaemon()Serve()server 就绪 (dial 验证)Register()注册成功go rm.CheckHealth()

go 复制代码
func (plugin *nvidiaDevicePlugin) Start(kubeletSocket string) error {
    plugin.initialize()  // ① 初始化 server/health/stop

    if err := plugin.mps.waitForDaemon(); err != nil {  // ② 等待 MPS daemon
        return fmt.Errorf("error waiting for MPS daemon: %w", err)
    }

    err := plugin.Serve()  // ③ 启动 gRPC 服务
    if err != nil {
        plugin.cleanup()
        return err
    }

    err = plugin.Register(kubeletSocket)  // ④ 注册到 kubelet
    if err != nil {
        return errors.Join(err, plugin.Stop())
    }

    go func() {  // ⑤ 启动健康检查 goroutine
        err := plugin.rm.CheckHealth(plugin.stop, plugin.health)
        if err != nil {
            klog.Errorf("Failed to start health check: %v; continuing with health checks disabled", err)
        }
    }()

    return nil
}

initialize() / cleanup() 配对:

go 复制代码
func (plugin *nvidiaDevicePlugin) initialize() {
    plugin.server = grpc.NewServer([]grpc.ServerOption{}...)
    plugin.health = make(chan *rm.Device)
    plugin.stop = make(chan interface{})
}

func (plugin *nvidiaDevicePlugin) cleanup() {
    close(plugin.stop)
    plugin.server = nil
    plugin.health = nil
    plugin.stop = nil
}
  • initialize 创建三个运行时组件:gRPC server、health channel(接收健康状态变更)、stop channel(广播停止信号)
  • cleanup 关闭 stop channel 并置空所有运行时字段,使插件回到可重新 Start 的状态

3.10 gRPC 服务启动:Serve()

go 复制代码
func (plugin *nvidiaDevicePlugin) Serve() error {
    os.Remove(plugin.socket)  // 清理残留 socket
    sock, err := net.Listen("unix", plugin.socket)  // 监听 Unix socket
    if err != nil {
        return err
    }

    pluginapi.RegisterDevicePluginServer(plugin.server, plugin)  // 注册 gRPC 服务

    go func() {  // 启动 gRPC server(含崩溃重启)
        lastCrashTime := time.Now()
        restartCount := 0

        for {
            // 防止频繁崩溃:5 次崩溃且每次存活 < 1 小时 → 退出
            if restartCount > 5 {
                klog.Fatalf("GRPC server for '%s' has repeatedly crashed recently. Quitting",
                    plugin.rm.Resource())
            }

            klog.Infof("Starting GRPC server for '%s'", plugin.rm.Resource())
            err := plugin.server.Serve(sock)
            if err == nil {
                break  // 正常停止
            }

            klog.Infof("GRPC server for '%s' crashed with error: %v", plugin.rm.Resource(), err)

            timeSinceLastCrash := time.Since(lastCrashTime).Seconds()
            lastCrashTime = time.Now()
            if timeSinceLastCrash > 3600 {
                restartCount = 0  // 超过 1 小时,重置计数
            } else {
                restartCount++
            }
        }
    }()

    // 阻塞等待 server 启动完成
    conn, err := plugin.dial(plugin.socket, 5*time.Second)
    if err != nil {
        return err
    }
    conn.Close()

    return nil
}

崩溃重启策略:
#mermaid-svg-TJXe3FzDeyxNkO0H{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-TJXe3FzDeyxNkO0H .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-TJXe3FzDeyxNkO0H .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-TJXe3FzDeyxNkO0H .error-icon{fill:#552222;}#mermaid-svg-TJXe3FzDeyxNkO0H .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-TJXe3FzDeyxNkO0H .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-TJXe3FzDeyxNkO0H .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-TJXe3FzDeyxNkO0H .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-TJXe3FzDeyxNkO0H .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-TJXe3FzDeyxNkO0H .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-TJXe3FzDeyxNkO0H .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-TJXe3FzDeyxNkO0H .marker{fill:#333333;stroke:#333333;}#mermaid-svg-TJXe3FzDeyxNkO0H .marker.cross{stroke:#333333;}#mermaid-svg-TJXe3FzDeyxNkO0H svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-TJXe3FzDeyxNkO0H p{margin:0;}#mermaid-svg-TJXe3FzDeyxNkO0H .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-TJXe3FzDeyxNkO0H .cluster-label text{fill:#333;}#mermaid-svg-TJXe3FzDeyxNkO0H .cluster-label span{color:#333;}#mermaid-svg-TJXe3FzDeyxNkO0H .cluster-label span p{background-color:transparent;}#mermaid-svg-TJXe3FzDeyxNkO0H .label text,#mermaid-svg-TJXe3FzDeyxNkO0H span{fill:#333;color:#333;}#mermaid-svg-TJXe3FzDeyxNkO0H .node rect,#mermaid-svg-TJXe3FzDeyxNkO0H .node circle,#mermaid-svg-TJXe3FzDeyxNkO0H .node ellipse,#mermaid-svg-TJXe3FzDeyxNkO0H .node polygon,#mermaid-svg-TJXe3FzDeyxNkO0H .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-TJXe3FzDeyxNkO0H .rough-node .label text,#mermaid-svg-TJXe3FzDeyxNkO0H .node .label text,#mermaid-svg-TJXe3FzDeyxNkO0H .image-shape .label,#mermaid-svg-TJXe3FzDeyxNkO0H .icon-shape .label{text-anchor:middle;}#mermaid-svg-TJXe3FzDeyxNkO0H .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-TJXe3FzDeyxNkO0H .rough-node .label,#mermaid-svg-TJXe3FzDeyxNkO0H .node .label,#mermaid-svg-TJXe3FzDeyxNkO0H .image-shape .label,#mermaid-svg-TJXe3FzDeyxNkO0H .icon-shape .label{text-align:center;}#mermaid-svg-TJXe3FzDeyxNkO0H .node.clickable{cursor:pointer;}#mermaid-svg-TJXe3FzDeyxNkO0H .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-TJXe3FzDeyxNkO0H .arrowheadPath{fill:#333333;}#mermaid-svg-TJXe3FzDeyxNkO0H .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-TJXe3FzDeyxNkO0H .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-TJXe3FzDeyxNkO0H .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-TJXe3FzDeyxNkO0H .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-TJXe3FzDeyxNkO0H .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-TJXe3FzDeyxNkO0H .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-TJXe3FzDeyxNkO0H .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-TJXe3FzDeyxNkO0H .cluster text{fill:#333;}#mermaid-svg-TJXe3FzDeyxNkO0H .cluster span{color:#333;}#mermaid-svg-TJXe3FzDeyxNkO0H div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-TJXe3FzDeyxNkO0H .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-TJXe3FzDeyxNkO0H rect.text{fill:none;stroke-width:0;}#mermaid-svg-TJXe3FzDeyxNkO0H .icon-shape,#mermaid-svg-TJXe3FzDeyxNkO0H .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-TJXe3FzDeyxNkO0H .icon-shape p,#mermaid-svg-TJXe3FzDeyxNkO0H .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-TJXe3FzDeyxNkO0H .icon-shape .label rect,#mermaid-svg-TJXe3FzDeyxNkO0H .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-TJXe3FzDeyxNkO0H .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-TJXe3FzDeyxNkO0H .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-TJXe3FzDeyxNkO0H :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Yes
No
Yes
No
Yes
No
启动 goroutine
restartCount > 5?
klog.Fatalf 退出
plugin.server.Serve(sock)
err == nil?
正常退出 goroutine
计算距上次崩溃时间
> 3600s?
restartCount = 0
restartCount++

设计意图: 防止 gRPC server 反复崩溃导致 CPU 飙升。通过 lastCrashTime + restartCount 组合实现退避------存活超 1 小时则重置计数;频繁崩溃超 5 次则 Fatal 退出(依赖 K8s 重启 Pod)。

3.11 注册到 Kubelet:Register()

go 复制代码
func (plugin *nvidiaDevicePlugin) Register(kubeletSocket string) error {
    if kubeletSocket == "" {
        klog.Info("Skipping registration with Kubelet")
        return nil  // 空 socket → 跳过注册(用于测试/调试场景)
    }

    conn, err := plugin.dial(kubeletSocket, 5*time.Second)  // 连接 kubelet
    // ...
    defer conn.Close()

    client := pluginapi.NewRegistrationClient(conn)
    reqt := &pluginapi.RegisterRequest{
        Version:      pluginapi.Version,           // Device Plugin API 版本
        Endpoint:     path.Base(plugin.socket),    // 插件 socket 文件名
        ResourceName: string(plugin.rm.Resource()), // 如 "nvidia.com/gpu"
        Options: &pluginapi.DevicePluginOptions{
            GetPreferredAllocationAvailable: true,  // 声明支持 PreferredAllocation
        },
    }

    _, err = client.Register(plugin.ctx, reqt)
    return err
}

注册请求字段说明:

字段 说明
Version v1beta1 K8s Device Plugin API 版本
Endpoint nvidia-gpu.sock kubelet 通过此路径连接插件
ResourceName nvidia.com/gpu 注册为 K8s Extended Resource
GetPreferredAllocationAvailable true 告知 kubelet 本插件实现了 GetPreferredAllocation

3.12 设备列表与健康监控:ListAndWatch()

go 复制代码
func (plugin *nvidiaDevicePlugin) ListAndWatch(e *pluginapi.Empty,
    s pluginapi.DevicePlugin_ListAndWatchServer) error {
    // 1. 首次推送完整设备列表
    if err := s.Send(&pluginapi.ListAndWatchResponse{Devices: plugin.apiDevices()}); err != nil {
        return err
    }

    // 2. 持续监听健康状态变更
    for {
        select {
        case <-plugin.stop:  // 插件停止 → 退出
            return nil
        case d := <-plugin.health:  // 收到健康状态变更
            // FIXME: 不可恢复 --- Unhealthy 是终态
            d.Health = pluginapi.Unhealthy
            klog.Infof("'%s' device marked unhealthy: %s", plugin.rm.Resource(), d.ID)
            if err := s.Send(&pluginapi.ListAndWatchResponse{Devices: plugin.apiDevices()}); err != nil {
                return nil
            }
        }
    }
}

ResourceManager Health Checker (goroutine) ListAndWatch() Kubelet ResourceManager Health Checker (goroutine) ListAndWatch() Kubelet #mermaid-svg-gbLJ5wJnQgy2mtQ3{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-gbLJ5wJnQgy2mtQ3 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-gbLJ5wJnQgy2mtQ3 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-gbLJ5wJnQgy2mtQ3 .error-icon{fill:#552222;}#mermaid-svg-gbLJ5wJnQgy2mtQ3 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-gbLJ5wJnQgy2mtQ3 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-gbLJ5wJnQgy2mtQ3 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-gbLJ5wJnQgy2mtQ3 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-gbLJ5wJnQgy2mtQ3 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-gbLJ5wJnQgy2mtQ3 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-gbLJ5wJnQgy2mtQ3 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-gbLJ5wJnQgy2mtQ3 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-gbLJ5wJnQgy2mtQ3 .marker.cross{stroke:#333333;}#mermaid-svg-gbLJ5wJnQgy2mtQ3 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-gbLJ5wJnQgy2mtQ3 p{margin:0;}#mermaid-svg-gbLJ5wJnQgy2mtQ3 .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-gbLJ5wJnQgy2mtQ3 text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-gbLJ5wJnQgy2mtQ3 .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-gbLJ5wJnQgy2mtQ3 .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-gbLJ5wJnQgy2mtQ3 .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-gbLJ5wJnQgy2mtQ3 .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-gbLJ5wJnQgy2mtQ3 #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-gbLJ5wJnQgy2mtQ3 .sequenceNumber{fill:white;}#mermaid-svg-gbLJ5wJnQgy2mtQ3 #sequencenumber{fill:#333;}#mermaid-svg-gbLJ5wJnQgy2mtQ3 #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-gbLJ5wJnQgy2mtQ3 .messageText{fill:#333;stroke:none;}#mermaid-svg-gbLJ5wJnQgy2mtQ3 .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-gbLJ5wJnQgy2mtQ3 .labelText,#mermaid-svg-gbLJ5wJnQgy2mtQ3 .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-gbLJ5wJnQgy2mtQ3 .loopText,#mermaid-svg-gbLJ5wJnQgy2mtQ3 .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-gbLJ5wJnQgy2mtQ3 .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-gbLJ5wJnQgy2mtQ3 .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-gbLJ5wJnQgy2mtQ3 .noteText,#mermaid-svg-gbLJ5wJnQgy2mtQ3 .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-gbLJ5wJnQgy2mtQ3 .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-gbLJ5wJnQgy2mtQ3 .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-gbLJ5wJnQgy2mtQ3 .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-gbLJ5wJnQgy2mtQ3 .actorPopupMenu{position:absolute;}#mermaid-svg-gbLJ5wJnQgy2mtQ3 .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-gbLJ5wJnQgy2mtQ3 .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-gbLJ5wJnQgy2mtQ3 .actor-man circle,#mermaid-svg-gbLJ5wJnQgy2mtQ3 line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-gbLJ5wJnQgy2mtQ3 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} loop持续监控 stop channel 关闭时退出 s.Send(完整设备列表)进入 select 循环检测到设备异常health channel <- Device{unhealthy}d.Health = Unhealthys.Send(更新后设备列表)

关键设计点:

  • 首次调用必须发送完整设备列表,kubelet 据此建立设备清单
  • 健康状态变更是增量推送------只在 health channel 有事件时才重新发送完整列表
  • Unhealthy不可逆终态(代码中 FIXME 标注),设备一旦标记为不健康就无法恢复
  • stop channel 关闭时优雅退出 stream

3.13 设备分配:Allocate()

这是设备插件最核心的 gRPC 方法------当 Pod 请求 GPU 资源时,kubelet 调用此方法获取分配信息。

go 复制代码
func (plugin *nvidiaDevicePlugin) Allocate(ctx context.Context,
    reqs *pluginapi.AllocateRequest) (*pluginapi.AllocateResponse, error) {
    responses := pluginapi.AllocateResponse{}
    for _, req := range reqs.ContainerRequests {
        // 1. 校验请求合法性
        if err := plugin.rm.ValidateRequest(req.DevicesIds); err != nil {
            return nil, fmt.Errorf("invalid allocation request for %q: %w", plugin.rm.Resource(), err)
        }
        // 2. 构造分配响应
        response, err := plugin.getAllocateResponse(req.DevicesIds)
        if err != nil {
            return nil, fmt.Errorf("failed to get allocate response: %v", err)
        }
        responses.ContainerResponses = append(responses.ContainerResponses, response)
    }
    return &responses, nil
}

getAllocateResponse() --- 分配响应构造核心:

go 复制代码
func (plugin *nvidiaDevicePlugin) getAllocateResponse(requestIds []string) (*pluginapi.ContainerAllocateResponse, error) {
    // 1. 从带注解的设备 ID 中提取真实设备 ID
    deviceIDs := plugin.uniqueDeviceIDsFromAnnotatedDeviceIDs(requestIds)

    // 2. 创建空响应
    response := &pluginapi.ContainerAllocateResponse{
        Envs: make(map[string]string),
    }

    // 3. CDI 策略处理
    if plugin.deviceListStrategies.AnyCDIEnabled() {
        responseID := uuid.New().String()
        if err := plugin.updateResponseForCDI(response, responseID, deviceIDs...); err != nil {
            return nil, fmt.Errorf("failed to get allocate response for CDI: %v", err)
        }
    }

    // 4. MPS 处理
    if plugin.mps.enabled {
        plugin.updateResponseForMPS(response)
    }

    // 5. 高级特性环境变量
    if plugin.config.Flags.GDRCopyEnabled != nil && *plugin.config.Flags.GDRCopyEnabled {
        response.Envs["NVIDIA_GDRCOPY"] = "enabled"
    }
    if plugin.config.Flags.GDSEnabled != nil && *plugin.config.Flags.GDSEnabled {
        response.Envs["NVIDIA_GDS"] = "enabled"
    }
    if plugin.config.Flags.MOFEDEnabled != nil && *plugin.config.Flags.MOFEDEnabled {
        response.Envs["NVIDIA_MOFED"] = "enabled"
    }

    // 6. 如果全部使用 CDI 策略,到此返回
    if plugin.deviceListStrategies.AllCDIEnabled() {
        return response, nil
    }

    // 7. 非 CDI 策略处理
    if plugin.deviceListStrategies.Includes(spec.DeviceListStrategyEnvVar) {
        plugin.updateResponseForDeviceListEnvVar(response, deviceIDs...)
        plugin.updateResponseForImexChannelsEnvVar(response)
    }
    if plugin.deviceListStrategies.Includes(spec.DeviceListStrategyVolumeMounts) {
        plugin.updateResponseForDeviceMounts(response, deviceIDs...)
    }
    if plugin.config.Flags.Plugin.PassDeviceSpecs != nil && *plugin.config.Flags.Plugin.PassDeviceSpecs {
        response.Devices = append(response.Devices,
            plugin.apiDeviceSpecs(*plugin.config.Flags.NvidiaDevRoot, requestIds)...)
    }

    return response, nil
}

分配响应构造流程图:
#mermaid-svg-bB9Z4EyiC6wmG2rp{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-bB9Z4EyiC6wmG2rp .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-bB9Z4EyiC6wmG2rp .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-bB9Z4EyiC6wmG2rp .error-icon{fill:#552222;}#mermaid-svg-bB9Z4EyiC6wmG2rp .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-bB9Z4EyiC6wmG2rp .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-bB9Z4EyiC6wmG2rp .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-bB9Z4EyiC6wmG2rp .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-bB9Z4EyiC6wmG2rp .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-bB9Z4EyiC6wmG2rp .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-bB9Z4EyiC6wmG2rp .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-bB9Z4EyiC6wmG2rp .marker{fill:#333333;stroke:#333333;}#mermaid-svg-bB9Z4EyiC6wmG2rp .marker.cross{stroke:#333333;}#mermaid-svg-bB9Z4EyiC6wmG2rp svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-bB9Z4EyiC6wmG2rp p{margin:0;}#mermaid-svg-bB9Z4EyiC6wmG2rp .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-bB9Z4EyiC6wmG2rp .cluster-label text{fill:#333;}#mermaid-svg-bB9Z4EyiC6wmG2rp .cluster-label span{color:#333;}#mermaid-svg-bB9Z4EyiC6wmG2rp .cluster-label span p{background-color:transparent;}#mermaid-svg-bB9Z4EyiC6wmG2rp .label text,#mermaid-svg-bB9Z4EyiC6wmG2rp span{fill:#333;color:#333;}#mermaid-svg-bB9Z4EyiC6wmG2rp .node rect,#mermaid-svg-bB9Z4EyiC6wmG2rp .node circle,#mermaid-svg-bB9Z4EyiC6wmG2rp .node ellipse,#mermaid-svg-bB9Z4EyiC6wmG2rp .node polygon,#mermaid-svg-bB9Z4EyiC6wmG2rp .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-bB9Z4EyiC6wmG2rp .rough-node .label text,#mermaid-svg-bB9Z4EyiC6wmG2rp .node .label text,#mermaid-svg-bB9Z4EyiC6wmG2rp .image-shape .label,#mermaid-svg-bB9Z4EyiC6wmG2rp .icon-shape .label{text-anchor:middle;}#mermaid-svg-bB9Z4EyiC6wmG2rp .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-bB9Z4EyiC6wmG2rp .rough-node .label,#mermaid-svg-bB9Z4EyiC6wmG2rp .node .label,#mermaid-svg-bB9Z4EyiC6wmG2rp .image-shape .label,#mermaid-svg-bB9Z4EyiC6wmG2rp .icon-shape .label{text-align:center;}#mermaid-svg-bB9Z4EyiC6wmG2rp .node.clickable{cursor:pointer;}#mermaid-svg-bB9Z4EyiC6wmG2rp .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-bB9Z4EyiC6wmG2rp .arrowheadPath{fill:#333333;}#mermaid-svg-bB9Z4EyiC6wmG2rp .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-bB9Z4EyiC6wmG2rp .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-bB9Z4EyiC6wmG2rp .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-bB9Z4EyiC6wmG2rp .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-bB9Z4EyiC6wmG2rp .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-bB9Z4EyiC6wmG2rp .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-bB9Z4EyiC6wmG2rp .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-bB9Z4EyiC6wmG2rp .cluster text{fill:#333;}#mermaid-svg-bB9Z4EyiC6wmG2rp .cluster span{color:#333;}#mermaid-svg-bB9Z4EyiC6wmG2rp div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-bB9Z4EyiC6wmG2rp .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-bB9Z4EyiC6wmG2rp rect.text{fill:none;stroke-width:0;}#mermaid-svg-bB9Z4EyiC6wmG2rp .icon-shape,#mermaid-svg-bB9Z4EyiC6wmG2rp .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-bB9Z4EyiC6wmG2rp .icon-shape p,#mermaid-svg-bB9Z4EyiC6wmG2rp .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-bB9Z4EyiC6wmG2rp .icon-shape .label rect,#mermaid-svg-bB9Z4EyiC6wmG2rp .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-bB9Z4EyiC6wmG2rp .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-bB9Z4EyiC6wmG2rp .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-bB9Z4EyiC6wmG2rp :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Yes
No
Yes
No
Yes
No
Yes
No
Yes
No
Yes
No
getAllocateResponse()
提取设备 ID
CDI?
updateResponseForCDI()
MPS?
updateResponseForMPS()
设置 GDRCopy/GDS/MOFED envs
AllCDI?
返回
EnvVar?
设置 NVIDIA_VISIBLE_DEVICES
VolumeMounts?
updateResponseForDeviceMounts()
PassDeviceSpecs?
apiDeviceSpecs()

3.14 CDI 响应处理:updateResponseForCDI()

go 复制代码
func (plugin *nvidiaDevicePlugin) updateResponseForCDI(
    response *pluginapi.ContainerAllocateResponse,
    responseID string, deviceIDs ...string) error {
    // 1. 构造 CDI 设备名列表
    var devices []string
    for _, id := range deviceIDs {
        devices = append(devices, plugin.cdiHandler.QualifiedName("gpu", id))
    }
    // 添加 IMEX 通道
    for _, channel := range plugin.imexChannels {
        devices = append(devices, plugin.cdiHandler.QualifiedName("imex-channel", channel.ID))
    }
    // 添加额外设备
    devices = append(devices, plugin.cdiHandler.AdditionalDevices()...)

    if len(devices) == 0 {
        return nil
    }

    // 2. 根据 CDI 策略生成响应
    if plugin.deviceListStrategies.Includes(spec.DeviceListStrategyCDIAnnotations) {
        annotations, err := plugin.getCDIDeviceAnnotations(responseID, devices...)
        if err != nil {
            return err
        }
        response.Annotations = annotations
    }
    if plugin.deviceListStrategies.Includes(spec.DeviceListStrategyCDICRI) {
        for _, device := range devices {
            cdiDevice := pluginapi.CDIDevice{Name: device}
            response.CdiDevices = append(response.CdiDevices, &cdiDevice)
        }
    }

    return nil
}

CDI 设备名格式: k8s.device-plugin.nvidia.com/gpu=<UUID>

两种 CDI 策略对比:

策略 响应字段 消费者
cdi-annotations Annotations map nvidia-container-runtime 解析注解
cdi-cri CdiDevices 列表 CRI runtime 直接消费
go 复制代码
func (plugin *nvidiaDevicePlugin) getCDIDeviceAnnotations(id string, devices ...string) (map[string]string, error) {
    // 使用 CDI SDK 生成标准注解
    annotations, err := cdiapi.UpdateAnnotations(map[string]string{}, "nvidia-device-plugin", id, devices)
    // ...

    // 如果使用自定义前缀,替换标准前缀
    if plugin.cdiAnnotationPrefix == spec.DefaultCDIAnnotationPrefix {
        return annotations, nil
    }

    updatedAnnotations := make(map[string]string)
    for k, v := range annotations {
        newKey := plugin.cdiAnnotationPrefix + strings.TrimPrefix(k, spec.DefaultCDIAnnotationPrefix)
        updatedAnnotations[newKey] = v
    }
    return updatedAnnotations, nil
}

3.15 设备 ID 处理:uniqueDeviceIDsFromAnnotatedDeviceIDs()

go 复制代码
func (plugin *nvidiaDevicePlugin) uniqueDeviceIDsFromAnnotatedDeviceIDs(ids []string) []string {
    var deviceIDs []string
    // 根据 ID 策略提取真实设备标识
    if *plugin.config.Flags.Plugin.DeviceIDStrategy == spec.DeviceIDStrategyUUID {
        deviceIDs = rm.AnnotatedIDs(ids).GetIDs()  // 从注解 ID 中提取 UUID
    }
    if *plugin.config.Flags.Plugin.DeviceIDStrategy == spec.DeviceIDStrategyIndex {
        deviceIDs = plugin.rm.Devices().Subset(ids).GetIndices()  // 获取设备索引
    }

    // 去重(同一设备可能被多次请求)
    var uniqueIDs []string
    seen := make(map[string]bool)
    for _, id := range deviceIDs {
        if seen[id] {
            continue
        }
        seen[id] = true
        uniqueIDs = append(uniqueIDs, id)
    }
    return uniqueIDs
}

UUID vs Index 策略:

  • UUID :设备以 GPU UUID 标识(如 GPU-abc123-...),稳定但冗长
  • Index :设备以索引号标识(如 0, 1, 2),简洁但可能因硬件变更而变化

3.16 DeviceSpecs 生成:apiDeviceSpecs()

go 复制代码
func (plugin *nvidiaDevicePlugin) apiDeviceSpecs(devRoot string, ids []string) []*pluginapi.DeviceSpec {
    // 可选设备路径 --- 不存在则跳过
    optional := map[string]bool{
        "/dev/nvidiactl":        true,
        "/dev/nvidia-uvm":       true,
        "/dev/nvidia-uvm-tools": true,
        "/dev/nvidia-modeset":   true,
    }

    paths := plugin.rm.GetDevicePaths(ids)  // 获取设备文件路径列表

    var specs []*pluginapi.DeviceSpec
    for _, p := range paths {
        if optional[p] {
            if _, err := os.Stat(p); err != nil {
                continue  // 可选设备不存在则跳过
            }
        }
        spec := &pluginapi.DeviceSpec{
            ContainerPath: p,                              // 容器内路径(固定)
            HostPath:      filepath.Join(devRoot, p),      // 宿主机路径(拼接 devRoot)
            Permissions:   "rw",                           // 读写权限
        }
        specs = append(specs, spec)
    }

    // 添加 IMEX 通道设备
    for _, channel := range plugin.imexChannels {
        spec := &pluginapi.DeviceSpec{
            ContainerPath: channel.Path,
            HostPath:      filepath.Join(devRoot, channel.Path),
            Permissions:   "rw",
        }
        specs = append(specs, spec)
    }

    return specs
}

DeviceSpec 结构说明:

  • ContainerPath:设备在容器内的路径(如 /dev/nvidia0
  • HostPath:设备在宿主机上的路径(如 /run/nvidia/driver/dev/nvidia0
  • Permissions:设备权限,固定为 rw

可选设备处理: nvidiactlnvidia-uvm 等控制设备可能不存在于所有配置中,通过 os.Stat 检测后决定是否包含。

3.17 PreferredAllocation:GetPreferredAllocation()

go 复制代码
func (plugin *nvidiaDevicePlugin) GetPreferredAllocation(ctx context.Context,
    r *pluginapi.PreferredAllocationRequest) (*pluginapi.PreferredAllocationResponse, error) {
    response := &pluginapi.PreferredAllocationResponse{}
    for _, req := range r.ContainerRequests {
        // 委托给 ResourceManager 获取推荐分配
        devices, err := plugin.rm.GetPreferredAllocation(
            req.AvailableDeviceIDs,   // 可用设备列表
            req.MustIncludeDeviceIDs,  // 必须包含的设备
            int(req.AllocationSize),   // 需要分配的数量
        )
        if err != nil {
            return nil, fmt.Errorf("error getting list of preferred allocation devices: %v", err)
        }

        resp := &pluginapi.ContainerPreferredAllocationResponse{
            DeviceIDs: devices,
        }
        response.ContainerResponses = append(response.ContainerResponses, resp)
    }
    return response, nil
}

设计意图: 允许插件在调度层面提供"偏好"建议。例如对于 MIG 策略,可能偏好某些 GPU 上的特定 MIG 实例。kubelet 在 scheduling hints 中使用此信息。

3.18 Volume Mounts 策略:updateResponseForDeviceMounts()

go 复制代码
func (plugin *nvidiaDevicePlugin) updateResponseForDeviceMounts(
    response *pluginapi.ContainerAllocateResponse, deviceIDs ...string) {
    // 设置环境变量指向 volume mount 根目录
    plugin.updateResponseForDeviceListEnvVar(response, deviceListAsVolumeMountsContainerPathRoot)

    // 为每个设备创建一个 volume mount
    for _, id := range deviceIDs {
        mount := &pluginapi.Mount{
            HostPath:      deviceListAsVolumeMountsHostPath,           // /dev/null(哑文件)
            ContainerPath: filepath.Join(deviceListAsVolumeMountsContainerPathRoot, id),
        }
        response.Mounts = append(response.Mounts, mount)
    }
    // 为 IMEX 通道也创建 volume mount
    for _, channel := range plugin.imexChannels {
        mount := &pluginapi.Mount{
            HostPath:      deviceListAsVolumeMountsHostPath,
            ContainerPath: filepath.Join(deviceListAsVolumeMountsContainerPathRoot, "imex", channel.ID),
        }
        response.Mounts = append(response.Mounts, mount)
    }
}

Volume Mounts 策略: HostPath=/dev/null(哑文件),ContainerPath=/var/run/nvidia-container-devices/<deviceID>。NVIDIA Container Toolkit 读取挂载路径识别要注入的设备。

3.19 MPS 选项处理 (mps.go)

go 复制代码
func (o *options) getMPSOptions(resourceManager rm.ResourceManager) (mpsOptions, error) {
    // 非 MPS 策略 → 返回空选项
    if o.config.Sharing.SharingStrategy() != spec.SharingStrategyMPS {
        return mpsOptions{}, nil
    }

    // MPS 不支持 MIG 设备
    for _, device := range resourceManager.Devices() {
        if device.IsMigDevice() {
            return mpsOptions{}, errors.New("sharing using MPS is not supported for MIG devices")
        }
    }

    m := mpsOptions{
        enabled:      true,
        resourceName: resourceManager.Resource(),
        daemon:       mps.NewDaemon(resourceManager, mps.ContainerRoot),
        hostRoot:     mps.Root(*o.config.Flags.MpsRoot),
    }
    return m, nil
}
go 复制代码
func (m *mpsOptions) updateReponse(response *pluginapi.ContainerAllocateResponse) {
    if m == nil || !m.enabled {
        return
    }
    // 设置 MPS 管道目录环境变量
    response.Envs["CUDA_MPS_PIPE_DIRECTORY"] = m.daemon.PipeDir()

    // 添加管道目录和共享内存目录的挂载
    response.Mounts = append(response.Mounts,
        &pluginapi.Mount{
            ContainerPath: m.daemon.PipeDir(),                        // 容器内管道目录
            HostPath:      m.hostRoot.PipeDir(m.resourceName),        // 宿主机管道目录
        },
        &pluginapi.Mount{
            ContainerPath: m.daemon.ShmDir(),                         // 容器内共享内存目录
            HostPath:      m.hostRoot.ShmDir(m.resourceName),         // 宿主机共享内存目录
        },
    )
}

MPS 机制: 多容器共享同一 GPU 计算上下文。通过 CUDA_MPS_PIPE_DIRECTORY 指定管道目录,容器挂载宿主机管道目录和共享内存目录与 MPS daemon 通信。每个资源有独立路径避免冲突。

3.20 Root 路径工具 (root.go)

go 复制代码
type root string

func (r root) join(parts ...string) string {
    return filepath.Join(append([]string{string(r)}, parts...)...)
}

func (r root) getDevRoot() string {
    if r.isDevRoot() {
        return string(r)  // root 本身包含 /dev 目录 → 是 dev root
    }
    return "/"  // 否则 dev root 默认为 /
}

func (r root) isDevRoot() bool {
    stat, err := os.Stat(filepath.Join(string(r), "dev"))
    if err != nil {
        return false
    }
    return stat.IsDir()
}

func (r root) tryResolveLibrary(libraryName string) string {
    if r == "" || r == "/" {
        return libraryName  // 根路径 → 直接返回库名(依赖系统搜索路径)
    }

    // 在 driver root 下搜索常见库路径
    librarySearchPaths := []string{
        "/usr/lib64",
        "/usr/lib/x86_64-linux-gnu",
        "/usr/lib/aarch64-linux-gnu",
        "/lib64",
        "/lib/x86_64-linux-gnu",
        "/lib/aarch64-linux-gnu",
    }

    for _, d := range librarySearchPaths {
        l := r.join(d, libraryName)
        resolved, err := resolveLink(l)  // 解析符号链接
        if err != nil {
            continue
        }
        return resolved
    }

    return libraryName  // 找不到 → 返回原始名称(依赖系统路径)
}

Driver Root vs Dev Root 概念:
#mermaid-svg-lp7aGlmJyzpvw4Gp{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-lp7aGlmJyzpvw4Gp .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-lp7aGlmJyzpvw4Gp .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-lp7aGlmJyzpvw4Gp .error-icon{fill:#552222;}#mermaid-svg-lp7aGlmJyzpvw4Gp .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-lp7aGlmJyzpvw4Gp .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-lp7aGlmJyzpvw4Gp .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-lp7aGlmJyzpvw4Gp .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-lp7aGlmJyzpvw4Gp .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-lp7aGlmJyzpvw4Gp .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-lp7aGlmJyzpvw4Gp .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-lp7aGlmJyzpvw4Gp .marker{fill:#333333;stroke:#333333;}#mermaid-svg-lp7aGlmJyzpvw4Gp .marker.cross{stroke:#333333;}#mermaid-svg-lp7aGlmJyzpvw4Gp svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-lp7aGlmJyzpvw4Gp p{margin:0;}#mermaid-svg-lp7aGlmJyzpvw4Gp .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-lp7aGlmJyzpvw4Gp .cluster-label text{fill:#333;}#mermaid-svg-lp7aGlmJyzpvw4Gp .cluster-label span{color:#333;}#mermaid-svg-lp7aGlmJyzpvw4Gp .cluster-label span p{background-color:transparent;}#mermaid-svg-lp7aGlmJyzpvw4Gp .label text,#mermaid-svg-lp7aGlmJyzpvw4Gp span{fill:#333;color:#333;}#mermaid-svg-lp7aGlmJyzpvw4Gp .node rect,#mermaid-svg-lp7aGlmJyzpvw4Gp .node circle,#mermaid-svg-lp7aGlmJyzpvw4Gp .node ellipse,#mermaid-svg-lp7aGlmJyzpvw4Gp .node polygon,#mermaid-svg-lp7aGlmJyzpvw4Gp .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-lp7aGlmJyzpvw4Gp .rough-node .label text,#mermaid-svg-lp7aGlmJyzpvw4Gp .node .label text,#mermaid-svg-lp7aGlmJyzpvw4Gp .image-shape .label,#mermaid-svg-lp7aGlmJyzpvw4Gp .icon-shape .label{text-anchor:middle;}#mermaid-svg-lp7aGlmJyzpvw4Gp .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-lp7aGlmJyzpvw4Gp .rough-node .label,#mermaid-svg-lp7aGlmJyzpvw4Gp .node .label,#mermaid-svg-lp7aGlmJyzpvw4Gp .image-shape .label,#mermaid-svg-lp7aGlmJyzpvw4Gp .icon-shape .label{text-align:center;}#mermaid-svg-lp7aGlmJyzpvw4Gp .node.clickable{cursor:pointer;}#mermaid-svg-lp7aGlmJyzpvw4Gp .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-lp7aGlmJyzpvw4Gp .arrowheadPath{fill:#333333;}#mermaid-svg-lp7aGlmJyzpvw4Gp .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-lp7aGlmJyzpvw4Gp .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-lp7aGlmJyzpvw4Gp .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-lp7aGlmJyzpvw4Gp .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-lp7aGlmJyzpvw4Gp .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-lp7aGlmJyzpvw4Gp .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-lp7aGlmJyzpvw4Gp .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-lp7aGlmJyzpvw4Gp .cluster text{fill:#333;}#mermaid-svg-lp7aGlmJyzpvw4Gp .cluster span{color:#333;}#mermaid-svg-lp7aGlmJyzpvw4Gp div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-lp7aGlmJyzpvw4Gp .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-lp7aGlmJyzpvw4Gp rect.text{fill:none;stroke-width:0;}#mermaid-svg-lp7aGlmJyzpvw4Gp .icon-shape,#mermaid-svg-lp7aGlmJyzpvw4Gp .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-lp7aGlmJyzpvw4Gp .icon-shape p,#mermaid-svg-lp7aGlmJyzpvw4Gp .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-lp7aGlmJyzpvw4Gp .icon-shape .label rect,#mermaid-svg-lp7aGlmJyzpvw4Gp .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-lp7aGlmJyzpvw4Gp .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-lp7aGlmJyzpvw4Gp .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-lp7aGlmJyzpvw4Gp :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 分离配置
/run/nvidia/driver

(driver-root)
libnvidia-ml.so.1
/

(dev-root)
/dev
Driver Container 节点
/run/nvidia/driver
/run/nvidia/driver/dev
/run/nvidia/driver/usr/lib64/libnvidia-ml.so.1
标准节点
/
/dev
/usr/lib64/libnvidia-ml.so.1

3.21 PreStartContainer

go 复制代码
func (plugin *nvidiaDevicePlugin) PreStartContainer(context.Context,
    *pluginapi.PreStartContainerRequest) (*pluginapi.PreStartContainerResponse, error) {
    return &pluginapi.PreStartContainerResponse{}, nil
}

空实现 --- 当前版本不需要在容器启动前执行任何操作。这是 Device Plugin API 的可选方法,保留接口完整性。

3.22 Stop 与清理

go 复制代码
func (plugin *nvidiaDevicePlugin) Stop() error {
    if plugin == nil || plugin.server == nil {
        return nil  // 防御性检查:未启动的插件直接返回
    }
    klog.Infof("Stopping to serve '%s' on %s", plugin.rm.Resource(), plugin.socket)
    plugin.server.Stop()  // 停止 gRPC server
    if err := os.Remove(plugin.socket); err != nil && !os.IsNotExist(err) {
        return err  // 删除 socket 文件(忽略"不存在"错误)
    }
    plugin.cleanup()  // 清理运行时状态
    return nil
}

停止流程: server.Stop()os.Remove(socket)cleanup()

3.23 Dial 工具方法

go 复制代码
func (plugin *nvidiaDevicePlugin) dial(unixSocketPath string, timeout time.Duration) (*grpc.ClientConn, error) {
    ctx, cancel := context.WithTimeout(plugin.ctx, timeout)
    defer cancel()
    c, err := grpc.DialContext(ctx, unixSocketPath,
        grpc.WithTransportCredentials(insecure.NewCredentials()),  // 无 TLS(Unix socket)
        grpc.WithBlock(),                                            // 阻塞直到连接成功
        grpc.WithContextDialer(func(ctx context.Context, addr string) (net.Conn, error) {
            return (&net.Dialer{}).DialContext(ctx, "unix", addr)   // Unix socket 拨号器
        }),
    )
    // ...
    return c, nil
}

用于两个场景:(1) Serve() 中验证 server 就绪;(2) Register() 中连接 kubelet。


四、完整执行流程总览

Allocate() ListAndWatch() CheckHealth() Register() Serve() Kubelet nvidiaDevicePlugin devicePluginForResource() getResourceManagers() plugin.New() GetPlugins() startPlugins() start() Allocate() ListAndWatch() CheckHealth() Register() Serve() Kubelet nvidiaDevicePlugin devicePluginForResource() getResourceManagers() plugin.New() GetPlugins() startPlugins() start() #mermaid-svg-yOXP33SNEDj0EG71{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-yOXP33SNEDj0EG71 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-yOXP33SNEDj0EG71 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-yOXP33SNEDj0EG71 .error-icon{fill:#552222;}#mermaid-svg-yOXP33SNEDj0EG71 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-yOXP33SNEDj0EG71 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-yOXP33SNEDj0EG71 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-yOXP33SNEDj0EG71 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-yOXP33SNEDj0EG71 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-yOXP33SNEDj0EG71 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-yOXP33SNEDj0EG71 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-yOXP33SNEDj0EG71 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-yOXP33SNEDj0EG71 .marker.cross{stroke:#333333;}#mermaid-svg-yOXP33SNEDj0EG71 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-yOXP33SNEDj0EG71 p{margin:0;}#mermaid-svg-yOXP33SNEDj0EG71 .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-yOXP33SNEDj0EG71 text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-yOXP33SNEDj0EG71 .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-yOXP33SNEDj0EG71 .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-yOXP33SNEDj0EG71 .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-yOXP33SNEDj0EG71 .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-yOXP33SNEDj0EG71 #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-yOXP33SNEDj0EG71 .sequenceNumber{fill:white;}#mermaid-svg-yOXP33SNEDj0EG71 #sequencenumber{fill:#333;}#mermaid-svg-yOXP33SNEDj0EG71 #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-yOXP33SNEDj0EG71 .messageText{fill:#333;stroke:none;}#mermaid-svg-yOXP33SNEDj0EG71 .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-yOXP33SNEDj0EG71 .labelText,#mermaid-svg-yOXP33SNEDj0EG71 .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-yOXP33SNEDj0EG71 .loopText,#mermaid-svg-yOXP33SNEDj0EG71 .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-yOXP33SNEDj0EG71 .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-yOXP33SNEDj0EG71 .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-yOXP33SNEDj0EG71 .noteText,#mermaid-svg-yOXP33SNEDj0EG71 .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-yOXP33SNEDj0EG71 .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-yOXP33SNEDj0EG71 .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-yOXP33SNEDj0EG71 .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-yOXP33SNEDj0EG71 .actorPopupMenu{position:absolute;}#mermaid-svg-yOXP33SNEDj0EG71 .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-yOXP33SNEDj0EG71 .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-yOXP33SNEDj0EG71 .actor-man circle,#mermaid-svg-yOXP33SNEDj0EG71 line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-yOXP33SNEDj0EG71 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} altnvml strategytegra strategy loop健康监控 altCDI enabled altMPS enabled altEnvVar strategy altVolumeMounts strategy altPassDevic-eSpecs 监听事件循环 altkubelet socket 重建 /SIGHUPSIGINT/SIGTERM main() c.Run(os.Args)watch.Files(kubeletSocketDir)watch.Signals(SIGHUP, SIGINT, ...)startPlugins()loadConfig()validateFlags()rm.AddDefaultResourcesToConfig()GetPlugins()resolveStrategy("auto")cdi.New()plugin.New()getResourceManagers()nvmllib.Init()rm.NewNVMLResourceManagers()rm.NewTegraResourceManagers()devicePluginForResource() per resourcegetMPSOptions()nvidiaDevicePlugin{...}cdiHandler.CreateSpecFile()p.Start(kubeletSocket)initialize()mps.waitForDaemon()Serve()net.Listen("unix", socket)RegisterDevicePluginServer()go server.Serve(sock)dial(socket) 验证就绪Register(kubeletSocket)RegisterRequest成功go rm.CheckHealth()ListAndWatch()Send(完整设备列表)health <- DeviceSend(更新后设备列表)Allocate(AllocateRequest)ValidateRequest()getAllocateResponse()updateResponseForCDI()updateResponseForMPS()updateResponseForDeviceListEnvVar()updateResponseForDeviceMounts()apiDeviceSpecs()AllocateResponsestopPlugins() → goto restartstopPlugins() → goto exit main()


五、Device Plugin API 交互时序

Health Check Kubelet Plugin Health Check Kubelet Plugin #mermaid-svg-jH1LiutSl4iOfdbK{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-jH1LiutSl4iOfdbK .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-jH1LiutSl4iOfdbK .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-jH1LiutSl4iOfdbK .error-icon{fill:#552222;}#mermaid-svg-jH1LiutSl4iOfdbK .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-jH1LiutSl4iOfdbK .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-jH1LiutSl4iOfdbK .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-jH1LiutSl4iOfdbK .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-jH1LiutSl4iOfdbK .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-jH1LiutSl4iOfdbK .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-jH1LiutSl4iOfdbK .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-jH1LiutSl4iOfdbK .marker{fill:#333333;stroke:#333333;}#mermaid-svg-jH1LiutSl4iOfdbK .marker.cross{stroke:#333333;}#mermaid-svg-jH1LiutSl4iOfdbK svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-jH1LiutSl4iOfdbK p{margin:0;}#mermaid-svg-jH1LiutSl4iOfdbK .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-jH1LiutSl4iOfdbK text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-jH1LiutSl4iOfdbK .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-jH1LiutSl4iOfdbK .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-jH1LiutSl4iOfdbK .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-jH1LiutSl4iOfdbK .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-jH1LiutSl4iOfdbK #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-jH1LiutSl4iOfdbK .sequenceNumber{fill:white;}#mermaid-svg-jH1LiutSl4iOfdbK #sequencenumber{fill:#333;}#mermaid-svg-jH1LiutSl4iOfdbK #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-jH1LiutSl4iOfdbK .messageText{fill:#333;stroke:none;}#mermaid-svg-jH1LiutSl4iOfdbK .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-jH1LiutSl4iOfdbK .labelText,#mermaid-svg-jH1LiutSl4iOfdbK .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-jH1LiutSl4iOfdbK .loopText,#mermaid-svg-jH1LiutSl4iOfdbK .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-jH1LiutSl4iOfdbK .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-jH1LiutSl4iOfdbK .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-jH1LiutSl4iOfdbK .noteText,#mermaid-svg-jH1LiutSl4iOfdbK .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-jH1LiutSl4iOfdbK .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-jH1LiutSl4iOfdbK .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-jH1LiutSl4iOfdbK .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-jH1LiutSl4iOfdbK .actorPopupMenu{position:absolute;}#mermaid-svg-jH1LiutSl4iOfdbK .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-jH1LiutSl4iOfdbK .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-jH1LiutSl4iOfdbK .actor-man circle,#mermaid-svg-jH1LiutSl4iOfdbK line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-jH1LiutSl4iOfdbK :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} loop运行期间 Register(Version, Endpoint, ResourceName)ListAndWatch()ListAndWatchResponse(Devices)GetPreferredAllocation()PreferredAllocationResponseAllocate(ContainerRequests)AllocateResponse(Envs, Mounts, Devices, Annotations, CdiDevices)PreStartContainer(Request)PreStartContainerResponse(空)health <- Device(unhealthy)ListAndWatchResponse(更新设备列表)server.Stop() → os.Remove(socket) → cleanup()


六、Allocate 响应策略矩阵

策略组合 Envs Mounts Devices Annotations CdiDevices
envvar ✅ NVIDIA_VISIBLE_DEVICES
volume-mounts ✅ (容器路径根) ✅ (/dev/null → 容器路径)
cdi-annotations ✅ (特性)
cdi-cri ✅ (特性)
envvar + pass-device-specs
cdi-annotations + envvar

七、关键设计模式与架构总结

7.1 设计模式使用

模式 使用位置 说明
Functional Options options.go With* 函数链式配置 plugin.New()
Factory Method factory.go New() 根据策略创建不同类型插件
Strategy Pattern getResourceManagers() nvml/tegra 两种设备发现策略
Observer Pattern ListAndWatch() health channel 推送状态变更
Template Method server.go Start() 定义骨架,Serve/Register/CheckHealth 为步骤
Adapter nvidiaDevicePlugin 适配 K8s Device Plugin gRPC API 与 NVIDIA RM
Graceful Degradation failOnInitError=false NVML 失败时不崩溃,等待重试

7.2 并发模型

#mermaid-svg-yiTIri42RtxBrczc{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-yiTIri42RtxBrczc .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-yiTIri42RtxBrczc .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-yiTIri42RtxBrczc .error-icon{fill:#552222;}#mermaid-svg-yiTIri42RtxBrczc .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-yiTIri42RtxBrczc .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-yiTIri42RtxBrczc .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-yiTIri42RtxBrczc .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-yiTIri42RtxBrczc .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-yiTIri42RtxBrczc .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-yiTIri42RtxBrczc .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-yiTIri42RtxBrczc .marker{fill:#333333;stroke:#333333;}#mermaid-svg-yiTIri42RtxBrczc .marker.cross{stroke:#333333;}#mermaid-svg-yiTIri42RtxBrczc svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-yiTIri42RtxBrczc p{margin:0;}#mermaid-svg-yiTIri42RtxBrczc .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-yiTIri42RtxBrczc .cluster-label text{fill:#333;}#mermaid-svg-yiTIri42RtxBrczc .cluster-label span{color:#333;}#mermaid-svg-yiTIri42RtxBrczc .cluster-label span p{background-color:transparent;}#mermaid-svg-yiTIri42RtxBrczc .label text,#mermaid-svg-yiTIri42RtxBrczc span{fill:#333;color:#333;}#mermaid-svg-yiTIri42RtxBrczc .node rect,#mermaid-svg-yiTIri42RtxBrczc .node circle,#mermaid-svg-yiTIri42RtxBrczc .node ellipse,#mermaid-svg-yiTIri42RtxBrczc .node polygon,#mermaid-svg-yiTIri42RtxBrczc .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-yiTIri42RtxBrczc .rough-node .label text,#mermaid-svg-yiTIri42RtxBrczc .node .label text,#mermaid-svg-yiTIri42RtxBrczc .image-shape .label,#mermaid-svg-yiTIri42RtxBrczc .icon-shape .label{text-anchor:middle;}#mermaid-svg-yiTIri42RtxBrczc .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-yiTIri42RtxBrczc .rough-node .label,#mermaid-svg-yiTIri42RtxBrczc .node .label,#mermaid-svg-yiTIri42RtxBrczc .image-shape .label,#mermaid-svg-yiTIri42RtxBrczc .icon-shape .label{text-align:center;}#mermaid-svg-yiTIri42RtxBrczc .node.clickable{cursor:pointer;}#mermaid-svg-yiTIri42RtxBrczc .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-yiTIri42RtxBrczc .arrowheadPath{fill:#333333;}#mermaid-svg-yiTIri42RtxBrczc .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-yiTIri42RtxBrczc .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-yiTIri42RtxBrczc .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-yiTIri42RtxBrczc .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-yiTIri42RtxBrczc .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-yiTIri42RtxBrczc .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-yiTIri42RtxBrczc .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-yiTIri42RtxBrczc .cluster text{fill:#333;}#mermaid-svg-yiTIri42RtxBrczc .cluster span{color:#333;}#mermaid-svg-yiTIri42RtxBrczc div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-yiTIri42RtxBrczc .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-yiTIri42RtxBrczc rect.text{fill:none;stroke-width:0;}#mermaid-svg-yiTIri42RtxBrczc .icon-shape,#mermaid-svg-yiTIri42RtxBrczc .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-yiTIri42RtxBrczc .icon-shape p,#mermaid-svg-yiTIri42RtxBrczc .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-yiTIri42RtxBrczc .icon-shape .label rect,#mermaid-svg-yiTIri42RtxBrczc .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-yiTIri42RtxBrczc .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-yiTIri42RtxBrczc .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-yiTIri42RtxBrczc :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 启动
启动
gRPC 请求
health channel
stop channel
server.Stop()
主 goroutine: start() 事件循环
Serve() goroutine: gRPC server.Serve(sock)
CheckHealth() goroutine: rm.CheckHealth(stop, health)
gRPC handlers: ListAndWatch / Allocate / GetPreferredAllocation

7.3 生命周期状态图

#mermaid-svg-HU2ESXf6mCpPC8sR{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-HU2ESXf6mCpPC8sR .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-HU2ESXf6mCpPC8sR .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-HU2ESXf6mCpPC8sR .error-icon{fill:#552222;}#mermaid-svg-HU2ESXf6mCpPC8sR .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-HU2ESXf6mCpPC8sR .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-HU2ESXf6mCpPC8sR .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-HU2ESXf6mCpPC8sR .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-HU2ESXf6mCpPC8sR .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-HU2ESXf6mCpPC8sR .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-HU2ESXf6mCpPC8sR .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-HU2ESXf6mCpPC8sR .marker{fill:#333333;stroke:#333333;}#mermaid-svg-HU2ESXf6mCpPC8sR .marker.cross{stroke:#333333;}#mermaid-svg-HU2ESXf6mCpPC8sR svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-HU2ESXf6mCpPC8sR p{margin:0;}#mermaid-svg-HU2ESXf6mCpPC8sR defs #statediagram-barbEnd{fill:#333333;stroke:#333333;}#mermaid-svg-HU2ESXf6mCpPC8sR g.stateGroup text{fill:#9370DB;stroke:none;font-size:10px;}#mermaid-svg-HU2ESXf6mCpPC8sR g.stateGroup text{fill:#333;stroke:none;font-size:10px;}#mermaid-svg-HU2ESXf6mCpPC8sR g.stateGroup .state-title{font-weight:bolder;fill:#131300;}#mermaid-svg-HU2ESXf6mCpPC8sR g.stateGroup rect{fill:#ECECFF;stroke:#9370DB;}#mermaid-svg-HU2ESXf6mCpPC8sR g.stateGroup line{stroke:#333333;stroke-width:1;}#mermaid-svg-HU2ESXf6mCpPC8sR .transition{stroke:#333333;stroke-width:1;fill:none;}#mermaid-svg-HU2ESXf6mCpPC8sR .stateGroup .composit{fill:white;border-bottom:1px;}#mermaid-svg-HU2ESXf6mCpPC8sR .stateGroup .alt-composit{fill:#e0e0e0;border-bottom:1px;}#mermaid-svg-HU2ESXf6mCpPC8sR .state-note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-HU2ESXf6mCpPC8sR .state-note text{fill:black;stroke:none;font-size:10px;}#mermaid-svg-HU2ESXf6mCpPC8sR .stateLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.5;}#mermaid-svg-HU2ESXf6mCpPC8sR .edgeLabel .label rect{fill:#ECECFF;opacity:0.5;}#mermaid-svg-HU2ESXf6mCpPC8sR .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-HU2ESXf6mCpPC8sR .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-HU2ESXf6mCpPC8sR .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-HU2ESXf6mCpPC8sR .edgeLabel .label text{fill:#333;}#mermaid-svg-HU2ESXf6mCpPC8sR .label div .edgeLabel{color:#333;}#mermaid-svg-HU2ESXf6mCpPC8sR .stateLabel text{fill:#131300;font-size:10px;font-weight:bold;}#mermaid-svg-HU2ESXf6mCpPC8sR .node circle.state-start{fill:#333333;stroke:#333333;}#mermaid-svg-HU2ESXf6mCpPC8sR .node .fork-join{fill:#333333;stroke:#333333;}#mermaid-svg-HU2ESXf6mCpPC8sR .node circle.state-end{fill:#9370DB;stroke:white;stroke-width:1.5;}#mermaid-svg-HU2ESXf6mCpPC8sR .end-state-inner{fill:white;stroke-width:1.5;}#mermaid-svg-HU2ESXf6mCpPC8sR .node rect{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-HU2ESXf6mCpPC8sR .node polygon{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-HU2ESXf6mCpPC8sR #statediagram-barbEnd{fill:#333333;}#mermaid-svg-HU2ESXf6mCpPC8sR .statediagram-cluster rect{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-HU2ESXf6mCpPC8sR .cluster-label,#mermaid-svg-HU2ESXf6mCpPC8sR .nodeLabel{color:#131300;}#mermaid-svg-HU2ESXf6mCpPC8sR .statediagram-cluster rect.outer{rx:5px;ry:5px;}#mermaid-svg-HU2ESXf6mCpPC8sR .statediagram-state .divider{stroke:#9370DB;}#mermaid-svg-HU2ESXf6mCpPC8sR .statediagram-state .title-state{rx:5px;ry:5px;}#mermaid-svg-HU2ESXf6mCpPC8sR .statediagram-cluster.statediagram-cluster .inner{fill:white;}#mermaid-svg-HU2ESXf6mCpPC8sR .statediagram-cluster.statediagram-cluster-alt .inner{fill:#f0f0f0;}#mermaid-svg-HU2ESXf6mCpPC8sR .statediagram-cluster .inner{rx:0;ry:0;}#mermaid-svg-HU2ESXf6mCpPC8sR .statediagram-state rect.basic{rx:5px;ry:5px;}#mermaid-svg-HU2ESXf6mCpPC8sR .statediagram-state rect.divider{stroke-dasharray:10,10;fill:#f0f0f0;}#mermaid-svg-HU2ESXf6mCpPC8sR .note-edge{stroke-dasharray:5;}#mermaid-svg-HU2ESXf6mCpPC8sR .statediagram-note rect{fill:#fff5ad;stroke:#aaaa33;stroke-width:1px;rx:0;ry:0;}#mermaid-svg-HU2ESXf6mCpPC8sR .statediagram-note rect{fill:#fff5ad;stroke:#aaaa33;stroke-width:1px;rx:0;ry:0;}#mermaid-svg-HU2ESXf6mCpPC8sR .statediagram-note text{fill:black;}#mermaid-svg-HU2ESXf6mCpPC8sR .statediagram-note .nodeLabel{color:black;}#mermaid-svg-HU2ESXf6mCpPC8sR .statediagram .edgeLabel{color:red;}#mermaid-svg-HU2ESXf6mCpPC8sR #dependencyStart,#mermaid-svg-HU2ESXf6mCpPC8sR #dependencyEnd{fill:#333333;stroke:#333333;stroke-width:1;}#mermaid-svg-HU2ESXf6mCpPC8sR .statediagramTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-HU2ESXf6mCpPC8sR :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} plugin.New()
kubelet restart / SIGHUP
Start()
Stop() → cleanup()
Start() (restart)
SIGTERM → stopPlugins()
Created
Serving
initialize()
Serve()
Register()
go CheckHealth()
Initialized
ServingGRPC
Registered
Monitoring

7.4 核心依赖关系

#mermaid-svg-Vexqt3kn5YhF9PAQ{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-Vexqt3kn5YhF9PAQ .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-Vexqt3kn5YhF9PAQ .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-Vexqt3kn5YhF9PAQ .error-icon{fill:#552222;}#mermaid-svg-Vexqt3kn5YhF9PAQ .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-Vexqt3kn5YhF9PAQ .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-Vexqt3kn5YhF9PAQ .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-Vexqt3kn5YhF9PAQ .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-Vexqt3kn5YhF9PAQ .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-Vexqt3kn5YhF9PAQ .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-Vexqt3kn5YhF9PAQ .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-Vexqt3kn5YhF9PAQ .marker{fill:#333333;stroke:#333333;}#mermaid-svg-Vexqt3kn5YhF9PAQ .marker.cross{stroke:#333333;}#mermaid-svg-Vexqt3kn5YhF9PAQ svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-Vexqt3kn5YhF9PAQ p{margin:0;}#mermaid-svg-Vexqt3kn5YhF9PAQ .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-Vexqt3kn5YhF9PAQ .cluster-label text{fill:#333;}#mermaid-svg-Vexqt3kn5YhF9PAQ .cluster-label span{color:#333;}#mermaid-svg-Vexqt3kn5YhF9PAQ .cluster-label span p{background-color:transparent;}#mermaid-svg-Vexqt3kn5YhF9PAQ .label text,#mermaid-svg-Vexqt3kn5YhF9PAQ span{fill:#333;color:#333;}#mermaid-svg-Vexqt3kn5YhF9PAQ .node rect,#mermaid-svg-Vexqt3kn5YhF9PAQ .node circle,#mermaid-svg-Vexqt3kn5YhF9PAQ .node ellipse,#mermaid-svg-Vexqt3kn5YhF9PAQ .node polygon,#mermaid-svg-Vexqt3kn5YhF9PAQ .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-Vexqt3kn5YhF9PAQ .rough-node .label text,#mermaid-svg-Vexqt3kn5YhF9PAQ .node .label text,#mermaid-svg-Vexqt3kn5YhF9PAQ .image-shape .label,#mermaid-svg-Vexqt3kn5YhF9PAQ .icon-shape .label{text-anchor:middle;}#mermaid-svg-Vexqt3kn5YhF9PAQ .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-Vexqt3kn5YhF9PAQ .rough-node .label,#mermaid-svg-Vexqt3kn5YhF9PAQ .node .label,#mermaid-svg-Vexqt3kn5YhF9PAQ .image-shape .label,#mermaid-svg-Vexqt3kn5YhF9PAQ .icon-shape .label{text-align:center;}#mermaid-svg-Vexqt3kn5YhF9PAQ .node.clickable{cursor:pointer;}#mermaid-svg-Vexqt3kn5YhF9PAQ .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-Vexqt3kn5YhF9PAQ .arrowheadPath{fill:#333333;}#mermaid-svg-Vexqt3kn5YhF9PAQ .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-Vexqt3kn5YhF9PAQ .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-Vexqt3kn5YhF9PAQ .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Vexqt3kn5YhF9PAQ .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-Vexqt3kn5YhF9PAQ .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Vexqt3kn5YhF9PAQ .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-Vexqt3kn5YhF9PAQ .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-Vexqt3kn5YhF9PAQ .cluster text{fill:#333;}#mermaid-svg-Vexqt3kn5YhF9PAQ .cluster span{color:#333;}#mermaid-svg-Vexqt3kn5YhF9PAQ div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-Vexqt3kn5YhF9PAQ .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-Vexqt3kn5YhF9PAQ rect.text{fill:none;stroke-width:0;}#mermaid-svg-Vexqt3kn5YhF9PAQ .icon-shape,#mermaid-svg-Vexqt3kn5YhF9PAQ .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Vexqt3kn5YhF9PAQ .icon-shape p,#mermaid-svg-Vexqt3kn5YhF9PAQ .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-Vexqt3kn5YhF9PAQ .icon-shape .label rect,#mermaid-svg-Vexqt3kn5YhF9PAQ .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Vexqt3kn5YhF9PAQ .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-Vexqt3kn5YhF9PAQ .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-Vexqt3kn5YhF9PAQ :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} main.go
plugin-manager.go
root.go
internal/watch
internal/plugin/factory.go
internal/rm
go-nvml
internal/cdi
internal/imex
server.go
grpc
google/uuid
mps.go
cmd/mps-control-daemon/mps
options.go


八、代码质量备注

8.1 已知技术债务

  1. ListAndWatch FIXMEUnhealthy 状态不可恢复
  2. apiDeviceSpecs TODO :IMEX 通道 HostPath 语义可能不正确
  3. grpc.DialContext deprecation :建议迁移到 grpc.NewClient
  4. MPS 健康检查 TODO:仅 Start 时检查一次

8.2 健壮性设计

  • gRPC 崩溃重启:5 次/1 小时退避
  • kubelet 重启检测:inotify 监听 socket 创建
  • 防御性 nil 检查:Stop() 检查 server == nil
  • 优雅降级:failOnInitError=false 支持非 GPU 节点
  • 错误聚合:errors.Join 聚合多插件停止错误

8.3 安全考量

  • gRPC 使用 insecure.NewCredentials()(仅 Unix socket)
  • 不暴露网络端口,全 Unix domain socket 通信
  • 设备文件权限固定 rw,无特权提升

文档版本 : v1.0 | 分析对象 : k8s-device-plugin v0.19.3 核心插件模块 | 日期: 2026-07-26

相关推荐
Linux-187412 小时前
分布式链路追踪系统之skywalking java agent 的使用、skywalking web界面简介
云原生·skywalking·分布式链路追踪系统·应用程序性能监控·halo博客系统·skywalking java agent·skywalking web 简介
9523614 小时前
Docker - 基础
运维·后端·docker·容器
Code_Ignis14 小时前
docker国内可用源(持续维护)
运维·docker·容器
童谣114 小时前
越华环保集团市级统筹申报数字化中台:政策校验与减排仿真一体化架构
数据库·架构
2601_9567436816 小时前
上海小程序开发公司技术路径全解:从架构选型到工程落地的核心判断
小程序·架构·开发经验·上海
JAVA面经实录91717 小时前
图解23种设计模式完整知识体系(Java后端面试完整版)
java·架构
AI_小站17 小时前
Loop Engineering又是啥?一文讲清企业Agent落地的四层工程进化论
java·人工智能·架构·prompt·大模型开发·智能体·大模型应用
l1t17 小时前
DeepSeek总结的DuckLake 架构深度剖析-2
数据库·架构
2601_9625029017 小时前
双工位蜡镶机控制系统工程实践:并行架构、视觉对齐与实时同步避坑指南
架构
论迹复利18 小时前
多平台移植 BTBLE Stack 的思路与踩坑实战——从 Ceva IP 到厂商适配层的迁移指南
嵌入式硬件·物联网·架构