NVIDIA k8s-device-plugin v0.19.3 标签管理模块 (internal/lm) 深度分析
一、模块定位
1.1 业务职责
internal/lm(Label Manager)模块是 NVIDIA k8s-device-plugin 的标签生成与管理核心模块。其核心职责是:
- 采集节点 GPU 硬件信息:驱动版本、CUDA 版本、GPU 型号、显存、架构族、MIG 能力、GPU 模式(Graphics/Compute)、vGPU 信息、IMEX Fabric 拓扑等
- 生成 Kubernetes Node Labels :将采集的硬件信息转换为
nvidia.com/*前缀的节点标签 - 处理 MIG 策略 :根据
mig-strategy(none/single/mixed)生成不同的 MIG 相关标签 - 处理共享策略 :根据 Time-Slicing / MPS 共享配置生成
replicas、sharing-strategy等标签 - 输出标签:支持文件输出(GFD 模式)和 NodeFeature CR 输出(NFD API 模式)两种方式
1.2 系统位置
该模块是 GPU Feature Discovery (GFD) 的核心实现。在系统中:
┌─────────────────────────────────────────────────────────┐
│ GFD 主程序 (cmd/gpu-feature-discovery) │
│ │ │
│ ┌───────────────┼───────────────┐ │
│ ▼ ▼ ▼ │
│ resource.Manager vgpu.Interface spec.Config │
│ │ │ │ │
│ └───────┬───────┴───────┬───────┘ │
│ ▼ ▼ │
│ ┌─────────────────────────────┐ │
│ │ internal/lm 模块 │ │
│ │ (Label Manager) │ │
│ │ │ │
│ │ Labeler接口 + 策略模式 │ │
│ │ 生成 Labels (map[str]str) │ │
│ └──────────┬──────────────────┘ │
│ ▼ │
│ ┌─────────────────────────────┐ │
│ │ Outputer 接口 │ │
│ │ toFile / toWriter / │ │
│ │ nodeFeatureObject │ │
│ └──────────┬──────────────────┘ │
│ ▼ │
│ 文件输出 或 NodeFeature CR │
│ (NFD Worker 读取并打标签到 Node) │
└─────────────────────────────────────────────────────────┘
#mermaid-svg-291N234dUoAx87w6{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-291N234dUoAx87w6 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-291N234dUoAx87w6 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-291N234dUoAx87w6 .error-icon{fill:#552222;}#mermaid-svg-291N234dUoAx87w6 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-291N234dUoAx87w6 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-291N234dUoAx87w6 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-291N234dUoAx87w6 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-291N234dUoAx87w6 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-291N234dUoAx87w6 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-291N234dUoAx87w6 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-291N234dUoAx87w6 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-291N234dUoAx87w6 .marker.cross{stroke:#333333;}#mermaid-svg-291N234dUoAx87w6 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-291N234dUoAx87w6 p{margin:0;}#mermaid-svg-291N234dUoAx87w6 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-291N234dUoAx87w6 .cluster-label text{fill:#333;}#mermaid-svg-291N234dUoAx87w6 .cluster-label span{color:#333;}#mermaid-svg-291N234dUoAx87w6 .cluster-label span p{background-color:transparent;}#mermaid-svg-291N234dUoAx87w6 .label text,#mermaid-svg-291N234dUoAx87w6 span{fill:#333;color:#333;}#mermaid-svg-291N234dUoAx87w6 .node rect,#mermaid-svg-291N234dUoAx87w6 .node circle,#mermaid-svg-291N234dUoAx87w6 .node ellipse,#mermaid-svg-291N234dUoAx87w6 .node polygon,#mermaid-svg-291N234dUoAx87w6 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-291N234dUoAx87w6 .rough-node .label text,#mermaid-svg-291N234dUoAx87w6 .node .label text,#mermaid-svg-291N234dUoAx87w6 .image-shape .label,#mermaid-svg-291N234dUoAx87w6 .icon-shape .label{text-anchor:middle;}#mermaid-svg-291N234dUoAx87w6 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-291N234dUoAx87w6 .rough-node .label,#mermaid-svg-291N234dUoAx87w6 .node .label,#mermaid-svg-291N234dUoAx87w6 .image-shape .label,#mermaid-svg-291N234dUoAx87w6 .icon-shape .label{text-align:center;}#mermaid-svg-291N234dUoAx87w6 .node.clickable{cursor:pointer;}#mermaid-svg-291N234dUoAx87w6 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-291N234dUoAx87w6 .arrowheadPath{fill:#333333;}#mermaid-svg-291N234dUoAx87w6 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-291N234dUoAx87w6 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-291N234dUoAx87w6 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-291N234dUoAx87w6 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-291N234dUoAx87w6 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-291N234dUoAx87w6 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-291N234dUoAx87w6 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-291N234dUoAx87w6 .cluster text{fill:#333;}#mermaid-svg-291N234dUoAx87w6 .cluster span{color:#333;}#mermaid-svg-291N234dUoAx87w6 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-291N234dUoAx87w6 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-291N234dUoAx87w6 rect.text{fill:none;stroke-width:0;}#mermaid-svg-291N234dUoAx87w6 .icon-shape,#mermaid-svg-291N234dUoAx87w6 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-291N234dUoAx87w6 .icon-shape p,#mermaid-svg-291N234dUoAx87w6 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-291N234dUoAx87w6 .icon-shape .label rect,#mermaid-svg-291N234dUoAx87w6 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-291N234dUoAx87w6 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-291N234dUoAx87w6 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-291N234dUoAx87w6 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} GFD 主程序
resource.Manager
vgpu.Interface
spec.Config
lm.NewLabelers
Labeler.Labels
Labels map
Outputer.Output
文件 / NodeFeature CR
K8s Node Labels
1.3 与 GPU Feature Discovery 的关系
GFD(GPU Feature Discovery)是 NVIDIA 的节点 GPU 特性发现工具,其历史演进:
- 早期 :独立运行
gpu-feature-discovery二进制,输出标签到文件,由 NFD Worker 读取 - 当前 :
internal/lm模块将标签生成逻辑内聚,同时支持传统文件输出和新一代 NodeFeature CR API
lm 模块本质上是 GFD 的标签生成引擎,解耦了硬件信息采集(resource.Manager)与标签输出(Outputer)。
二、模块整体结构
2.1 文件清单与职责
| 文件 | 核心内容 | 行数(约) |
|---|---|---|
labeler.go |
Labeler 接口定义 + NewLabelers 工厂入口 |
~50 |
labels.go |
Labels 类型定义(map[string]string) |
~25 |
list.go |
list 复合 Labeler + Merge 函数(组合模式) |
~40 |
nvml.go |
设备标签器工厂 + 版本/MIG能力/共享/GPU模式标签器 | ~190 |
mig-strategy.go |
MIG 策略标签器(none/single/mixed) | ~170 |
resource.go |
resourceLabeler 结构体 + GPU/MIG 资源标签生成 |
~200 |
output.go |
Outputer 接口 + 文件/NodeFeature CR 输出 |
~180 |
strategy.go |
MIG 策略标签辅助 labeler | ~20 |
machine-type.go |
机器类型标签(读取 sysfs) | ~40 |
timestamp.go |
GFD 时间戳标签 | ~25 |
vgpu.go |
vGPU 标签器 | ~45 |
imex.go |
IMEX Fabric 拓扑标签器 | ~75 |
empty.go |
空 Labeler(无设备时的 noop) | ~10 |
2.2 核心接口与类型关系
#mermaid-svg-reRIAydPKwKaYwUt{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-reRIAydPKwKaYwUt .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-reRIAydPKwKaYwUt .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-reRIAydPKwKaYwUt .error-icon{fill:#552222;}#mermaid-svg-reRIAydPKwKaYwUt .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-reRIAydPKwKaYwUt .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-reRIAydPKwKaYwUt .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-reRIAydPKwKaYwUt .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-reRIAydPKwKaYwUt .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-reRIAydPKwKaYwUt .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-reRIAydPKwKaYwUt .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-reRIAydPKwKaYwUt .marker{fill:#333333;stroke:#333333;}#mermaid-svg-reRIAydPKwKaYwUt .marker.cross{stroke:#333333;}#mermaid-svg-reRIAydPKwKaYwUt svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-reRIAydPKwKaYwUt p{margin:0;}#mermaid-svg-reRIAydPKwKaYwUt g.classGroup text{fill:#9370DB;stroke:none;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:10px;}#mermaid-svg-reRIAydPKwKaYwUt g.classGroup text .title{font-weight:bolder;}#mermaid-svg-reRIAydPKwKaYwUt .cluster-label text{fill:#333;}#mermaid-svg-reRIAydPKwKaYwUt .cluster-label span{color:#333;}#mermaid-svg-reRIAydPKwKaYwUt .cluster-label span p{background-color:transparent;}#mermaid-svg-reRIAydPKwKaYwUt .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-reRIAydPKwKaYwUt .cluster text{fill:#333;}#mermaid-svg-reRIAydPKwKaYwUt .cluster span{color:#333;}#mermaid-svg-reRIAydPKwKaYwUt .nodeLabel,#mermaid-svg-reRIAydPKwKaYwUt .edgeLabel{color:#131300;}#mermaid-svg-reRIAydPKwKaYwUt .edgeLabel .label rect{fill:#ECECFF;}#mermaid-svg-reRIAydPKwKaYwUt .label text{fill:#131300;}#mermaid-svg-reRIAydPKwKaYwUt .labelBkg{background:#ECECFF;}#mermaid-svg-reRIAydPKwKaYwUt .edgeLabel .label span{background:#ECECFF;}#mermaid-svg-reRIAydPKwKaYwUt .classTitle{font-weight:bolder;}#mermaid-svg-reRIAydPKwKaYwUt .node rect,#mermaid-svg-reRIAydPKwKaYwUt .node circle,#mermaid-svg-reRIAydPKwKaYwUt .node ellipse,#mermaid-svg-reRIAydPKwKaYwUt .node polygon,#mermaid-svg-reRIAydPKwKaYwUt .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-reRIAydPKwKaYwUt .divider{stroke:#9370DB;stroke-width:1;}#mermaid-svg-reRIAydPKwKaYwUt g.clickable{cursor:pointer;}#mermaid-svg-reRIAydPKwKaYwUt g.classGroup rect{fill:#ECECFF;stroke:#9370DB;}#mermaid-svg-reRIAydPKwKaYwUt g.classGroup line{stroke:#9370DB;stroke-width:1;}#mermaid-svg-reRIAydPKwKaYwUt .classLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.5;}#mermaid-svg-reRIAydPKwKaYwUt .classLabel .label{fill:#9370DB;font-size:10px;}#mermaid-svg-reRIAydPKwKaYwUt .relation{stroke:#333333;stroke-width:1;fill:none;}#mermaid-svg-reRIAydPKwKaYwUt .dashed-line{stroke-dasharray:3;}#mermaid-svg-reRIAydPKwKaYwUt .dotted-line{stroke-dasharray:1 2;}#mermaid-svg-reRIAydPKwKaYwUt #compositionStart,#mermaid-svg-reRIAydPKwKaYwUt .composition{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-reRIAydPKwKaYwUt #compositionEnd,#mermaid-svg-reRIAydPKwKaYwUt .composition{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-reRIAydPKwKaYwUt #dependencyStart,#mermaid-svg-reRIAydPKwKaYwUt .dependency{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-reRIAydPKwKaYwUt #dependencyStart,#mermaid-svg-reRIAydPKwKaYwUt .dependency{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-reRIAydPKwKaYwUt #extensionStart,#mermaid-svg-reRIAydPKwKaYwUt .extension{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-reRIAydPKwKaYwUt #extensionEnd,#mermaid-svg-reRIAydPKwKaYwUt .extension{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-reRIAydPKwKaYwUt #aggregationStart,#mermaid-svg-reRIAydPKwKaYwUt .aggregation{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-reRIAydPKwKaYwUt #aggregationEnd,#mermaid-svg-reRIAydPKwKaYwUt .aggregation{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-reRIAydPKwKaYwUt #lollipopStart,#mermaid-svg-reRIAydPKwKaYwUt .lollipop{fill:#ECECFF!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-reRIAydPKwKaYwUt #lollipopEnd,#mermaid-svg-reRIAydPKwKaYwUt .lollipop{fill:#ECECFF!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-reRIAydPKwKaYwUt .edgeTerminals{font-size:11px;line-height:initial;}#mermaid-svg-reRIAydPKwKaYwUt .classTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-reRIAydPKwKaYwUt .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-reRIAydPKwKaYwUt .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-reRIAydPKwKaYwUt :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} returns
returns self
<<interface>>
Labeler
+Labels()(Labels, error)
Labels
mapstringstring
+Labels()(Labels, error)
list
\[\]Labeler
+Labels()(Labels, error)
empty
+Labels()(Labels, error)
vgpuLabeler
-lib vgpu.Interface
+Labels()(Labels, error)
resourceLabeler
-resourceName spec.ResourceName
-sharing *spec.Sharing
+single(suffix, value) : Labels
+labels(suffixValues) : Labels
+updateLabel(labels, suffix, value)
+key(suffix) : string
+baseLabeler(count, parts) : Labeler
+productLabel(parts) : Labels
nodeFeatureObject
-nodeConfig flags.NodeConfig
-nfdClientset nfdclientset.Interface
-ownerRefs \[\]metav1.OwnerReference
+Output(Labels) : error
toFile
string
+Output(Labels) : error
toWriter
io.Writer
+Output(Labels) : error
<<interface>>
Outputer
+Output(Labels) : error
2.3 核心方法清单
| 方法 | 所属 | 作用 |
|---|---|---|
NewLabelers |
labeler.go |
顶层入口:构造完整 Labeler 树 |
NewDeviceLabeler |
nvml.go |
构造设备相关标签器集合 |
newVersionLabeler |
nvml.go |
生成 CUDA/Driver 版本标签 |
newMigCapabilityLabeler |
nvml.go |
生成 MIG 能力标签 |
newSharingLabeler |
nvml.go |
生成 MPS 共享能力标签 |
newGPUModeLabeler |
nvml.go |
生成 GPU 模式(graphics/compute)标签 |
NewResourceLabeler |
mig-strategy.go |
构造资源标签器(含 MIG 策略分发) |
newMigLabeler |
mig-strategy.go |
MIG 策略标签器分发 |
newMigStrategySingleLabeler |
mig-strategy.go |
single 策略标签生成 |
newMigStrategyMixedLabeler |
mig-strategy.go |
mixed 策略标签生成 |
NewGPUResourceLabeler |
resource.go |
全 GPU 资源标签生成 |
NewMIGResourceLabeler |
resource.go |
MIG 设备资源标签生成 |
newImexLabeler |
imex.go |
IMEX Fabric 拓扑标签生成 |
NewVGPULabeler |
vgpu.go |
vGPU 标签生成 |
newMachineTypeLabeler |
machine-type.go |
机器类型标签 |
NewTimestampLabeler |
timestamp.go |
时间戳标签 |
Merge |
list.go |
合并多个 Labeler |
NewOutputer |
output.go |
构造输出器 |
2.4 内部调用关系
#mermaid-svg-GVLj9O0Qo2xN7HO6{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-GVLj9O0Qo2xN7HO6 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-GVLj9O0Qo2xN7HO6 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-GVLj9O0Qo2xN7HO6 .error-icon{fill:#552222;}#mermaid-svg-GVLj9O0Qo2xN7HO6 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-GVLj9O0Qo2xN7HO6 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-GVLj9O0Qo2xN7HO6 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-GVLj9O0Qo2xN7HO6 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-GVLj9O0Qo2xN7HO6 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-GVLj9O0Qo2xN7HO6 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-GVLj9O0Qo2xN7HO6 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-GVLj9O0Qo2xN7HO6 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-GVLj9O0Qo2xN7HO6 .marker.cross{stroke:#333333;}#mermaid-svg-GVLj9O0Qo2xN7HO6 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-GVLj9O0Qo2xN7HO6 p{margin:0;}#mermaid-svg-GVLj9O0Qo2xN7HO6 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-GVLj9O0Qo2xN7HO6 .cluster-label text{fill:#333;}#mermaid-svg-GVLj9O0Qo2xN7HO6 .cluster-label span{color:#333;}#mermaid-svg-GVLj9O0Qo2xN7HO6 .cluster-label span p{background-color:transparent;}#mermaid-svg-GVLj9O0Qo2xN7HO6 .label text,#mermaid-svg-GVLj9O0Qo2xN7HO6 span{fill:#333;color:#333;}#mermaid-svg-GVLj9O0Qo2xN7HO6 .node rect,#mermaid-svg-GVLj9O0Qo2xN7HO6 .node circle,#mermaid-svg-GVLj9O0Qo2xN7HO6 .node ellipse,#mermaid-svg-GVLj9O0Qo2xN7HO6 .node polygon,#mermaid-svg-GVLj9O0Qo2xN7HO6 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-GVLj9O0Qo2xN7HO6 .rough-node .label text,#mermaid-svg-GVLj9O0Qo2xN7HO6 .node .label text,#mermaid-svg-GVLj9O0Qo2xN7HO6 .image-shape .label,#mermaid-svg-GVLj9O0Qo2xN7HO6 .icon-shape .label{text-anchor:middle;}#mermaid-svg-GVLj9O0Qo2xN7HO6 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-GVLj9O0Qo2xN7HO6 .rough-node .label,#mermaid-svg-GVLj9O0Qo2xN7HO6 .node .label,#mermaid-svg-GVLj9O0Qo2xN7HO6 .image-shape .label,#mermaid-svg-GVLj9O0Qo2xN7HO6 .icon-shape .label{text-align:center;}#mermaid-svg-GVLj9O0Qo2xN7HO6 .node.clickable{cursor:pointer;}#mermaid-svg-GVLj9O0Qo2xN7HO6 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-GVLj9O0Qo2xN7HO6 .arrowheadPath{fill:#333333;}#mermaid-svg-GVLj9O0Qo2xN7HO6 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-GVLj9O0Qo2xN7HO6 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-GVLj9O0Qo2xN7HO6 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-GVLj9O0Qo2xN7HO6 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-GVLj9O0Qo2xN7HO6 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-GVLj9O0Qo2xN7HO6 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-GVLj9O0Qo2xN7HO6 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-GVLj9O0Qo2xN7HO6 .cluster text{fill:#333;}#mermaid-svg-GVLj9O0Qo2xN7HO6 .cluster span{color:#333;}#mermaid-svg-GVLj9O0Qo2xN7HO6 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-GVLj9O0Qo2xN7HO6 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-GVLj9O0Qo2xN7HO6 rect.text{fill:none;stroke-width:0;}#mermaid-svg-GVLj9O0Qo2xN7HO6 .icon-shape,#mermaid-svg-GVLj9O0Qo2xN7HO6 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-GVLj9O0Qo2xN7HO6 .icon-shape p,#mermaid-svg-GVLj9O0Qo2xN7HO6 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-GVLj9O0Qo2xN7HO6 .icon-shape .label rect,#mermaid-svg-GVLj9O0Qo2xN7HO6 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-GVLj9O0Qo2xN7HO6 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-GVLj9O0Qo2xN7HO6 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-GVLj9O0Qo2xN7HO6 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 辅助
资源标签生成层
MIG策略层
资源标签层
设备标签层
入口层
no devices
no devices
输出层
NewOutputer
ToFile
toWriter
nodeFeatureObject
NewLabelers
NewDeviceLabeler
newVersionLabeler
newMigCapabilityLabeler
newSharingLabeler
newGPUModeLabeler
newImexLabeler
newMachineTypeLabeler
NewResourceLabeler
newMigLabeler
newGPULabelers
migStrategyLabeler
newMigStrategySingleLabeler
newMigStrategyMixedLabeler
newInvalidMigStrategyLabeler
NewGPUResourceLabeler
NewMIGResourceLabeler
NewGPUResourceLabelerWithoutSharing
NewVGPULabeler
NewTimestampLabeler
empty
2.5 数据流入流出
#mermaid-svg-ohF5Q1a6IqTrxYZ5{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-ohF5Q1a6IqTrxYZ5 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-ohF5Q1a6IqTrxYZ5 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-ohF5Q1a6IqTrxYZ5 .error-icon{fill:#552222;}#mermaid-svg-ohF5Q1a6IqTrxYZ5 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-ohF5Q1a6IqTrxYZ5 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-ohF5Q1a6IqTrxYZ5 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-ohF5Q1a6IqTrxYZ5 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-ohF5Q1a6IqTrxYZ5 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-ohF5Q1a6IqTrxYZ5 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-ohF5Q1a6IqTrxYZ5 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-ohF5Q1a6IqTrxYZ5 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-ohF5Q1a6IqTrxYZ5 .marker.cross{stroke:#333333;}#mermaid-svg-ohF5Q1a6IqTrxYZ5 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-ohF5Q1a6IqTrxYZ5 p{margin:0;}#mermaid-svg-ohF5Q1a6IqTrxYZ5 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-ohF5Q1a6IqTrxYZ5 .cluster-label text{fill:#333;}#mermaid-svg-ohF5Q1a6IqTrxYZ5 .cluster-label span{color:#333;}#mermaid-svg-ohF5Q1a6IqTrxYZ5 .cluster-label span p{background-color:transparent;}#mermaid-svg-ohF5Q1a6IqTrxYZ5 .label text,#mermaid-svg-ohF5Q1a6IqTrxYZ5 span{fill:#333;color:#333;}#mermaid-svg-ohF5Q1a6IqTrxYZ5 .node rect,#mermaid-svg-ohF5Q1a6IqTrxYZ5 .node circle,#mermaid-svg-ohF5Q1a6IqTrxYZ5 .node ellipse,#mermaid-svg-ohF5Q1a6IqTrxYZ5 .node polygon,#mermaid-svg-ohF5Q1a6IqTrxYZ5 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-ohF5Q1a6IqTrxYZ5 .rough-node .label text,#mermaid-svg-ohF5Q1a6IqTrxYZ5 .node .label text,#mermaid-svg-ohF5Q1a6IqTrxYZ5 .image-shape .label,#mermaid-svg-ohF5Q1a6IqTrxYZ5 .icon-shape .label{text-anchor:middle;}#mermaid-svg-ohF5Q1a6IqTrxYZ5 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-ohF5Q1a6IqTrxYZ5 .rough-node .label,#mermaid-svg-ohF5Q1a6IqTrxYZ5 .node .label,#mermaid-svg-ohF5Q1a6IqTrxYZ5 .image-shape .label,#mermaid-svg-ohF5Q1a6IqTrxYZ5 .icon-shape .label{text-align:center;}#mermaid-svg-ohF5Q1a6IqTrxYZ5 .node.clickable{cursor:pointer;}#mermaid-svg-ohF5Q1a6IqTrxYZ5 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-ohF5Q1a6IqTrxYZ5 .arrowheadPath{fill:#333333;}#mermaid-svg-ohF5Q1a6IqTrxYZ5 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-ohF5Q1a6IqTrxYZ5 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-ohF5Q1a6IqTrxYZ5 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ohF5Q1a6IqTrxYZ5 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-ohF5Q1a6IqTrxYZ5 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ohF5Q1a6IqTrxYZ5 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-ohF5Q1a6IqTrxYZ5 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-ohF5Q1a6IqTrxYZ5 .cluster text{fill:#333;}#mermaid-svg-ohF5Q1a6IqTrxYZ5 .cluster span{color:#333;}#mermaid-svg-ohF5Q1a6IqTrxYZ5 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-ohF5Q1a6IqTrxYZ5 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-ohF5Q1a6IqTrxYZ5 rect.text{fill:none;stroke-width:0;}#mermaid-svg-ohF5Q1a6IqTrxYZ5 .icon-shape,#mermaid-svg-ohF5Q1a6IqTrxYZ5 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ohF5Q1a6IqTrxYZ5 .icon-shape p,#mermaid-svg-ohF5Q1a6IqTrxYZ5 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-ohF5Q1a6IqTrxYZ5 .icon-shape .label rect,#mermaid-svg-ohF5Q1a6IqTrxYZ5 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ohF5Q1a6IqTrxYZ5 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-ohF5Q1a6IqTrxYZ5 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-ohF5Q1a6IqTrxYZ5 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 数据输出
lm 模块处理
数据输入
GPU设备信息
vGPU信息
配置参数
机器类型
Labels map
文件模式
NFD API模式
NFD Worker
NFD Controller
resource.Manager
vgpu.Interface
spec.Config
sysfs 文件
Labeler 组合树
Merge 合并
Labels map
Outputer
label_file.txt
NodeFeature CR
K8s Node Labels
三、核心业务逻辑深度解析
3.1 完整执行流程
#mermaid-svg-xfjZvxQTWIJdkH3d{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-xfjZvxQTWIJdkH3d .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-xfjZvxQTWIJdkH3d .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-xfjZvxQTWIJdkH3d .error-icon{fill:#552222;}#mermaid-svg-xfjZvxQTWIJdkH3d .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-xfjZvxQTWIJdkH3d .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-xfjZvxQTWIJdkH3d .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-xfjZvxQTWIJdkH3d .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-xfjZvxQTWIJdkH3d .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-xfjZvxQTWIJdkH3d .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-xfjZvxQTWIJdkH3d .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-xfjZvxQTWIJdkH3d .marker{fill:#333333;stroke:#333333;}#mermaid-svg-xfjZvxQTWIJdkH3d .marker.cross{stroke:#333333;}#mermaid-svg-xfjZvxQTWIJdkH3d svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-xfjZvxQTWIJdkH3d p{margin:0;}#mermaid-svg-xfjZvxQTWIJdkH3d .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-xfjZvxQTWIJdkH3d .cluster-label text{fill:#333;}#mermaid-svg-xfjZvxQTWIJdkH3d .cluster-label span{color:#333;}#mermaid-svg-xfjZvxQTWIJdkH3d .cluster-label span p{background-color:transparent;}#mermaid-svg-xfjZvxQTWIJdkH3d .label text,#mermaid-svg-xfjZvxQTWIJdkH3d span{fill:#333;color:#333;}#mermaid-svg-xfjZvxQTWIJdkH3d .node rect,#mermaid-svg-xfjZvxQTWIJdkH3d .node circle,#mermaid-svg-xfjZvxQTWIJdkH3d .node ellipse,#mermaid-svg-xfjZvxQTWIJdkH3d .node polygon,#mermaid-svg-xfjZvxQTWIJdkH3d .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-xfjZvxQTWIJdkH3d .rough-node .label text,#mermaid-svg-xfjZvxQTWIJdkH3d .node .label text,#mermaid-svg-xfjZvxQTWIJdkH3d .image-shape .label,#mermaid-svg-xfjZvxQTWIJdkH3d .icon-shape .label{text-anchor:middle;}#mermaid-svg-xfjZvxQTWIJdkH3d .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-xfjZvxQTWIJdkH3d .rough-node .label,#mermaid-svg-xfjZvxQTWIJdkH3d .node .label,#mermaid-svg-xfjZvxQTWIJdkH3d .image-shape .label,#mermaid-svg-xfjZvxQTWIJdkH3d .icon-shape .label{text-align:center;}#mermaid-svg-xfjZvxQTWIJdkH3d .node.clickable{cursor:pointer;}#mermaid-svg-xfjZvxQTWIJdkH3d .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-xfjZvxQTWIJdkH3d .arrowheadPath{fill:#333333;}#mermaid-svg-xfjZvxQTWIJdkH3d .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-xfjZvxQTWIJdkH3d .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-xfjZvxQTWIJdkH3d .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-xfjZvxQTWIJdkH3d .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-xfjZvxQTWIJdkH3d .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-xfjZvxQTWIJdkH3d .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-xfjZvxQTWIJdkH3d .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-xfjZvxQTWIJdkH3d .cluster text{fill:#333;}#mermaid-svg-xfjZvxQTWIJdkH3d .cluster span{color:#333;}#mermaid-svg-xfjZvxQTWIJdkH3d 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-xfjZvxQTWIJdkH3d .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-xfjZvxQTWIJdkH3d rect.text{fill:none;stroke-width:0;}#mermaid-svg-xfjZvxQTWIJdkH3d .icon-shape,#mermaid-svg-xfjZvxQTWIJdkH3d .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-xfjZvxQTWIJdkH3d .icon-shape p,#mermaid-svg-xfjZvxQTWIJdkH3d .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-xfjZvxQTWIJdkH3d .icon-shape .label rect,#mermaid-svg-xfjZvxQTWIJdkH3d .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-xfjZvxQTWIJdkH3d .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-xfjZvxQTWIJdkH3d .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-xfjZvxQTWIJdkH3d :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 否
是
是
否
否
是
否
是
开始
NewLabelers: 构造Labeler树
NewDeviceLabeler
manager.Init: 初始化NVML
初始化成功?
返回错误
manager.GetDevices: 获取GPU设备列表
设备数==0?
返回 empty 空标签
构造各子Labeler
newMachineTypeLabeler
newVersionLabeler
newMigCapabilityLabeler
newSharingLabeler
NewResourceLabeler
newGPUModeLabeler
newImexLabeler
Merge: 合并所有子Labeler
manager.Shutdown: 清理NVML
NewVGPULabeler: 构造vGPU Labeler
Merge(deviceLabeler, vgpuLabeler)
返回顶层Labeler
Labeler.Labels: 执行标签生成
遍历list中每个Labeler
调用子Labeler.Labels
合并所有Labels到allLabels
返回完整Labels
NewOutputer: 构造输出器
UseNodeFeatureAPI?
ToFile: 输出到文件
node名/命名空间有效?
返回错误
getOwnerReferences
nodeFeatureObject
Output: 创建/更新NodeFeature CR
完成
3.2 逐文件深度解析
3.2.1 labeler.go --- 模块入口与核心接口
go
// Labeler 定义了标签生成的接口
type Labeler interface {
Labels() (Labels, error)
}
设计意图 :这是整个模块的策略接口。所有标签生成器都实现此接口,通过多态组合实现灵活的标签生成体系。这种设计允许:
- 单个标签器独立生成标签
- 多个标签器通过
Merge组合 - 标签器可以嵌套形成树形结构
go
// NewLabelers 构造完整的 Labeler 树
func NewLabelers(manager resource.Manager, vgpu vgpu.Interface, config *spec.Config) (Labeler, error) {
deviceLabeler, err := NewDeviceLabeler(manager, config) // 构造设备标签器
if err != nil {
return nil, fmt.Errorf("error creating labeler: %v", err)
}
l := Merge(
deviceLabeler, // 设备标签器(包含版本、MIG、资源等)
NewVGPULabeler(vgpu), // vGPU 标签器
)
return l, nil
}
关键设计 :NewLabelers 是对外暴露的唯一入口。它只组合 deviceLabeler 和 vgpuLabeler 两个顶层标签器。deviceLabeler 内部再递归构造更细粒度的子标签器。
注意:
NewTimestampLabeler虽然在此模块定义,但不在NewLabelers中调用,而是由 GFD 主程序单独调用后 Merge。
3.2.2 labels.go --- 标签类型定义
go
type Labels map[string]string
// Labels 自身也实现了 Labeler 接口
func (labels Labels) Labels() (Labels, error) {
return labels, nil
}
设计精妙之处:
Labels既是数据载体(map[string]string),又自身实现Labeler接口- 这意味着任何
Labels实例可以直接当作Labeler使用 - 在
Merge中,静态标签可以直接以Labels{...}形式参与组合,无需包装
3.2.3 list.go --- 组合模式实现
go
// list 是一个 Labeler 切片,实现 Labeler 接口
type list []Labeler
// Merge 将多个 Labeler 合并为一个复合 Labeler
func Merge(labelers ...Labeler) Labeler {
l := list(labelers)
return l
}
设计模式 :这是经典的组合模式(Composite Pattern) 。Merge 允许将任意数量的 Labeler 组合成一个,对外暴露统一接口。
go
// Labels 遍历所有子 Labeler,合并标签
// 后面的 labeler 的标签会覆盖前面的
func (labelers list) Labels() (Labels, error) {
allLabels := make(Labels)
for _, labeler := range labelers {
labels, err := labeler.Labels()
if err != nil {
return nil, fmt.Errorf("error generating labels: %v", err)
}
for k, v := range labels {
allLabels[k] = v // 后者覆盖前者
}
}
return allLabels, nil
}
覆盖语义 :列表后面的 Labeler 生成的标签会覆盖前面的同名标签。这在 newGPULabelers 中被利用:全 GPU 的标签会覆盖 MIG-enabled 设备的同名标签。
#mermaid-svg-FZUnH9fokQSSfFF3{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-FZUnH9fokQSSfFF3 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-FZUnH9fokQSSfFF3 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-FZUnH9fokQSSfFF3 .error-icon{fill:#552222;}#mermaid-svg-FZUnH9fokQSSfFF3 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-FZUnH9fokQSSfFF3 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-FZUnH9fokQSSfFF3 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-FZUnH9fokQSSfFF3 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-FZUnH9fokQSSfFF3 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-FZUnH9fokQSSfFF3 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-FZUnH9fokQSSfFF3 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-FZUnH9fokQSSfFF3 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-FZUnH9fokQSSfFF3 .marker.cross{stroke:#333333;}#mermaid-svg-FZUnH9fokQSSfFF3 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-FZUnH9fokQSSfFF3 p{margin:0;}#mermaid-svg-FZUnH9fokQSSfFF3 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-FZUnH9fokQSSfFF3 .cluster-label text{fill:#333;}#mermaid-svg-FZUnH9fokQSSfFF3 .cluster-label span{color:#333;}#mermaid-svg-FZUnH9fokQSSfFF3 .cluster-label span p{background-color:transparent;}#mermaid-svg-FZUnH9fokQSSfFF3 .label text,#mermaid-svg-FZUnH9fokQSSfFF3 span{fill:#333;color:#333;}#mermaid-svg-FZUnH9fokQSSfFF3 .node rect,#mermaid-svg-FZUnH9fokQSSfFF3 .node circle,#mermaid-svg-FZUnH9fokQSSfFF3 .node ellipse,#mermaid-svg-FZUnH9fokQSSfFF3 .node polygon,#mermaid-svg-FZUnH9fokQSSfFF3 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-FZUnH9fokQSSfFF3 .rough-node .label text,#mermaid-svg-FZUnH9fokQSSfFF3 .node .label text,#mermaid-svg-FZUnH9fokQSSfFF3 .image-shape .label,#mermaid-svg-FZUnH9fokQSSfFF3 .icon-shape .label{text-anchor:middle;}#mermaid-svg-FZUnH9fokQSSfFF3 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-FZUnH9fokQSSfFF3 .rough-node .label,#mermaid-svg-FZUnH9fokQSSfFF3 .node .label,#mermaid-svg-FZUnH9fokQSSfFF3 .image-shape .label,#mermaid-svg-FZUnH9fokQSSfFF3 .icon-shape .label{text-align:center;}#mermaid-svg-FZUnH9fokQSSfFF3 .node.clickable{cursor:pointer;}#mermaid-svg-FZUnH9fokQSSfFF3 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-FZUnH9fokQSSfFF3 .arrowheadPath{fill:#333333;}#mermaid-svg-FZUnH9fokQSSfFF3 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-FZUnH9fokQSSfFF3 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-FZUnH9fokQSSfFF3 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-FZUnH9fokQSSfFF3 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-FZUnH9fokQSSfFF3 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-FZUnH9fokQSSfFF3 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-FZUnH9fokQSSfFF3 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-FZUnH9fokQSSfFF3 .cluster text{fill:#333;}#mermaid-svg-FZUnH9fokQSSfFF3 .cluster span{color:#333;}#mermaid-svg-FZUnH9fokQSSfFF3 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-FZUnH9fokQSSfFF3 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-FZUnH9fokQSSfFF3 rect.text{fill:none;stroke-width:0;}#mermaid-svg-FZUnH9fokQSSfFF3 .icon-shape,#mermaid-svg-FZUnH9fokQSSfFF3 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-FZUnH9fokQSSfFF3 .icon-shape p,#mermaid-svg-FZUnH9fokQSSfFF3 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-FZUnH9fokQSSfFF3 .icon-shape .label rect,#mermaid-svg-FZUnH9fokQSSfFF3 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-FZUnH9fokQSSfFF3 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-FZUnH9fokQSSfFF3 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-FZUnH9fokQSSfFF3 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Merge labeler1, labeler2, labeler3
list.Labels
调用 labeler1.Labels
allLabels += labeler1标签
调用 labeler2.Labels
allLabels += labeler2标签
同名key覆盖
调用 labeler3.Labels
allLabels += labeler3标签
同名key覆盖
返回合并后的 allLabels
3.2.4 empty.go --- 空 Labeler(Null Object 模式)
go
type empty struct{}
func (manager empty) Labels() (Labels, error) {
return nil, nil
}
设计模式 :空对象模式(Null Object Pattern) 。当没有设备或某功能不可用时,返回 empty{} 而非 nil,避免空指针,简化调用方代码。
使用场景:
NewDeviceLabeler中设备数为 0 时NewResourceLabeler中设备数为 0 时newMigStrategySingleLabeler中无 MIG-enabled 设备时migStrategyLabeler中策略为 none 时NewTimestampLabeler中NoTimestamp为 true 时newImexLabeler中无 Fabric ID 时
3.2.5 nvml.go --- 设备标签器工厂(核心文件)
NewDeviceLabeler --- 设备标签器集合构造
go
func NewDeviceLabeler(manager resource.Manager, config *spec.Config) (Labeler, error) {
// 1. 初始化 NVML
if err := manager.Init(); err != nil {
return nil, fmt.Errorf("failed to initialize resource manager: %v", err)
}
defer func() {
_ = manager.Shutdown() // 确保清理
}()
// 2. 获取设备列表
devices, err := manager.GetDevices()
if err != nil {
return nil, fmt.Errorf("error getting devices: %v", err)
}
// 3. 无设备时返回空 Labeler
if len(devices) == 0 {
return empty{}, nil
}
// 4. 构造各子标签器
machineTypeLabeler, err := newMachineTypeLabeler(*config.Flags.GFD.MachineTypeFile)
// ...
versionLabeler, err := newVersionLabeler(manager)
// ...
migCapabilityLabeler, err := newMigCapabilityLabeler(manager)
// ...
sharingLabeler, err := newSharingLabeler(manager, config)
// ...
resourceLabeler, err := NewResourceLabeler(manager, config)
// ...
gpuModeLabeler, err := newGPUModeLabeler(devices)
// ...
imexLabeler, err := newImexLabeler(config, devices)
// ...
// 5. 合并所有子标签器
l := Merge(
machineTypeLabeler, // nvidia.com/gpu.machine
versionLabeler, // nvidia.com/cuda.* 版本标签
migCapabilityLabeler, // nvidia.com/mig.capable
sharingLabeler, // nvidia.com/mps.capable
resourceLabeler, // nvidia.com/gpu.product/count/replicas等
gpuModeLabeler, // nvidia.com/gpu.mode
imexLabeler, // nvidia.com/gpu.clique
)
return l, nil
}
关键设计 :Init() / Shutdown() 配对调用,确保 NVML 正确初始化和清理。使用 defer 保证 Shutdown 必然执行。
newVersionLabeler --- CUDA/Driver 版本标签
go
func newVersionLabeler(manager resource.Manager) (Labeler, error) {
driverVersion, err := manager.GetDriverVersion() // 如 "535.129.03"
// ...
// 解析驱动版本: "X.Y[.Z]" 格式
driverVersionSplit := strings.Split(driverVersion, ".")
if len(driverVersionSplit) > 3 || len(driverVersionSplit) < 2 {
return nil, fmt.Errorf("error getting driver version: ...")
}
driverMajor := driverVersionSplit[0] // "535"
driverMinor := driverVersionSplit[1] // "129"
driverRev := ""
if len(driverVersionSplit) > 2 {
driverRev = driverVersionSplit[2] // "03"
}
// 获取 CUDA Runtime 版本
cudaMajor, cudaMinor, err := manager.GetCudaDriverVersion() // 如 12, 2
// ...
labels := Labels{
// 旧版标签(已废弃,保持向后兼容)
"nvidia.com/cuda.driver.major": driverMajor,
"nvidia.com/cuda.driver.minor": driverMinor,
"nvidia.com/cuda.driver.rev": driverRev,
"nvidia.com/cuda.runtime.major": fmt.Sprintf("%d", cudaMajor),
"nvidia.com/cuda.runtime.minor": fmt.Sprintf("%d", cudaMinor),
// 新版标签(命名空间化)
"nvidia.com/cuda.driver-version.major": driverMajor,
"nvidia.com/cuda.driver-version.minor": driverMinor,
"nvidia.com/cuda.driver-version.revision": driverRev,
"nvidia.com/cuda.driver-version.full": driverVersion,
"nvidia.com/cuda.runtime-version.major": fmt.Sprintf("%d", cudaMajor),
"nvidia.com/cuda.runtime-version.minor": fmt.Sprintf("%d", cudaMinor),
"nvidia.com/cuda.runtime-version.full": fmt.Sprintf("%d.%d", cudaMajor, cudaMinor),
}
return labels, nil
}
标签体系 :同时维护新旧两套标签 ,旧标签保持向后兼容,新标签使用更规范的命名(driver-version vs driver)。
#mermaid-svg-Wk1TDpSUEAwq3o5d{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-Wk1TDpSUEAwq3o5d .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-Wk1TDpSUEAwq3o5d .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-Wk1TDpSUEAwq3o5d .error-icon{fill:#552222;}#mermaid-svg-Wk1TDpSUEAwq3o5d .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-Wk1TDpSUEAwq3o5d .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-Wk1TDpSUEAwq3o5d .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-Wk1TDpSUEAwq3o5d .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-Wk1TDpSUEAwq3o5d .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-Wk1TDpSUEAwq3o5d .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-Wk1TDpSUEAwq3o5d .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-Wk1TDpSUEAwq3o5d .marker{fill:#333333;stroke:#333333;}#mermaid-svg-Wk1TDpSUEAwq3o5d .marker.cross{stroke:#333333;}#mermaid-svg-Wk1TDpSUEAwq3o5d svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-Wk1TDpSUEAwq3o5d p{margin:0;}#mermaid-svg-Wk1TDpSUEAwq3o5d .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-Wk1TDpSUEAwq3o5d .cluster-label text{fill:#333;}#mermaid-svg-Wk1TDpSUEAwq3o5d .cluster-label span{color:#333;}#mermaid-svg-Wk1TDpSUEAwq3o5d .cluster-label span p{background-color:transparent;}#mermaid-svg-Wk1TDpSUEAwq3o5d .label text,#mermaid-svg-Wk1TDpSUEAwq3o5d span{fill:#333;color:#333;}#mermaid-svg-Wk1TDpSUEAwq3o5d .node rect,#mermaid-svg-Wk1TDpSUEAwq3o5d .node circle,#mermaid-svg-Wk1TDpSUEAwq3o5d .node ellipse,#mermaid-svg-Wk1TDpSUEAwq3o5d .node polygon,#mermaid-svg-Wk1TDpSUEAwq3o5d .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-Wk1TDpSUEAwq3o5d .rough-node .label text,#mermaid-svg-Wk1TDpSUEAwq3o5d .node .label text,#mermaid-svg-Wk1TDpSUEAwq3o5d .image-shape .label,#mermaid-svg-Wk1TDpSUEAwq3o5d .icon-shape .label{text-anchor:middle;}#mermaid-svg-Wk1TDpSUEAwq3o5d .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-Wk1TDpSUEAwq3o5d .rough-node .label,#mermaid-svg-Wk1TDpSUEAwq3o5d .node .label,#mermaid-svg-Wk1TDpSUEAwq3o5d .image-shape .label,#mermaid-svg-Wk1TDpSUEAwq3o5d .icon-shape .label{text-align:center;}#mermaid-svg-Wk1TDpSUEAwq3o5d .node.clickable{cursor:pointer;}#mermaid-svg-Wk1TDpSUEAwq3o5d .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-Wk1TDpSUEAwq3o5d .arrowheadPath{fill:#333333;}#mermaid-svg-Wk1TDpSUEAwq3o5d .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-Wk1TDpSUEAwq3o5d .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-Wk1TDpSUEAwq3o5d .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Wk1TDpSUEAwq3o5d .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-Wk1TDpSUEAwq3o5d .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Wk1TDpSUEAwq3o5d .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-Wk1TDpSUEAwq3o5d .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-Wk1TDpSUEAwq3o5d .cluster text{fill:#333;}#mermaid-svg-Wk1TDpSUEAwq3o5d .cluster span{color:#333;}#mermaid-svg-Wk1TDpSUEAwq3o5d 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-Wk1TDpSUEAwq3o5d .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-Wk1TDpSUEAwq3o5d rect.text{fill:none;stroke-width:0;}#mermaid-svg-Wk1TDpSUEAwq3o5d .icon-shape,#mermaid-svg-Wk1TDpSUEAwq3o5d .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Wk1TDpSUEAwq3o5d .icon-shape p,#mermaid-svg-Wk1TDpSUEAwq3o5d .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-Wk1TDpSUEAwq3o5d .icon-shape .label rect,#mermaid-svg-Wk1TDpSUEAwq3o5d .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Wk1TDpSUEAwq3o5d .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-Wk1TDpSUEAwq3o5d .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-Wk1TDpSUEAwq3o5d :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 新版标签
旧版标签 (Deprecated)
nvidia.com/cuda.driver.major
nvidia.com/cuda.driver.minor
nvidia.com/cuda.driver.rev
nvidia.com/cuda.runtime.major
nvidia.com/cuda.runtime.minor
nvidia.com/cuda.driver-version.major
nvidia.com/cuda.driver-version.minor
nvidia.com/cuda.driver-version.revision
nvidia.com/cuda.driver-version.full
nvidia.com/cuda.runtime-version.major
nvidia.com/cuda.runtime-version.minor
nvidia.com/cuda.runtime-version.full
newMigCapabilityLabeler --- MIG 能力标签
go
func newMigCapabilityLabeler(manager resource.Manager) (Labeler, error) {
isMigCapable := false
devices, err := manager.GetDevices()
// ...
if len(devices) == 0 {
return empty{}, nil
}
// 遍历所有设备,只要有一个支持 MIG 就标记为 true
for _, d := range devices {
isMigCapable, err = d.IsMigCapable()
if err != nil {
return nil, fmt.Errorf("error getting mig capability: %v", err)
}
if isMigCapable {
break // 短路优化:找到一个就够了
}
}
labels := Labels{
"nvidia.com/mig.capable": strconv.FormatBool(isMigCapable), // "true" 或 "false"
}
return labels, nil
}
语义 :节点级别标签,只要有一张 GPU 支持 MIG,则 nvidia.com/mig.capable=true。
newSharingLabeler --- MPS 共享能力标签
go
func newSharingLabeler(manager resource.Manager, config *spec.Config) (Labeler, error) {
// 非 MPS 策略直接标记 mps.capable=false
if config == nil || config.Sharing.SharingStrategy() != spec.SharingStrategyMPS {
labels := Labels{
"nvidia.com/mps.capable": "false",
}
return labels, nil
}
// MPS 策略下,检查是否所有设备都不在 MIG 模式
capable, err := isMPSCapable(manager)
if err != nil {
return nil, fmt.Errorf("failed to check MPS-capable: %w", err)
}
labels := Labels{
"nvidia.com/mps.capable": strconv.FormatBool(capable),
}
return labels, nil
}
func isMPSCapable(manager resource.Manager) (bool, error) {
devices, err := manager.GetDevices()
// ...
// 遍历设备,如果有任何设备启用了 MIG,则不支持 MPS
for _, d := range devices {
isMigEnabled, err := d.IsMigEnabled()
if err != nil {
return false, fmt.Errorf("failed to check if device is MIG-enabled: %w", err)
}
if isMigEnabled {
return false, fmt.Errorf("%w for mig devices", errMPSSharingNotSupported)
}
}
return true, nil
}
业务约束:MPS 与 MIG 互斥。如果任何 GPU 启用了 MIG,则 MPS 不可用。
newGPUModeLabeler --- GPU 模式标签
go
func newGPUModeLabeler(devices []resource.Device) (Labeler, error) {
classes, err := getDeviceClasses(devices)
if err != nil {
klog.Warningf("Failed to create GPU mode labeler: %v", err)
return Labels{"nvidia.com/gpu.mode": "unknown"}, nil // 降级处理
}
gpuMode := getModeForClasses(classes)
labels := Labels{
"nvidia.com/gpu.mode": gpuMode, // "graphics" / "compute" / "unknown"
}
return labels, nil
}
func getModeForClasses(classes []uint32) string {
if len(classes) == 0 {
return "unknown"
}
// 所有设备必须属于同一 PCI class
for _, class := range classes {
if class != classes[0] {
return "unknown" // 混合类型 → unknown
}
}
switch classes[0] {
case nvpci.PCIVgaControllerClass: // 0x0300 → VGA 控制器
return "graphics"
case nvpci.PCI3dControllerClass: // 0x0302 → 3D 控制器
return "compute"
default:
return "unknown"
}
}
func getDeviceClasses(devices []resource.Device) ([]uint32, error) {
seenClasses := make(map[uint32]bool) // 去重
for _, d := range devices {
class, err := d.GetPCIClass()
if err != nil {
return nil, err
}
seenClasses[class] = true
}
var classes []uint32
for class := range seenClasses {
classes = append(classes, class)
}
return classes, nil
}
逻辑 :通过 PCI Class ID 判断 GPU 工作模式。0x0300(VGA Controller)= Graphics 模式,0x0302(3D Controller)= Compute 模式。混合设备类型返回 unknown。
#mermaid-svg-Fw1m9gk0M9JOSzU6{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-Fw1m9gk0M9JOSzU6 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-Fw1m9gk0M9JOSzU6 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-Fw1m9gk0M9JOSzU6 .error-icon{fill:#552222;}#mermaid-svg-Fw1m9gk0M9JOSzU6 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-Fw1m9gk0M9JOSzU6 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-Fw1m9gk0M9JOSzU6 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-Fw1m9gk0M9JOSzU6 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-Fw1m9gk0M9JOSzU6 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-Fw1m9gk0M9JOSzU6 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-Fw1m9gk0M9JOSzU6 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-Fw1m9gk0M9JOSzU6 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-Fw1m9gk0M9JOSzU6 .marker.cross{stroke:#333333;}#mermaid-svg-Fw1m9gk0M9JOSzU6 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-Fw1m9gk0M9JOSzU6 p{margin:0;}#mermaid-svg-Fw1m9gk0M9JOSzU6 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-Fw1m9gk0M9JOSzU6 .cluster-label text{fill:#333;}#mermaid-svg-Fw1m9gk0M9JOSzU6 .cluster-label span{color:#333;}#mermaid-svg-Fw1m9gk0M9JOSzU6 .cluster-label span p{background-color:transparent;}#mermaid-svg-Fw1m9gk0M9JOSzU6 .label text,#mermaid-svg-Fw1m9gk0M9JOSzU6 span{fill:#333;color:#333;}#mermaid-svg-Fw1m9gk0M9JOSzU6 .node rect,#mermaid-svg-Fw1m9gk0M9JOSzU6 .node circle,#mermaid-svg-Fw1m9gk0M9JOSzU6 .node ellipse,#mermaid-svg-Fw1m9gk0M9JOSzU6 .node polygon,#mermaid-svg-Fw1m9gk0M9JOSzU6 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-Fw1m9gk0M9JOSzU6 .rough-node .label text,#mermaid-svg-Fw1m9gk0M9JOSzU6 .node .label text,#mermaid-svg-Fw1m9gk0M9JOSzU6 .image-shape .label,#mermaid-svg-Fw1m9gk0M9JOSzU6 .icon-shape .label{text-anchor:middle;}#mermaid-svg-Fw1m9gk0M9JOSzU6 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-Fw1m9gk0M9JOSzU6 .rough-node .label,#mermaid-svg-Fw1m9gk0M9JOSzU6 .node .label,#mermaid-svg-Fw1m9gk0M9JOSzU6 .image-shape .label,#mermaid-svg-Fw1m9gk0M9JOSzU6 .icon-shape .label{text-align:center;}#mermaid-svg-Fw1m9gk0M9JOSzU6 .node.clickable{cursor:pointer;}#mermaid-svg-Fw1m9gk0M9JOSzU6 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-Fw1m9gk0M9JOSzU6 .arrowheadPath{fill:#333333;}#mermaid-svg-Fw1m9gk0M9JOSzU6 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-Fw1m9gk0M9JOSzU6 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-Fw1m9gk0M9JOSzU6 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Fw1m9gk0M9JOSzU6 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-Fw1m9gk0M9JOSzU6 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Fw1m9gk0M9JOSzU6 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-Fw1m9gk0M9JOSzU6 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-Fw1m9gk0M9JOSzU6 .cluster text{fill:#333;}#mermaid-svg-Fw1m9gk0M9JOSzU6 .cluster span{color:#333;}#mermaid-svg-Fw1m9gk0M9JOSzU6 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-Fw1m9gk0M9JOSzU6 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-Fw1m9gk0M9JOSzU6 rect.text{fill:none;stroke-width:0;}#mermaid-svg-Fw1m9gk0M9JOSzU6 .icon-shape,#mermaid-svg-Fw1m9gk0M9JOSzU6 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Fw1m9gk0M9JOSzU6 .icon-shape p,#mermaid-svg-Fw1m9gk0M9JOSzU6 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-Fw1m9gk0M9JOSzU6 .icon-shape .label rect,#mermaid-svg-Fw1m9gk0M9JOSzU6 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Fw1m9gk0M9JOSzU6 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-Fw1m9gk0M9JOSzU6 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-Fw1m9gk0M9JOSzU6 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 否, 混合
是, 统一
0x0300 VGA Controller
0x0302 3D Controller
其他
获取所有GPU设备PCI Class
设备类型==1?
返回 unknown
PCI Class
返回 graphics
返回 compute
返回 unknown
3.2.6 mig-strategy.go --- MIG 策略标签器
MIG 策略常量
go
const (
MigStrategyNone = "none"
MigStrategySingle = "single"
MigStrategyMixed = "mixed"
)
migResource 结构体
go
// migResource 用于跟踪 MIG 设备,关联资源名、设备和计数
type migResource struct {
name spec.ResourceName // 资源名(如 nvidia.com/gpu 或 nvidia.com/mig-1g.10gb)
device resource.Device // MIG 设备引用
count int // 该类型 MIG 设备的数量
}
NewResourceLabeler --- 资源标签器入口
go
func NewResourceLabeler(manager resource.Manager, config *spec.Config) (Labeler, error) {
devices, err := manager.GetDevices()
// ...
if len(devices) == 0 {
return empty{}, nil
}
// 构造全 GPU 标签器
fullGPULabeler, err := newGPULabelers(manager, config)
// ...
// MIG 策略为 none 时,只返回全 GPU 标签器
if *config.Flags.MigStrategy == spec.MigStrategyNone {
return fullGPULabeler, nil
}
// 构造 MIG 标签器
migLabeler, err := newMigLabeler(manager, config)
// ...
// 合并全 GPU 标签器和 MIG 标签器
labelers := Merge(fullGPULabeler, migLabeler)
return labelers, nil
}
#mermaid-svg-NdSMld3fqilpIi6z{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-NdSMld3fqilpIi6z .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-NdSMld3fqilpIi6z .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-NdSMld3fqilpIi6z .error-icon{fill:#552222;}#mermaid-svg-NdSMld3fqilpIi6z .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-NdSMld3fqilpIi6z .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-NdSMld3fqilpIi6z .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-NdSMld3fqilpIi6z .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-NdSMld3fqilpIi6z .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-NdSMld3fqilpIi6z .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-NdSMld3fqilpIi6z .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-NdSMld3fqilpIi6z .marker{fill:#333333;stroke:#333333;}#mermaid-svg-NdSMld3fqilpIi6z .marker.cross{stroke:#333333;}#mermaid-svg-NdSMld3fqilpIi6z svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-NdSMld3fqilpIi6z p{margin:0;}#mermaid-svg-NdSMld3fqilpIi6z .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-NdSMld3fqilpIi6z .cluster-label text{fill:#333;}#mermaid-svg-NdSMld3fqilpIi6z .cluster-label span{color:#333;}#mermaid-svg-NdSMld3fqilpIi6z .cluster-label span p{background-color:transparent;}#mermaid-svg-NdSMld3fqilpIi6z .label text,#mermaid-svg-NdSMld3fqilpIi6z span{fill:#333;color:#333;}#mermaid-svg-NdSMld3fqilpIi6z .node rect,#mermaid-svg-NdSMld3fqilpIi6z .node circle,#mermaid-svg-NdSMld3fqilpIi6z .node ellipse,#mermaid-svg-NdSMld3fqilpIi6z .node polygon,#mermaid-svg-NdSMld3fqilpIi6z .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-NdSMld3fqilpIi6z .rough-node .label text,#mermaid-svg-NdSMld3fqilpIi6z .node .label text,#mermaid-svg-NdSMld3fqilpIi6z .image-shape .label,#mermaid-svg-NdSMld3fqilpIi6z .icon-shape .label{text-anchor:middle;}#mermaid-svg-NdSMld3fqilpIi6z .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-NdSMld3fqilpIi6z .rough-node .label,#mermaid-svg-NdSMld3fqilpIi6z .node .label,#mermaid-svg-NdSMld3fqilpIi6z .image-shape .label,#mermaid-svg-NdSMld3fqilpIi6z .icon-shape .label{text-align:center;}#mermaid-svg-NdSMld3fqilpIi6z .node.clickable{cursor:pointer;}#mermaid-svg-NdSMld3fqilpIi6z .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-NdSMld3fqilpIi6z .arrowheadPath{fill:#333333;}#mermaid-svg-NdSMld3fqilpIi6z .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-NdSMld3fqilpIi6z .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-NdSMld3fqilpIi6z .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-NdSMld3fqilpIi6z .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-NdSMld3fqilpIi6z .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-NdSMld3fqilpIi6z .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-NdSMld3fqilpIi6z .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-NdSMld3fqilpIi6z .cluster text{fill:#333;}#mermaid-svg-NdSMld3fqilpIi6z .cluster span{color:#333;}#mermaid-svg-NdSMld3fqilpIi6z 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-NdSMld3fqilpIi6z .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-NdSMld3fqilpIi6z rect.text{fill:none;stroke-width:0;}#mermaid-svg-NdSMld3fqilpIi6z .icon-shape,#mermaid-svg-NdSMld3fqilpIi6z .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-NdSMld3fqilpIi6z .icon-shape p,#mermaid-svg-NdSMld3fqilpIi6z .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-NdSMld3fqilpIi6z .icon-shape .label rect,#mermaid-svg-NdSMld3fqilpIi6z .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-NdSMld3fqilpIi6z .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-NdSMld3fqilpIi6z .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-NdSMld3fqilpIi6z :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 是
否
none
single/mixed
NewResourceLabeler
设备数==0?
返回 empty
newGPULabelers: 构造全GPU标签
MigStrategy?
只返回全GPU标签
newMigLabeler: 构造MIG标签
Merge: 合并全GPU+MIG标签
返回合并标签
newGPULabelers --- 全 GPU 标签器构造
go
func newGPULabelers(manager resource.Manager, config *spec.Config) (Labeler, error) {
deviceInfo := mig.NewDeviceInfo(manager)
// 获取按 MIG 启用状态分组的设备映射
// map[bool][]resource.Device: true=MIG启用, false=MIG未启用
devicesByMigEnabled, err := deviceInfo.GetDevicesMap()
// ...
counts := make(map[string]int) // 设备型号 → 数量
migEnabledDevices := make(map[string]resource.Device) // 型号 → MIG启用设备
fullGPUs := make(map[string]resource.Device) // 型号 → 全GPU设备
// 统计 MIG 启用设备
for _, device := range devicesByMigEnabled[true] {
name, err := device.GetName()
// ...
migEnabledDevices[name] = device
counts[name]++
}
// 统计 MIG 未启用设备(全 GPU)
for _, device := range devicesByMigEnabled[false] {
name, err := device.GetName()
// ...
fullGPUs[name] = device
counts[name]++
}
// 多型号警告
if len(counts) > 1 {
klog.Warningf("Multiple device types detected: %v", names)
}
var labelers list
// MIG 启用设备:生成不带共享信息的标签
for name, migEnabledDevice := range migEnabledDevices {
l, err := NewGPUResourceLabelerWithoutSharing(migEnabledDevice, counts[name])
// ...
labelers = append(labelers, l)
}
// 全 GPU 设备:生成带共享信息的标签(覆盖MIG启用设备的同名标签)
for name, fullGPU := range fullGPUs {
l, err := NewGPUResourceLabeler(config, fullGPU, counts[name])
// ...
labelers = append(labelers, l)
}
return labelers.Labels() // 立即执行,返回 Labels
}
关键逻辑:
- MIG 启用的设备生成标签时不包含共享信息(因为 MIG 分区本身就是一种资源切分)
- 全 GPU 设备生成标签时包含共享信息(Time-Slicing/MPS)
- 全 GPU 标签在 Merge 列表中靠后,会覆盖 MIG 启用设备的同名标签
newMigLabeler --- MIG 策略分发
go
func newMigLabeler(manager resource.Manager, config *spec.Config) (Labeler, error) {
var labeler Labeler
switch *config.Flags.MigStrategy {
case MigStrategyNone:
labeler = empty{}
case MigStrategySingle:
labeler, err = newMigStrategySingleLabeler(manager, config)
case MigStrategyMixed:
labeler, err = newMigStrategyMixedLabeler(manager, config)
default:
return nil, fmt.Errorf("unknown strategy: %v", *config.Flags.MigStrategy)
}
// 合并策略标签和具体标签
labelers := Merge(
migStrategyLabeler(*config.Flags.MigStrategy), // nvidia.com/mig.strategy
labeler, // 具体 MIG 资源标签
)
return labelers, nil
}
#mermaid-svg-12gUBatcgIHcFfyp{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-12gUBatcgIHcFfyp .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-12gUBatcgIHcFfyp .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-12gUBatcgIHcFfyp .error-icon{fill:#552222;}#mermaid-svg-12gUBatcgIHcFfyp .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-12gUBatcgIHcFfyp .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-12gUBatcgIHcFfyp .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-12gUBatcgIHcFfyp .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-12gUBatcgIHcFfyp .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-12gUBatcgIHcFfyp .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-12gUBatcgIHcFfyp .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-12gUBatcgIHcFfyp .marker{fill:#333333;stroke:#333333;}#mermaid-svg-12gUBatcgIHcFfyp .marker.cross{stroke:#333333;}#mermaid-svg-12gUBatcgIHcFfyp svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-12gUBatcgIHcFfyp p{margin:0;}#mermaid-svg-12gUBatcgIHcFfyp .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-12gUBatcgIHcFfyp .cluster-label text{fill:#333;}#mermaid-svg-12gUBatcgIHcFfyp .cluster-label span{color:#333;}#mermaid-svg-12gUBatcgIHcFfyp .cluster-label span p{background-color:transparent;}#mermaid-svg-12gUBatcgIHcFfyp .label text,#mermaid-svg-12gUBatcgIHcFfyp span{fill:#333;color:#333;}#mermaid-svg-12gUBatcgIHcFfyp .node rect,#mermaid-svg-12gUBatcgIHcFfyp .node circle,#mermaid-svg-12gUBatcgIHcFfyp .node ellipse,#mermaid-svg-12gUBatcgIHcFfyp .node polygon,#mermaid-svg-12gUBatcgIHcFfyp .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-12gUBatcgIHcFfyp .rough-node .label text,#mermaid-svg-12gUBatcgIHcFfyp .node .label text,#mermaid-svg-12gUBatcgIHcFfyp .image-shape .label,#mermaid-svg-12gUBatcgIHcFfyp .icon-shape .label{text-anchor:middle;}#mermaid-svg-12gUBatcgIHcFfyp .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-12gUBatcgIHcFfyp .rough-node .label,#mermaid-svg-12gUBatcgIHcFfyp .node .label,#mermaid-svg-12gUBatcgIHcFfyp .image-shape .label,#mermaid-svg-12gUBatcgIHcFfyp .icon-shape .label{text-align:center;}#mermaid-svg-12gUBatcgIHcFfyp .node.clickable{cursor:pointer;}#mermaid-svg-12gUBatcgIHcFfyp .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-12gUBatcgIHcFfyp .arrowheadPath{fill:#333333;}#mermaid-svg-12gUBatcgIHcFfyp .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-12gUBatcgIHcFfyp .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-12gUBatcgIHcFfyp .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-12gUBatcgIHcFfyp .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-12gUBatcgIHcFfyp .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-12gUBatcgIHcFfyp .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-12gUBatcgIHcFfyp .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-12gUBatcgIHcFfyp .cluster text{fill:#333;}#mermaid-svg-12gUBatcgIHcFfyp .cluster span{color:#333;}#mermaid-svg-12gUBatcgIHcFfyp 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-12gUBatcgIHcFfyp .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-12gUBatcgIHcFfyp rect.text{fill:none;stroke-width:0;}#mermaid-svg-12gUBatcgIHcFfyp .icon-shape,#mermaid-svg-12gUBatcgIHcFfyp .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-12gUBatcgIHcFfyp .icon-shape p,#mermaid-svg-12gUBatcgIHcFfyp .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-12gUBatcgIHcFfyp .icon-shape .label rect,#mermaid-svg-12gUBatcgIHcFfyp .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-12gUBatcgIHcFfyp .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-12gUBatcgIHcFfyp .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-12gUBatcgIHcFfyp :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} none
single
mixed
未知
newMigLabeler
MigStrategy
empty
newMigStrategySingleLabeler
newMigStrategyMixedLabeler
返回错误
Merge: migStrategyLabeler + empty
Merge: migStrategyLabeler + single标签
Merge: migStrategyLabeler + mixed标签
返回
newMigStrategySingleLabeler --- Single 策略
Single 策略的核心约束:所有 GPU 必须以相同方式配置 MIG。
go
func newMigStrategySingleLabeler(manager resource.Manager, config *spec.Config) (Labeler, error) {
deviceInfo := mig.NewDeviceInfo(manager)
// 1. 获取 MIG 启用的设备
migEnabledDevices, err := deviceInfo.GetDevicesWithMigEnabled()
// ...
// 无 MIG 启用设备 → 等同于 none 策略
if len(migEnabledDevices) == 0 {
return empty{}, nil
}
// 2. 检查是否有空的 MIG 启用设备(启用了 MIG 但未创建 MIG 实例)
hasEmpty, err := deviceInfo.AnyMigEnabledDeviceIsEmpty()
// ...
// 有空设备 → 返回 INVALID 标签
if hasEmpty {
return newInvalidMigStrategyLabeler(migEnabledDevices[0], "at least one MIG device is enabled but empty")
}
// 3. 检查是否有 MIG 未启用的设备混用
migDisabledDevices, err := deviceInfo.GetDevicesWithMigDisabled()
// ...
// 混用 → 返回 INVALID 标签
if len(migDisabledDevices) != 0 {
return newInvalidMigStrategyLabeler(migEnabledDevices[0], "devices with MIG enabled and disable detected")
}
// 4. 获取所有 MIG 设备
migs, err := deviceInfo.GetAllMigDevices()
// ...
// 5. 统计 MIG 设备类型
resources := make(map[string]migResource)
for _, mig := range migs {
name, err := mig.GetName() // 如 "1g.10gb"
// ...
resource, exists := resources[name]
if !exists {
resource.device = mig
resource.name = fullGPUResourceName // "nvidia.com/gpu" --- single策略下MIG设备用GPU资源名
}
resource.count++
resources[name] = resource
}
// 6. 多种 MIG 类型 → INVALID
if len(resources) != 1 {
return newInvalidMigStrategyLabeler(migEnabledDevices[0], "more than one MIG device type present on node")
}
// 7. 生成有效的 MIG 标签
return newMIGDeviceLabelers(resources, config)
}
#mermaid-svg-YXPk3zhxz0XzdIx5{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-YXPk3zhxz0XzdIx5 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-YXPk3zhxz0XzdIx5 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-YXPk3zhxz0XzdIx5 .error-icon{fill:#552222;}#mermaid-svg-YXPk3zhxz0XzdIx5 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-YXPk3zhxz0XzdIx5 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-YXPk3zhxz0XzdIx5 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-YXPk3zhxz0XzdIx5 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-YXPk3zhxz0XzdIx5 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-YXPk3zhxz0XzdIx5 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-YXPk3zhxz0XzdIx5 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-YXPk3zhxz0XzdIx5 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-YXPk3zhxz0XzdIx5 .marker.cross{stroke:#333333;}#mermaid-svg-YXPk3zhxz0XzdIx5 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-YXPk3zhxz0XzdIx5 p{margin:0;}#mermaid-svg-YXPk3zhxz0XzdIx5 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-YXPk3zhxz0XzdIx5 .cluster-label text{fill:#333;}#mermaid-svg-YXPk3zhxz0XzdIx5 .cluster-label span{color:#333;}#mermaid-svg-YXPk3zhxz0XzdIx5 .cluster-label span p{background-color:transparent;}#mermaid-svg-YXPk3zhxz0XzdIx5 .label text,#mermaid-svg-YXPk3zhxz0XzdIx5 span{fill:#333;color:#333;}#mermaid-svg-YXPk3zhxz0XzdIx5 .node rect,#mermaid-svg-YXPk3zhxz0XzdIx5 .node circle,#mermaid-svg-YXPk3zhxz0XzdIx5 .node ellipse,#mermaid-svg-YXPk3zhxz0XzdIx5 .node polygon,#mermaid-svg-YXPk3zhxz0XzdIx5 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-YXPk3zhxz0XzdIx5 .rough-node .label text,#mermaid-svg-YXPk3zhxz0XzdIx5 .node .label text,#mermaid-svg-YXPk3zhxz0XzdIx5 .image-shape .label,#mermaid-svg-YXPk3zhxz0XzdIx5 .icon-shape .label{text-anchor:middle;}#mermaid-svg-YXPk3zhxz0XzdIx5 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-YXPk3zhxz0XzdIx5 .rough-node .label,#mermaid-svg-YXPk3zhxz0XzdIx5 .node .label,#mermaid-svg-YXPk3zhxz0XzdIx5 .image-shape .label,#mermaid-svg-YXPk3zhxz0XzdIx5 .icon-shape .label{text-align:center;}#mermaid-svg-YXPk3zhxz0XzdIx5 .node.clickable{cursor:pointer;}#mermaid-svg-YXPk3zhxz0XzdIx5 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-YXPk3zhxz0XzdIx5 .arrowheadPath{fill:#333333;}#mermaid-svg-YXPk3zhxz0XzdIx5 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-YXPk3zhxz0XzdIx5 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-YXPk3zhxz0XzdIx5 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-YXPk3zhxz0XzdIx5 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-YXPk3zhxz0XzdIx5 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-YXPk3zhxz0XzdIx5 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-YXPk3zhxz0XzdIx5 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-YXPk3zhxz0XzdIx5 .cluster text{fill:#333;}#mermaid-svg-YXPk3zhxz0XzdIx5 .cluster span{color:#333;}#mermaid-svg-YXPk3zhxz0XzdIx5 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-YXPk3zhxz0XzdIx5 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-YXPk3zhxz0XzdIx5 rect.text{fill:none;stroke-width:0;}#mermaid-svg-YXPk3zhxz0XzdIx5 .icon-shape,#mermaid-svg-YXPk3zhxz0XzdIx5 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-YXPk3zhxz0XzdIx5 .icon-shape p,#mermaid-svg-YXPk3zhxz0XzdIx5 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-YXPk3zhxz0XzdIx5 .icon-shape .label rect,#mermaid-svg-YXPk3zhxz0XzdIx5 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-YXPk3zhxz0XzdIx5 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-YXPk3zhxz0XzdIx5 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-YXPk3zhxz0XzdIx5 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 是
否
是
否
是
否
否
是
newMigStrategySingleLabeler
获取MIG启用设备
数量==0?
返回empty
检查是否有空MIG设备
有空设备?
返回INVALID标签
检查是否有MIG未启用设备
有混用?
返回INVALID标签
获取所有MIG设备
统计MIG类型
类型数==1?
返回INVALID标签
生成有效MIG标签
resource.name = nvidia.com/gpu
返回标签
Single 策略的有效条件:
- 所有 GPU 都启用了 MIG
- 所有 MIG 设备都不为空(已创建 MIG 实例)
- 所有 MIG 实例属于同一种类型
- 满足以上条件时,MIG 设备使用
nvidia.com/gpu资源名
newMigStrategyMixedLabeler --- Mixed 策略
Mixed 策略更宽松:允许不同 GPU 有不同的 MIG 配置。
go
func newMigStrategyMixedLabeler(manager resource.Manager, config *spec.Config) (Labeler, error) {
deviceInfo := mig.NewDeviceInfo(manager)
// 获取所有 MIG 设备(忽略 migEnabled=true 但无 MIG 实例的设备)
migs, err := deviceInfo.GetAllMigDevices()
// ...
// 统计每种 MIG 类型
resources := make(map[string]migResource)
for _, mig := range migs {
name, err := mig.GetName() // 如 "1g.10gb"
// ...
resource, exists := resources[name]
if !exists {
resource.device = mig
// Mixed 策略下,每种 MIG 类型有独立的资源名
resource.name = spec.ResourceName("nvidia.com/mig-" + name) // 如 "nvidia.com/mig-1g.10gb"
}
resource.count++
resources[name] = resource
}
return newMIGDeviceLabelers(resources, config)
}
Single vs Mixed 对比:
| 特性 | Single | Mixed |
|---|---|---|
| 资源名 | nvidia.com/gpu |
nvidia.com/mig-{profile} |
| 允许混用 MIG/非MIG | ❌ | ✅ |
| 允许多种 MIG 类型 | ❌ | ✅ |
| 允许空 MIG 设备 | ❌ | ✅(忽略) |
| 校验严格度 | 高 | 低 |
#mermaid-svg-27prSp4BvdVObmYs{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-27prSp4BvdVObmYs .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-27prSp4BvdVObmYs .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-27prSp4BvdVObmYs .error-icon{fill:#552222;}#mermaid-svg-27prSp4BvdVObmYs .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-27prSp4BvdVObmYs .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-27prSp4BvdVObmYs .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-27prSp4BvdVObmYs .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-27prSp4BvdVObmYs .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-27prSp4BvdVObmYs .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-27prSp4BvdVObmYs .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-27prSp4BvdVObmYs .marker{fill:#333333;stroke:#333333;}#mermaid-svg-27prSp4BvdVObmYs .marker.cross{stroke:#333333;}#mermaid-svg-27prSp4BvdVObmYs svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-27prSp4BvdVObmYs p{margin:0;}#mermaid-svg-27prSp4BvdVObmYs .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-27prSp4BvdVObmYs .cluster-label text{fill:#333;}#mermaid-svg-27prSp4BvdVObmYs .cluster-label span{color:#333;}#mermaid-svg-27prSp4BvdVObmYs .cluster-label span p{background-color:transparent;}#mermaid-svg-27prSp4BvdVObmYs .label text,#mermaid-svg-27prSp4BvdVObmYs span{fill:#333;color:#333;}#mermaid-svg-27prSp4BvdVObmYs .node rect,#mermaid-svg-27prSp4BvdVObmYs .node circle,#mermaid-svg-27prSp4BvdVObmYs .node ellipse,#mermaid-svg-27prSp4BvdVObmYs .node polygon,#mermaid-svg-27prSp4BvdVObmYs .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-27prSp4BvdVObmYs .rough-node .label text,#mermaid-svg-27prSp4BvdVObmYs .node .label text,#mermaid-svg-27prSp4BvdVObmYs .image-shape .label,#mermaid-svg-27prSp4BvdVObmYs .icon-shape .label{text-anchor:middle;}#mermaid-svg-27prSp4BvdVObmYs .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-27prSp4BvdVObmYs .rough-node .label,#mermaid-svg-27prSp4BvdVObmYs .node .label,#mermaid-svg-27prSp4BvdVObmYs .image-shape .label,#mermaid-svg-27prSp4BvdVObmYs .icon-shape .label{text-align:center;}#mermaid-svg-27prSp4BvdVObmYs .node.clickable{cursor:pointer;}#mermaid-svg-27prSp4BvdVObmYs .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-27prSp4BvdVObmYs .arrowheadPath{fill:#333333;}#mermaid-svg-27prSp4BvdVObmYs .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-27prSp4BvdVObmYs .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-27prSp4BvdVObmYs .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-27prSp4BvdVObmYs .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-27prSp4BvdVObmYs .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-27prSp4BvdVObmYs .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-27prSp4BvdVObmYs .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-27prSp4BvdVObmYs .cluster text{fill:#333;}#mermaid-svg-27prSp4BvdVObmYs .cluster span{color:#333;}#mermaid-svg-27prSp4BvdVObmYs 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-27prSp4BvdVObmYs .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-27prSp4BvdVObmYs rect.text{fill:none;stroke-width:0;}#mermaid-svg-27prSp4BvdVObmYs .icon-shape,#mermaid-svg-27prSp4BvdVObmYs .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-27prSp4BvdVObmYs .icon-shape p,#mermaid-svg-27prSp4BvdVObmYs .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-27prSp4BvdVObmYs .icon-shape .label rect,#mermaid-svg-27prSp4BvdVObmYs .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-27prSp4BvdVObmYs .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-27prSp4BvdVObmYs .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-27prSp4BvdVObmYs :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Mixed 策略
各GPU可独立配置
不同MIG类型独立计数
资源名: nvidia.com/mig-{profile}
标签: nvidia.com/mig-1g.10gb.product=MODEL-MIG-1g.10gb
Single 策略
所有GPU启用MIG
所有MIG实例同类型
资源名: nvidia.com/gpu
标签: nvidia.com/gpu.product=MODEL-MIG-PROFILE
newInvalidMigStrategyLabeler --- 无效配置标签
go
func newInvalidMigStrategyLabeler(device resource.Device, reason string) (Labeler, error) {
klog.Warningf("Invalid configuration detected for mig-strategy=single: %v", reason)
model, err := device.GetName()
// ...
rl := resourceLabeler{
resourceName: "nvidia.com/gpu",
}
// 生成 INVALID 标签,标记配置问题
labels := rl.productLabel(model, "MIG", "INVALID")
rl.updateLabel(labels, "count", 0) // 数量=0
rl.updateLabel(labels, "replicas", 0) // 副本=0
rl.updateLabel(labels, "sharing-strategy", "")
rl.updateLabel(labels, "memory", 0) // 显存=0
return labels, nil
}
设计意图 :不直接报错,而是生成 INVALID 标签让集群管理员通过标签发现问题。
3.2.7 resource.go --- 资源标签生成器
resourceLabeler 结构体
go
type resourceLabeler struct {
resourceName spec.ResourceName // 资源全名,如 "nvidia.com/gpu"
sharing *spec.Sharing // 共享配置(nil表示禁用共享)
}
标签键生成机制
go
// key 生成标签键:<resourceName>.<suffix>
func (rl resourceLabeler) key(suffix string) string {
return string(rl.resourceName) + "." + suffix
}
// updateLabel 更新标签
func (rl resourceLabeler) updateLabel(labels Labels, suffix string, value interface{}) {
key := rl.key(suffix)
labels[key] = fmt.Sprintf("%v", value)
}
示例:
resourceName = "nvidia.com/gpu",suffix = "product"→key = "nvidia.com/gpu.product"resourceName = "nvidia.com/mig-1g.10gb",suffix = "count"→key = "nvidia.com/mig-1g.10gb.count"
NewGPUResourceLabeler --- 全 GPU 资源标签
go
func NewGPUResourceLabeler(config *spec.Config, device resource.Device, count int) (Labeler, error) {
if count == 0 {
return empty{}, nil
}
model, err := device.GetName() // GPU 型号,如 "NVIDIA-A100-SXM4-40GB"
// ...
totalMemoryMiB, err := device.GetTotalMemoryMiB() // 总显存(MB)
// ...
resourceLabeler := newResourceLabeler(fullGPUResourceName, config) // "nvidia.com/gpu"
// 生成架构标签
architectureLabels, err := newArchitectureLabels(resourceLabeler, device)
// ...
// 显存标签
memoryLabeler := (Labeler)(&empty{})
if totalMemoryMiB != 0 {
memoryLabeler = resourceLabeler.single("memory", totalMemoryMiB)
}
// 合并:基础标签 + 显存标签 + 架构标签
labelers := Merge(
resourceLabeler.baseLabeler(count, model), // product/count/replicas/sharing-strategy
memoryLabeler, // memory
architectureLabels, // family/compute.major/compute.minor
)
return labelers, nil
}
生成的标签示例:
nvidia.com/gpu.product=NVIDIA-A100-SXM4-40GB
nvidia.com/gpu.count=8
nvidia.com/gpu.replicas=1
nvidia.com/gpu.sharing-strategy=none
nvidia.com/gpu.memory=40960
nvidia.com/gpu.family=ampere
nvidia.com/gpu.compute.major=8
nvidia.com/gpu.compute.minor=0
NewMIGResourceLabeler --- MIG 设备资源标签
go
func NewMIGResourceLabeler(resourceName spec.ResourceName, config *spec.Config, device resource.Device, count int) (Labeler, error) {
if count == 0 {
return empty{}, nil
}
// MIG 设备的"型号"取自其父 GPU 的型号
parent, err := device.GetDeviceHandleFromMigDeviceHandle()
// ...
model, err := parent.GetName()
// ...
// MIG profile 名称,如 "1g.10gb"
migProfile, err := device.GetName()
// ...
resourceLabeler := newResourceLabeler(resourceName, config)
// MIG 属性标签
attributeLabels, err := newMigAttributeLabels(resourceLabeler, device)
// ...
// 合并:基础标签(含MIG profile) + 属性标签
labelers := Merge(
resourceLabeler.baseLabeler(count, model, "MIG", migProfile),
attributeLabels,
)
return labelers, nil
}
生成的标签示例(mixed 策略):
nvidia.com/mig-1g.10gb.product=NVIDIA-A100-SXM4-40GB-MIG-1g.10gb
nvidia.com/mig-1g.10gb.count=4
nvidia.com/mig-1g.10gb.replicas=1
nvidia.com/mig-1g.10gb.sharing-strategy=none
nvidia.com/mig-1g.10gb.memory=1024
nvidia.com/mig-1g.10gb.engines=1
baseLabeler --- 基础标签生成
go
func (rl resourceLabeler) baseLabeler(count int, parts ...string) Labeler {
replicas := rl.getReplicas()
strategy := spec.SharingStrategyNone
if rl.sharing != nil && replicas > 1 {
strategy = rl.sharing.SharingStrategy()
}
rawLabels := map[string]interface{}{
"product": rl.getProductName(parts...), // 产品标识
"count": count, // 设备数量
"replicas": replicas, // 共享副本数
"sharing-strategy": strategy, // 共享策略
}
labels := make(Labels)
for k, v := range rawLabels {
labels[rl.key(k)] = fmt.Sprintf("%v", v)
}
return labels
}
getProductName --- 产品名生成
go
func (rl resourceLabeler) getProductName(parts ...string) string {
var strippedParts []string
for _, p := range parts {
if p != "" {
sanitisedPart := sanitise(p) // 清理特殊字符
strippedParts = append(strippedParts, sanitisedPart)
}
}
if len(strippedParts) == 0 {
return ""
}
// 如果是共享但未重命名,追加 "SHARED" 标记
if rl.isShared() && !rl.isRenamed() {
strippedParts = append(strippedParts, "SHARED")
}
return strings.Join(strippedParts, "-")
}
产品名拼接规则:
- 全 GPU:
MODEL(如NVIDIA-A100-SXM4-40GB) - MIG 设备:
MODEL-MIG-PROFILE(如NVIDIA-A100-SXM4-40GB-MIG-1g.10gb) - 共享 GPU(未重命名):
MODEL-SHARED(如NVIDIA-A100-SXM4-40GB-SHARED) - 无效 MIG 配置:
MODEL-MIG-INVALID
#mermaid-svg-bF1ceR5oYVj27igy{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-bF1ceR5oYVj27igy .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-bF1ceR5oYVj27igy .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-bF1ceR5oYVj27igy .error-icon{fill:#552222;}#mermaid-svg-bF1ceR5oYVj27igy .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-bF1ceR5oYVj27igy .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-bF1ceR5oYVj27igy .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-bF1ceR5oYVj27igy .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-bF1ceR5oYVj27igy .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-bF1ceR5oYVj27igy .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-bF1ceR5oYVj27igy .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-bF1ceR5oYVj27igy .marker{fill:#333333;stroke:#333333;}#mermaid-svg-bF1ceR5oYVj27igy .marker.cross{stroke:#333333;}#mermaid-svg-bF1ceR5oYVj27igy svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-bF1ceR5oYVj27igy p{margin:0;}#mermaid-svg-bF1ceR5oYVj27igy .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-bF1ceR5oYVj27igy .cluster-label text{fill:#333;}#mermaid-svg-bF1ceR5oYVj27igy .cluster-label span{color:#333;}#mermaid-svg-bF1ceR5oYVj27igy .cluster-label span p{background-color:transparent;}#mermaid-svg-bF1ceR5oYVj27igy .label text,#mermaid-svg-bF1ceR5oYVj27igy span{fill:#333;color:#333;}#mermaid-svg-bF1ceR5oYVj27igy .node rect,#mermaid-svg-bF1ceR5oYVj27igy .node circle,#mermaid-svg-bF1ceR5oYVj27igy .node ellipse,#mermaid-svg-bF1ceR5oYVj27igy .node polygon,#mermaid-svg-bF1ceR5oYVj27igy .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-bF1ceR5oYVj27igy .rough-node .label text,#mermaid-svg-bF1ceR5oYVj27igy .node .label text,#mermaid-svg-bF1ceR5oYVj27igy .image-shape .label,#mermaid-svg-bF1ceR5oYVj27igy .icon-shape .label{text-anchor:middle;}#mermaid-svg-bF1ceR5oYVj27igy .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-bF1ceR5oYVj27igy .rough-node .label,#mermaid-svg-bF1ceR5oYVj27igy .node .label,#mermaid-svg-bF1ceR5oYVj27igy .image-shape .label,#mermaid-svg-bF1ceR5oYVj27igy .icon-shape .label{text-align:center;}#mermaid-svg-bF1ceR5oYVj27igy .node.clickable{cursor:pointer;}#mermaid-svg-bF1ceR5oYVj27igy .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-bF1ceR5oYVj27igy .arrowheadPath{fill:#333333;}#mermaid-svg-bF1ceR5oYVj27igy .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-bF1ceR5oYVj27igy .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-bF1ceR5oYVj27igy .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-bF1ceR5oYVj27igy .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-bF1ceR5oYVj27igy .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-bF1ceR5oYVj27igy .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-bF1ceR5oYVj27igy .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-bF1ceR5oYVj27igy .cluster text{fill:#333;}#mermaid-svg-bF1ceR5oYVj27igy .cluster span{color:#333;}#mermaid-svg-bF1ceR5oYVj27igy 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-bF1ceR5oYVj27igy .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-bF1ceR5oYVj27igy rect.text{fill:none;stroke-width:0;}#mermaid-svg-bF1ceR5oYVj27igy .icon-shape,#mermaid-svg-bF1ceR5oYVj27igy .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-bF1ceR5oYVj27igy .icon-shape p,#mermaid-svg-bF1ceR5oYVj27igy .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-bF1ceR5oYVj27igy .icon-shape .label rect,#mermaid-svg-bF1ceR5oYVj27igy .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-bF1ceR5oYVj27igy .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-bF1ceR5oYVj27igy .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-bF1ceR5oYVj27igy :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 是
否
是
否
getProductName parts...
清理每个part的非法字符
parts为空?
返回空字符串
isShared && !isRenamed?
追加 SHARED
直接使用
用 - 连接各部分
返回产品名
共享相关方法链
go
func (rl resourceLabeler) getReplicas() int {
if rl.sharingDisabled() {
return 0 // 禁用共享时 replicas=0
} else if r := rl.replicationInfo(); r != nil && r.Replicas > 0 {
return r.Replicas // 配置的副本数
}
return 1 // 默认1(不共享)
}
func (rl resourceLabeler) sharingDisabled() bool {
return rl.sharing == nil // nil config → 禁用
}
func (rl resourceLabeler) isShared() bool {
if r := rl.replicationInfo(); r != nil && r.Replicas > 1 {
return true // replicas>1 → 共享
}
return false
}
func (rl resourceLabeler) isRenamed() bool {
if r := rl.replicationInfo(); r != nil && r.Rename != "" {
return true // 有重命名 → 共享资源被重命名
}
return false
}
// replicationInfo 在配置中查找当前资源的复制信息
func (rl resourceLabeler) replicationInfo() *spec.ReplicatedResource {
if rl.sharingDisabled() {
return nil
}
for _, r := range rl.sharing.ReplicatedResources().Resources {
if r.Name == rl.resourceName {
return &r
}
}
return nil
}
#mermaid-svg-wxegPr2c7CpwdXhP{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-wxegPr2c7CpwdXhP .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-wxegPr2c7CpwdXhP .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-wxegPr2c7CpwdXhP .error-icon{fill:#552222;}#mermaid-svg-wxegPr2c7CpwdXhP .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-wxegPr2c7CpwdXhP .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-wxegPr2c7CpwdXhP .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-wxegPr2c7CpwdXhP .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-wxegPr2c7CpwdXhP .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-wxegPr2c7CpwdXhP .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-wxegPr2c7CpwdXhP .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-wxegPr2c7CpwdXhP .marker{fill:#333333;stroke:#333333;}#mermaid-svg-wxegPr2c7CpwdXhP .marker.cross{stroke:#333333;}#mermaid-svg-wxegPr2c7CpwdXhP svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-wxegPr2c7CpwdXhP p{margin:0;}#mermaid-svg-wxegPr2c7CpwdXhP .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-wxegPr2c7CpwdXhP .cluster-label text{fill:#333;}#mermaid-svg-wxegPr2c7CpwdXhP .cluster-label span{color:#333;}#mermaid-svg-wxegPr2c7CpwdXhP .cluster-label span p{background-color:transparent;}#mermaid-svg-wxegPr2c7CpwdXhP .label text,#mermaid-svg-wxegPr2c7CpwdXhP span{fill:#333;color:#333;}#mermaid-svg-wxegPr2c7CpwdXhP .node rect,#mermaid-svg-wxegPr2c7CpwdXhP .node circle,#mermaid-svg-wxegPr2c7CpwdXhP .node ellipse,#mermaid-svg-wxegPr2c7CpwdXhP .node polygon,#mermaid-svg-wxegPr2c7CpwdXhP .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-wxegPr2c7CpwdXhP .rough-node .label text,#mermaid-svg-wxegPr2c7CpwdXhP .node .label text,#mermaid-svg-wxegPr2c7CpwdXhP .image-shape .label,#mermaid-svg-wxegPr2c7CpwdXhP .icon-shape .label{text-anchor:middle;}#mermaid-svg-wxegPr2c7CpwdXhP .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-wxegPr2c7CpwdXhP .rough-node .label,#mermaid-svg-wxegPr2c7CpwdXhP .node .label,#mermaid-svg-wxegPr2c7CpwdXhP .image-shape .label,#mermaid-svg-wxegPr2c7CpwdXhP .icon-shape .label{text-align:center;}#mermaid-svg-wxegPr2c7CpwdXhP .node.clickable{cursor:pointer;}#mermaid-svg-wxegPr2c7CpwdXhP .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-wxegPr2c7CpwdXhP .arrowheadPath{fill:#333333;}#mermaid-svg-wxegPr2c7CpwdXhP .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-wxegPr2c7CpwdXhP .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-wxegPr2c7CpwdXhP .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-wxegPr2c7CpwdXhP .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-wxegPr2c7CpwdXhP .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-wxegPr2c7CpwdXhP .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-wxegPr2c7CpwdXhP .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-wxegPr2c7CpwdXhP .cluster text{fill:#333;}#mermaid-svg-wxegPr2c7CpwdXhP .cluster span{color:#333;}#mermaid-svg-wxegPr2c7CpwdXhP 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-wxegPr2c7CpwdXhP .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-wxegPr2c7CpwdXhP rect.text{fill:none;stroke-width:0;}#mermaid-svg-wxegPr2c7CpwdXhP .icon-shape,#mermaid-svg-wxegPr2c7CpwdXhP .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-wxegPr2c7CpwdXhP .icon-shape p,#mermaid-svg-wxegPr2c7CpwdXhP .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-wxegPr2c7CpwdXhP .icon-shape .label rect,#mermaid-svg-wxegPr2c7CpwdXhP .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-wxegPr2c7CpwdXhP .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-wxegPr2c7CpwdXhP .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-wxegPr2c7CpwdXhP :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 是
否
是
否
是
否
是
否
resourceLabeler
sharing == nil?
sharingDisabled = true
replicationInfo: 查找匹配资源配置
找到?
Replicas > 1?
replicas=1, isShared=false
isShared=true, replicas=配置值
replicas=0, isShared=false
Rename != ?
isRenamed=true, 产品名不加SHARED
isRenamed=false, 产品名加SHARED
newArchitectureLabels --- GPU 架构标签
go
func newArchitectureLabels(rl resourceLabeler, device resource.Device) (Labels, error) {
computeMajor, computeMinor, err := device.GetCudaComputeCapability()
// ...
if computeMajor == 0 {
return make(Labels), nil // 无效架构,返回空
}
family := getArchFamily(computeMajor, computeMinor)
labels := rl.labels(map[string]interface{}{
"family": family, // 如 "ampere"
"compute.major": computeMajor, // 如 8
"compute.minor": computeMinor, // 如 0
})
return labels, nil
}
getArchFamily --- CUDA 计算能力 → 架构族映射
go
func getArchFamily(computeMajor, computeMinor int) string {
switch computeMajor {
case 1: return "tesla"
case 2: return "fermi"
case 3: return "kepler"
case 5: return "maxwell"
case 6: return "pascal"
case 7:
if computeMinor < 5 { return "volta" }
return "turing"
case 8:
if computeMinor < 9 { return "ampere" }
return "ada-lovelace"
case 9: return "hopper"
case 10, 12: return "blackwell" // Blackwell 分为 10.0 和 12.0
}
return "undefined"
}
#mermaid-svg-nYfjdmeqwHfBddRQ{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-nYfjdmeqwHfBddRQ .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-nYfjdmeqwHfBddRQ .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-nYfjdmeqwHfBddRQ .error-icon{fill:#552222;}#mermaid-svg-nYfjdmeqwHfBddRQ .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-nYfjdmeqwHfBddRQ .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-nYfjdmeqwHfBddRQ .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-nYfjdmeqwHfBddRQ .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-nYfjdmeqwHfBddRQ .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-nYfjdmeqwHfBddRQ .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-nYfjdmeqwHfBddRQ .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-nYfjdmeqwHfBddRQ .marker{fill:#333333;stroke:#333333;}#mermaid-svg-nYfjdmeqwHfBddRQ .marker.cross{stroke:#333333;}#mermaid-svg-nYfjdmeqwHfBddRQ svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-nYfjdmeqwHfBddRQ p{margin:0;}#mermaid-svg-nYfjdmeqwHfBddRQ .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-nYfjdmeqwHfBddRQ .cluster-label text{fill:#333;}#mermaid-svg-nYfjdmeqwHfBddRQ .cluster-label span{color:#333;}#mermaid-svg-nYfjdmeqwHfBddRQ .cluster-label span p{background-color:transparent;}#mermaid-svg-nYfjdmeqwHfBddRQ .label text,#mermaid-svg-nYfjdmeqwHfBddRQ span{fill:#333;color:#333;}#mermaid-svg-nYfjdmeqwHfBddRQ .node rect,#mermaid-svg-nYfjdmeqwHfBddRQ .node circle,#mermaid-svg-nYfjdmeqwHfBddRQ .node ellipse,#mermaid-svg-nYfjdmeqwHfBddRQ .node polygon,#mermaid-svg-nYfjdmeqwHfBddRQ .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-nYfjdmeqwHfBddRQ .rough-node .label text,#mermaid-svg-nYfjdmeqwHfBddRQ .node .label text,#mermaid-svg-nYfjdmeqwHfBddRQ .image-shape .label,#mermaid-svg-nYfjdmeqwHfBddRQ .icon-shape .label{text-anchor:middle;}#mermaid-svg-nYfjdmeqwHfBddRQ .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-nYfjdmeqwHfBddRQ .rough-node .label,#mermaid-svg-nYfjdmeqwHfBddRQ .node .label,#mermaid-svg-nYfjdmeqwHfBddRQ .image-shape .label,#mermaid-svg-nYfjdmeqwHfBddRQ .icon-shape .label{text-align:center;}#mermaid-svg-nYfjdmeqwHfBddRQ .node.clickable{cursor:pointer;}#mermaid-svg-nYfjdmeqwHfBddRQ .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-nYfjdmeqwHfBddRQ .arrowheadPath{fill:#333333;}#mermaid-svg-nYfjdmeqwHfBddRQ .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-nYfjdmeqwHfBddRQ .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-nYfjdmeqwHfBddRQ .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-nYfjdmeqwHfBddRQ .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-nYfjdmeqwHfBddRQ .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-nYfjdmeqwHfBddRQ .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-nYfjdmeqwHfBddRQ .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-nYfjdmeqwHfBddRQ .cluster text{fill:#333;}#mermaid-svg-nYfjdmeqwHfBddRQ .cluster span{color:#333;}#mermaid-svg-nYfjdmeqwHfBddRQ 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-nYfjdmeqwHfBddRQ .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-nYfjdmeqwHfBddRQ rect.text{fill:none;stroke-width:0;}#mermaid-svg-nYfjdmeqwHfBddRQ .icon-shape,#mermaid-svg-nYfjdmeqwHfBddRQ .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-nYfjdmeqwHfBddRQ .icon-shape p,#mermaid-svg-nYfjdmeqwHfBddRQ .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-nYfjdmeqwHfBddRQ .icon-shape .label rect,#mermaid-svg-nYfjdmeqwHfBddRQ .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-nYfjdmeqwHfBddRQ .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-nYfjdmeqwHfBddRQ .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-nYfjdmeqwHfBddRQ :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} CUDA Compute Capability → 架构族
1.x → tesla
2.x → fermi
3.x → kepler
5.x → maxwell
6.x → pascal
7.0-7.4 → volta
7.5+ → turing
8.0-8.8 → ampere
8.9 → ada-lovelace
9.x → hopper
10.0 → blackwell
12.0 → blackwell
sanitise --- 字符串清理
go
func sanitise(input string) string {
var sanitised string
// 移除非 [A-Za-z0-9-_. ] 的字符
re := regexp.MustCompile("[^A-Za-z0-9-_. ]")
input = re.ReplaceAllString(input, "")
// 移除冗余空格,用 - 替换
sanitised = strings.Join(strings.Fields(input), "-")
return sanitised
}
作用 :确保标签值符合 Kubernetes 标签值规范。例如 NVIDIA A100 SXM4 40GB → NVIDIA-A100-SXM4-40GB。
3.2.8 strategy.go --- MIG 策略标签
go
func migStrategyLabeler(strategy string) Labeler {
if strategy == MigStrategyNone {
return empty{} // none 策略不生成 mig.strategy 标签
}
return Labels{
"nvidia.com/mig.strategy": strategy, // "single" 或 "mixed"
}
}
设计:简洁的工厂函数。none 策略不输出标签(减少噪音),single/mixed 输出对应标签值。
3.2.9 machine-type.go --- 机器类型标签
go
func newMachineTypeLabeler(machineTypePath string) (Labeler, error) {
machineType, err := getMachineType(machineTypePath)
if err != nil {
klog.Warningf("Error getting machine type from %v: %v", machineTypePath, err)
machineType = machineTypeUnknown // 降级为 "unknown"
}
l := Labels{
"nvidia.com/gpu.machine": sanitise(machineType),
}
return l, nil
}
func getMachineType(path string) (string, error) {
if path == "" {
return machineTypeUnknown, nil // 空路径 → unknown
}
data, err := os.ReadFile(path) // 通常读取 /sys/class/dmi/id/product_name
if err != nil {
return "", fmt.Errorf("could not open machine type file: %v", err)
}
return strings.TrimSpace(string(data)), nil
}
数据源 :通过读取 sysfs DMI 信息获取机器型号(如 PowerEdge-R750、ThinkPad-P15等)。错误时降级为 unknown 而非报错。
3.2.10 timestamp.go --- 时间戳标签
go
func NewTimestampLabeler(config *spec.Config) Labeler {
if *config.Flags.GFD.NoTimestamp {
return empty{} // 禁用时间戳
}
return Labels{
"nvidia.com/gfd.timestamp": fmt.Sprintf("%d", time.Now().Unix()), // Unix 时间戳
}
}
用途 :标记标签生成时间,用于检测标签是否过期。NFD Worker 可以据此判断是否需要重新发现。由 GFD 主程序单独调用,不在 NewLabelers 中。
3.2.11 vgpu.go --- vGPU 标签器
go
type vgpuLabeler struct {
lib vgpu.Interface
}
func NewVGPULabeler(vgpu vgpu.Interface) Labeler {
return vgpuLabeler{lib: vgpu}
}
func (manager vgpuLabeler) Labels() (Labels, error) {
devices, err := manager.lib.Devices()
if err != nil {
klog.ErrorS(err, "unable to get vGPU devices")
return nil, nil // 注意:错误时返回 nil, nil 而非 error
}
labels := Labels{
"nvidia.com/vgpu.present": strconv.FormatBool(len(devices) > 0), // "true"/"false"
}
for _, device := range devices {
info, err := device.GetInfo()
if err != nil {
return nil, fmt.Errorf("error getting vGPU device info: %v", err)
}
// 每个 vGPU 设备的主机驱动信息
labels["nvidia.com/vgpu.host-driver-version"] = info.HostDriverVersion
labels["nvidia.com/vgpu.host-driver-branch"] = info.HostDriverBranch
}
return labels, nil
}
注意点 :Devices() 出错时返回 nil, nil(吞掉错误),这是有意设计------vGPU 信息不影响核心功能。
生成的标签:
nvidia.com/vgpu.present=true
nvidia.com/vgpu.host-driver-version=535.129.03
nvidia.com/vgpu.host-driver-branch=r535
3.2.12 imex.go --- IMEX Fabric 拓扑标签
go
func newImexLabeler(config *spec.Config, devices []resource.Device) (Labeler, error) {
clusterUUID, cliqueID, err := getFabricIDs(devices)
if err != nil {
return nil, err
}
// 任一 ID 为空则不生成标签
if clusterUUID == "" || cliqueID == "" {
return empty{}, nil
}
labels := Labels{
"nvidia.com/gpu.clique": strings.Join([]string{clusterUUID, cliqueID}, "."),
// 如 "cluster-uuid.clique-id"
}
return labels, nil
}
getFabricIDs --- Fabric ID 采集与校验
go
func getFabricIDs(devices []resource.Device) (string, string, error) {
uniqueClusterUUIDs := make(map[string][]int) // UUID → 设备索引列表
uniqueCliqueIDs := make(map[string][]int) // CliqueID → 设备索引列表
for i, device := range devices {
// 检查设备是否连接到 Fabric
isFabricAttached, err := device.IsFabricAttached()
if err != nil {
return "", "", fmt.Errorf("error checking imex capability: %v", err)
}
if !isFabricAttached {
continue // 跳过非 Fabric 设备
}
clusterUUID, cliqueID, err := device.GetFabricIDs()
if err != nil {
return "", "", fmt.Errorf("error getting fabric IDs: %w", err)
}
uniqueClusterUUIDs[clusterUUID] = append(uniqueClusterUUIDs[clusterUUID], i)
uniqueCliqueIDs[cliqueID] = append(uniqueCliqueIDs[cliqueID], i)
}
// Cluster UUID 必须唯一
if len(uniqueClusterUUIDs) > 1 {
klog.Warningf("Cluster UUIDs are non-unique: %v", uniqueClusterUUIDs)
return "", "", nil // 不唯一 → 返回空
}
// Clique ID 必须唯一
if len(uniqueCliqueIDs) > 1 {
klog.Warningf("Clique IDs are non-unique: %v", uniqueCliqueIDs)
return "", "", nil
}
// 返回唯一的 UUID 和 CliqueID
for clusterUUID := range uniqueClusterUUIDs {
for cliqueID := range uniqueCliqueIDs {
return clusterUUID, cliqueID, nil
}
}
return "", "", nil
}
业务逻辑:
- 遍历所有设备,检查是否连接到 NVLink Fabric
- 收集所有 Fabric 设备的 Cluster UUID 和 Clique ID
- 约束:Cluster UUID 必须唯一(所有 GPU 属于同一集群),Clique ID 必须唯一(所有 GPU 属于同一 Clique)
- 不满足约束时返回空(不生成标签,而非报错)
- 标签格式:
nvidia.com/gpu.clique=<cluster-uuid>.<clique-id>
#mermaid-svg-fuaqV4vyVT8rMcTu{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-fuaqV4vyVT8rMcTu .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-fuaqV4vyVT8rMcTu .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-fuaqV4vyVT8rMcTu .error-icon{fill:#552222;}#mermaid-svg-fuaqV4vyVT8rMcTu .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-fuaqV4vyVT8rMcTu .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-fuaqV4vyVT8rMcTu .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-fuaqV4vyVT8rMcTu .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-fuaqV4vyVT8rMcTu .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-fuaqV4vyVT8rMcTu .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-fuaqV4vyVT8rMcTu .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-fuaqV4vyVT8rMcTu .marker{fill:#333333;stroke:#333333;}#mermaid-svg-fuaqV4vyVT8rMcTu .marker.cross{stroke:#333333;}#mermaid-svg-fuaqV4vyVT8rMcTu svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-fuaqV4vyVT8rMcTu p{margin:0;}#mermaid-svg-fuaqV4vyVT8rMcTu .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-fuaqV4vyVT8rMcTu .cluster-label text{fill:#333;}#mermaid-svg-fuaqV4vyVT8rMcTu .cluster-label span{color:#333;}#mermaid-svg-fuaqV4vyVT8rMcTu .cluster-label span p{background-color:transparent;}#mermaid-svg-fuaqV4vyVT8rMcTu .label text,#mermaid-svg-fuaqV4vyVT8rMcTu span{fill:#333;color:#333;}#mermaid-svg-fuaqV4vyVT8rMcTu .node rect,#mermaid-svg-fuaqV4vyVT8rMcTu .node circle,#mermaid-svg-fuaqV4vyVT8rMcTu .node ellipse,#mermaid-svg-fuaqV4vyVT8rMcTu .node polygon,#mermaid-svg-fuaqV4vyVT8rMcTu .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-fuaqV4vyVT8rMcTu .rough-node .label text,#mermaid-svg-fuaqV4vyVT8rMcTu .node .label text,#mermaid-svg-fuaqV4vyVT8rMcTu .image-shape .label,#mermaid-svg-fuaqV4vyVT8rMcTu .icon-shape .label{text-anchor:middle;}#mermaid-svg-fuaqV4vyVT8rMcTu .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-fuaqV4vyVT8rMcTu .rough-node .label,#mermaid-svg-fuaqV4vyVT8rMcTu .node .label,#mermaid-svg-fuaqV4vyVT8rMcTu .image-shape .label,#mermaid-svg-fuaqV4vyVT8rMcTu .icon-shape .label{text-align:center;}#mermaid-svg-fuaqV4vyVT8rMcTu .node.clickable{cursor:pointer;}#mermaid-svg-fuaqV4vyVT8rMcTu .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-fuaqV4vyVT8rMcTu .arrowheadPath{fill:#333333;}#mermaid-svg-fuaqV4vyVT8rMcTu .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-fuaqV4vyVT8rMcTu .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-fuaqV4vyVT8rMcTu .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-fuaqV4vyVT8rMcTu .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-fuaqV4vyVT8rMcTu .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-fuaqV4vyVT8rMcTu .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-fuaqV4vyVT8rMcTu .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-fuaqV4vyVT8rMcTu .cluster text{fill:#333;}#mermaid-svg-fuaqV4vyVT8rMcTu .cluster span{color:#333;}#mermaid-svg-fuaqV4vyVT8rMcTu 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-fuaqV4vyVT8rMcTu .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-fuaqV4vyVT8rMcTu rect.text{fill:none;stroke-width:0;}#mermaid-svg-fuaqV4vyVT8rMcTu .icon-shape,#mermaid-svg-fuaqV4vyVT8rMcTu .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-fuaqV4vyVT8rMcTu .icon-shape p,#mermaid-svg-fuaqV4vyVT8rMcTu .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-fuaqV4vyVT8rMcTu .icon-shape .label rect,#mermaid-svg-fuaqV4vyVT8rMcTu .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-fuaqV4vyVT8rMcTu .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-fuaqV4vyVT8rMcTu .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-fuaqV4vyVT8rMcTu :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 否
是
否
否
是
是
遍历所有GPU设备
设备连接Fabric?
跳过
获取Cluster UUID + Clique ID
收集到uniqueClusterUUIDs
收集到uniqueCliqueIDs
UUID唯一?
CliqueID唯一?
Warning + 返回空
Warning + 返回空
生成标签: nvidia.com/gpu.clique=uuid.clique
3.2.13 output.go --- 输出器
Outputer 接口
go
type Outputer interface {
Output(Labels) error
}
NewOutputer --- 输出器工厂
go
func NewOutputer(config *spec.Config, nodeConfig flags.NodeConfig, clientSets flags.ClientSets) (Outputer, error) {
// 传统模式:输出到文件
if config.Flags.UseNodeFeatureAPI == nil || !*config.Flags.UseNodeFeatureAPI {
return ToFile(*config.Flags.GFD.OutputFile), nil
}
// NFD API 模式:通过 NodeFeature CR 输出
if nodeConfig.Name == "" {
return nil, fmt.Errorf("required flag node-name not set")
}
if nodeConfig.Namespace == "" {
return nil, fmt.Errorf("required flag namespace not set")
}
// 获取 OwnerReferences(用于垃圾回收)
ownerRefs, err := getOwnerReferences(context.TODO(), clientSets.Core, nodeConfig.Namespace, nodeConfig.PodName)
if err != nil {
klog.Warningf("Failed to resolve owner references: %v", err)
// 降级:无 OwnerReferences 继续
}
o := nodeFeatureObject{
nodeConfig: nodeConfig,
nfdClientset: clientSets.NFD,
ownerRefs: ownerRefs,
}
return &o, nil
}
#mermaid-svg-UVJQ5E2sGDfLMmn3{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-UVJQ5E2sGDfLMmn3 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-UVJQ5E2sGDfLMmn3 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-UVJQ5E2sGDfLMmn3 .error-icon{fill:#552222;}#mermaid-svg-UVJQ5E2sGDfLMmn3 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-UVJQ5E2sGDfLMmn3 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-UVJQ5E2sGDfLMmn3 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-UVJQ5E2sGDfLMmn3 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-UVJQ5E2sGDfLMmn3 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-UVJQ5E2sGDfLMmn3 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-UVJQ5E2sGDfLMmn3 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-UVJQ5E2sGDfLMmn3 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-UVJQ5E2sGDfLMmn3 .marker.cross{stroke:#333333;}#mermaid-svg-UVJQ5E2sGDfLMmn3 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-UVJQ5E2sGDfLMmn3 p{margin:0;}#mermaid-svg-UVJQ5E2sGDfLMmn3 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-UVJQ5E2sGDfLMmn3 .cluster-label text{fill:#333;}#mermaid-svg-UVJQ5E2sGDfLMmn3 .cluster-label span{color:#333;}#mermaid-svg-UVJQ5E2sGDfLMmn3 .cluster-label span p{background-color:transparent;}#mermaid-svg-UVJQ5E2sGDfLMmn3 .label text,#mermaid-svg-UVJQ5E2sGDfLMmn3 span{fill:#333;color:#333;}#mermaid-svg-UVJQ5E2sGDfLMmn3 .node rect,#mermaid-svg-UVJQ5E2sGDfLMmn3 .node circle,#mermaid-svg-UVJQ5E2sGDfLMmn3 .node ellipse,#mermaid-svg-UVJQ5E2sGDfLMmn3 .node polygon,#mermaid-svg-UVJQ5E2sGDfLMmn3 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-UVJQ5E2sGDfLMmn3 .rough-node .label text,#mermaid-svg-UVJQ5E2sGDfLMmn3 .node .label text,#mermaid-svg-UVJQ5E2sGDfLMmn3 .image-shape .label,#mermaid-svg-UVJQ5E2sGDfLMmn3 .icon-shape .label{text-anchor:middle;}#mermaid-svg-UVJQ5E2sGDfLMmn3 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-UVJQ5E2sGDfLMmn3 .rough-node .label,#mermaid-svg-UVJQ5E2sGDfLMmn3 .node .label,#mermaid-svg-UVJQ5E2sGDfLMmn3 .image-shape .label,#mermaid-svg-UVJQ5E2sGDfLMmn3 .icon-shape .label{text-align:center;}#mermaid-svg-UVJQ5E2sGDfLMmn3 .node.clickable{cursor:pointer;}#mermaid-svg-UVJQ5E2sGDfLMmn3 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-UVJQ5E2sGDfLMmn3 .arrowheadPath{fill:#333333;}#mermaid-svg-UVJQ5E2sGDfLMmn3 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-UVJQ5E2sGDfLMmn3 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-UVJQ5E2sGDfLMmn3 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-UVJQ5E2sGDfLMmn3 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-UVJQ5E2sGDfLMmn3 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-UVJQ5E2sGDfLMmn3 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-UVJQ5E2sGDfLMmn3 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-UVJQ5E2sGDfLMmn3 .cluster text{fill:#333;}#mermaid-svg-UVJQ5E2sGDfLMmn3 .cluster span{color:#333;}#mermaid-svg-UVJQ5E2sGDfLMmn3 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-UVJQ5E2sGDfLMmn3 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-UVJQ5E2sGDfLMmn3 rect.text{fill:none;stroke-width:0;}#mermaid-svg-UVJQ5E2sGDfLMmn3 .icon-shape,#mermaid-svg-UVJQ5E2sGDfLMmn3 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-UVJQ5E2sGDfLMmn3 .icon-shape p,#mermaid-svg-UVJQ5E2sGDfLMmn3 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-UVJQ5E2sGDfLMmn3 .icon-shape .label rect,#mermaid-svg-UVJQ5E2sGDfLMmn3 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-UVJQ5E2sGDfLMmn3 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-UVJQ5E2sGDfLMmn3 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-UVJQ5E2sGDfLMmn3 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} nil 或 false
true
否
是
否
是
NewOutputer
UseNodeFeatureAPI?
ToFile: 输出到文件
node-name有效?
返回错误
namespace有效?
返回错误
getOwnerReferences
nodeFeatureObject
通过NodeFeature CR输出
toFile --- 文件输出
go
type toFile string // 文件路径
func (path *toFile) Output(labels Labels) error {
klog.Infof("Writing labels to output file %v", *path)
// 先写入缓冲区
buffer := new(bytes.Buffer)
output := &toWriter{buffer}
if err := output.Output(labels); err != nil {
return fmt.Errorf("error writing labels to buffer: %v", err)
}
// 原子写入文件(使用 renameio 库)
if err := renameio.WriteFile(string(*path), buffer.Bytes(), 0644); err != nil {
return fmt.Errorf("error atomically writing file '%s': %w", *path, err)
}
return nil
}
关键设计 :使用 renameio 实现原子写入------先写入临时文件,然后 rename 到目标路径。这确保读取方永远不会看到部分写入的文件内容。
toWriter --- 写入器输出
go
type toWriter struct {
io.Writer
}
func (output *toWriter) Output(labels Labels) error {
for k, v := range labels {
_, err := fmt.Fprintf(output, "%s=%s\n", k, v) // 格式: key=value\n
if err != nil {
return err
}
}
return nil
}
输出格式 :每行一个标签,格式为 key=value\n。这是 NFD Worker 期望的文件格式。
nvidia.com/cuda.driver-version.full=535.129.03
nvidia.com/cuda.runtime-version.full=12.2
nvidia.com/gpu.count=8
nvidia.com/gpu.product=NVIDIA-A100-SXM4-40GB
nvidia.com/mig.capable=true
nvidia.com/mig.strategy=mixed
...
nodeFeatureObject --- NodeFeature CR 输出
go
type nodeFeatureObject struct {
nodeConfig flags.NodeConfig
nfdClientset nfdclientset.Interface
ownerRefs []metav1.OwnerReference
}
func (n *nodeFeatureObject) Output(labels Labels) error {
nodename := n.nodeConfig.Name
namespace := n.nodeConfig.Namespace
nodeFeatureName := strings.Join([]string{nodeFeatureVendorPrefix, nodename}, "-")
// 如 "nvidia-features-for-node1"
// 尝试获取现有的 NodeFeature CR
nfr, err := n.nfdClientset.NfdV1alpha1().NodeFeatures(namespace).Get(
context.TODO(), nodeFeatureName, metav1.GetOptions{})
if errors.IsNotFound(err) {
// 不存在 → 创建
nfr = &nfdv1alpha1.NodeFeature{
ObjectMeta: metav1.ObjectMeta{
Name: nodeFeatureName,
Labels: map[string]string{nfdv1alpha1.NodeFeatureObjNodeNameLabel: nodename},
OwnerReferences: n.ownerRefs,
},
Spec: nfdv1alpha1.NodeFeatureSpec{
Features: *nfdv1alpha1.NewFeatures(),
Labels: labels, // GPU 标签放入 Spec.Labels
},
}
_, err = n.nfdClientset.NfdV1alpha1().NodeFeatures(namespace).Create(
context.TODO(), nfr, metav1.CreateOptions{})
return nil
}
// 存在 → 比较并更新
nfrUpdated := nfr.DeepCopy()
nfrUpdated.Labels = map[string]string{nfdv1alpha1.NodeFeatureObjNodeNameLabel: nodename}
nfrUpdated.Spec = nfdv1alpha1.NodeFeatureSpec{
Features: *nfdv1alpha1.NewFeatures(),
Labels: labels,
}
nfrUpdated.OwnerReferences = n.ownerRefs
// 无变化则跳过
if apiequality.Semantic.DeepEqual(nfr, nfrUpdated) {
return nil
}
// 有变化则更新
_, err = n.nfdClientset.NfdV1alpha1().NodeFeatures(namespace).Update(
context.TODO(), nfrUpdated, metav1.UpdateOptions{})
return nil
}
设计要点:
- 幂等性:先 Get 再 Create/Update,避免重复创建
- 差异检测 :使用
apiequality.Semantic.DeepEqual比较新旧对象,无变化时跳过更新,减少 API Server 负载 - 垃圾回收 :设置
OwnerReferences指向 DaemonSet 和 Pod,当 DaemonSet 删除时自动清理 NodeFeature CR
NFD Controller K8s API Server GFD (nodeFeatureObject) NFD Controller K8s API Server GFD (nodeFeatureObject) #mermaid-svg-ha4zdR8b8H2wuHQT{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-ha4zdR8b8H2wuHQT .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-ha4zdR8b8H2wuHQT .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-ha4zdR8b8H2wuHQT .error-icon{fill:#552222;}#mermaid-svg-ha4zdR8b8H2wuHQT .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-ha4zdR8b8H2wuHQT .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-ha4zdR8b8H2wuHQT .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-ha4zdR8b8H2wuHQT .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-ha4zdR8b8H2wuHQT .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-ha4zdR8b8H2wuHQT .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-ha4zdR8b8H2wuHQT .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-ha4zdR8b8H2wuHQT .marker{fill:#333333;stroke:#333333;}#mermaid-svg-ha4zdR8b8H2wuHQT .marker.cross{stroke:#333333;}#mermaid-svg-ha4zdR8b8H2wuHQT svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-ha4zdR8b8H2wuHQT p{margin:0;}#mermaid-svg-ha4zdR8b8H2wuHQT .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-ha4zdR8b8H2wuHQT text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-ha4zdR8b8H2wuHQT .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-ha4zdR8b8H2wuHQT .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-ha4zdR8b8H2wuHQT .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-ha4zdR8b8H2wuHQT .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-ha4zdR8b8H2wuHQT #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-ha4zdR8b8H2wuHQT .sequenceNumber{fill:white;}#mermaid-svg-ha4zdR8b8H2wuHQT #sequencenumber{fill:#333;}#mermaid-svg-ha4zdR8b8H2wuHQT #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-ha4zdR8b8H2wuHQT .messageText{fill:#333;stroke:none;}#mermaid-svg-ha4zdR8b8H2wuHQT .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-ha4zdR8b8H2wuHQT .labelText,#mermaid-svg-ha4zdR8b8H2wuHQT .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-ha4zdR8b8H2wuHQT .loopText,#mermaid-svg-ha4zdR8b8H2wuHQT .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-ha4zdR8b8H2wuHQT .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-ha4zdR8b8H2wuHQT .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-ha4zdR8b8H2wuHQT .noteText,#mermaid-svg-ha4zdR8b8H2wuHQT .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-ha4zdR8b8H2wuHQT .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-ha4zdR8b8H2wuHQT .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-ha4zdR8b8H2wuHQT .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-ha4zdR8b8H2wuHQT .actorPopupMenu{position:absolute;}#mermaid-svg-ha4zdR8b8H2wuHQT .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-ha4zdR8b8H2wuHQT .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-ha4zdR8b8H2wuHQT .actor-man circle,#mermaid-svg-ha4zdR8b8H2wuHQT line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-ha4zdR8b8H2wuHQT :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 下次周期... alt无变化有变化 Get NodeFeature CRNotFoundCreate NodeFeature CR (with OwnerRefs)CreatedGet NodeFeature CRExisting CRDeepEqual(old, new)?SkipUpdate NodeFeature CRUpdatedWatch NodeFeature CRUpdate Node Labels
getOwnerReferences --- Owner Reference 解析
go
func getOwnerReferences(ctx context.Context, client kubernetes.Interface,
namespace, podName string) ([]metav1.OwnerReference, error) {
if podName == "" {
return nil, nil // 无 Pod 名则跳过
}
// 获取 Pod 信息
pod, err := client.CoreV1().Pods(namespace).Get(ctx, podName, metav1.GetOptions{})
if err != nil {
return nil, fmt.Errorf("failed to get pod %s/%s: %w", namespace, podName, err)
}
// 查找 owning DaemonSet
var dsOwnerRef *metav1.OwnerReference
for i := range pod.OwnerReferences {
if pod.OwnerReferences[i].Kind == "DaemonSet" {
dsOwnerRef = &pod.OwnerReferences[i]
break
}
}
if dsOwnerRef == nil {
return nil, nil // 非 DaemonSet Pod,跳过
}
controller := true
ownerRefs := []metav1.OwnerReference{
{
// DaemonSet 作为 controller
APIVersion: dsOwnerRef.APIVersion,
Kind: dsOwnerRef.Kind,
Name: dsOwnerRef.Name,
UID: dsOwnerRef.UID,
Controller: &controller,
},
{
// Pod 本身也作为 owner
APIVersion: "v1",
Kind: "Pod",
Name: pod.Name,
UID: pod.UID,
},
}
return ownerRefs, nil
}
Owner Reference 链 :NodeFeature CR ← Pod ← DaemonSet。当 DaemonSet 被删除时,K8s 垃圾回收器自动清理关联的 NodeFeature CR。
#mermaid-svg-TA2Jas3KjuwXcvdd{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-TA2Jas3KjuwXcvdd .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-TA2Jas3KjuwXcvdd .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-TA2Jas3KjuwXcvdd .error-icon{fill:#552222;}#mermaid-svg-TA2Jas3KjuwXcvdd .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-TA2Jas3KjuwXcvdd .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-TA2Jas3KjuwXcvdd .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-TA2Jas3KjuwXcvdd .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-TA2Jas3KjuwXcvdd .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-TA2Jas3KjuwXcvdd .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-TA2Jas3KjuwXcvdd .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-TA2Jas3KjuwXcvdd .marker{fill:#333333;stroke:#333333;}#mermaid-svg-TA2Jas3KjuwXcvdd .marker.cross{stroke:#333333;}#mermaid-svg-TA2Jas3KjuwXcvdd svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-TA2Jas3KjuwXcvdd p{margin:0;}#mermaid-svg-TA2Jas3KjuwXcvdd .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-TA2Jas3KjuwXcvdd .cluster-label text{fill:#333;}#mermaid-svg-TA2Jas3KjuwXcvdd .cluster-label span{color:#333;}#mermaid-svg-TA2Jas3KjuwXcvdd .cluster-label span p{background-color:transparent;}#mermaid-svg-TA2Jas3KjuwXcvdd .label text,#mermaid-svg-TA2Jas3KjuwXcvdd span{fill:#333;color:#333;}#mermaid-svg-TA2Jas3KjuwXcvdd .node rect,#mermaid-svg-TA2Jas3KjuwXcvdd .node circle,#mermaid-svg-TA2Jas3KjuwXcvdd .node ellipse,#mermaid-svg-TA2Jas3KjuwXcvdd .node polygon,#mermaid-svg-TA2Jas3KjuwXcvdd .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-TA2Jas3KjuwXcvdd .rough-node .label text,#mermaid-svg-TA2Jas3KjuwXcvdd .node .label text,#mermaid-svg-TA2Jas3KjuwXcvdd .image-shape .label,#mermaid-svg-TA2Jas3KjuwXcvdd .icon-shape .label{text-anchor:middle;}#mermaid-svg-TA2Jas3KjuwXcvdd .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-TA2Jas3KjuwXcvdd .rough-node .label,#mermaid-svg-TA2Jas3KjuwXcvdd .node .label,#mermaid-svg-TA2Jas3KjuwXcvdd .image-shape .label,#mermaid-svg-TA2Jas3KjuwXcvdd .icon-shape .label{text-align:center;}#mermaid-svg-TA2Jas3KjuwXcvdd .node.clickable{cursor:pointer;}#mermaid-svg-TA2Jas3KjuwXcvdd .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-TA2Jas3KjuwXcvdd .arrowheadPath{fill:#333333;}#mermaid-svg-TA2Jas3KjuwXcvdd .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-TA2Jas3KjuwXcvdd .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-TA2Jas3KjuwXcvdd .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-TA2Jas3KjuwXcvdd .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-TA2Jas3KjuwXcvdd .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-TA2Jas3KjuwXcvdd .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-TA2Jas3KjuwXcvdd .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-TA2Jas3KjuwXcvdd .cluster text{fill:#333;}#mermaid-svg-TA2Jas3KjuwXcvdd .cluster span{color:#333;}#mermaid-svg-TA2Jas3KjuwXcvdd 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-TA2Jas3KjuwXcvdd .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-TA2Jas3KjuwXcvdd rect.text{fill:none;stroke-width:0;}#mermaid-svg-TA2Jas3KjuwXcvdd .icon-shape,#mermaid-svg-TA2Jas3KjuwXcvdd .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-TA2Jas3KjuwXcvdd .icon-shape p,#mermaid-svg-TA2Jas3KjuwXcvdd .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-TA2Jas3KjuwXcvdd .icon-shape .label rect,#mermaid-svg-TA2Jas3KjuwXcvdd .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-TA2Jas3KjuwXcvdd .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-TA2Jas3KjuwXcvdd .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-TA2Jas3KjuwXcvdd :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} owns
owns
Controller=true
DaemonSet
Pod
NodeFeature CR
DaemonSet删除时
自动级联清理
3.3 完整标签清单
#mermaid-svg-wN8FfbelzxEl4pRL{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-wN8FfbelzxEl4pRL .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-wN8FfbelzxEl4pRL .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-wN8FfbelzxEl4pRL .error-icon{fill:#552222;}#mermaid-svg-wN8FfbelzxEl4pRL .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-wN8FfbelzxEl4pRL .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-wN8FfbelzxEl4pRL .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-wN8FfbelzxEl4pRL .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-wN8FfbelzxEl4pRL .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-wN8FfbelzxEl4pRL .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-wN8FfbelzxEl4pRL .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-wN8FfbelzxEl4pRL .marker{fill:#333333;stroke:#333333;}#mermaid-svg-wN8FfbelzxEl4pRL .marker.cross{stroke:#333333;}#mermaid-svg-wN8FfbelzxEl4pRL svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-wN8FfbelzxEl4pRL p{margin:0;}#mermaid-svg-wN8FfbelzxEl4pRL .edge{stroke-width:3;}#mermaid-svg-wN8FfbelzxEl4pRL .section--1 rect,#mermaid-svg-wN8FfbelzxEl4pRL .section--1 path,#mermaid-svg-wN8FfbelzxEl4pRL .section--1 circle,#mermaid-svg-wN8FfbelzxEl4pRL .section--1 polygon,#mermaid-svg-wN8FfbelzxEl4pRL .section--1 path{fill:hsl(240, 100%, 76.2745098039%);}#mermaid-svg-wN8FfbelzxEl4pRL .section--1 text{fill:#ffffff;}#mermaid-svg-wN8FfbelzxEl4pRL .node-icon--1{font-size:40px;color:#ffffff;}#mermaid-svg-wN8FfbelzxEl4pRL .section-edge--1{stroke:hsl(240, 100%, 76.2745098039%);}#mermaid-svg-wN8FfbelzxEl4pRL .edge-depth--1{stroke-width:17;}#mermaid-svg-wN8FfbelzxEl4pRL .section--1 line{stroke:hsl(60, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-wN8FfbelzxEl4pRL .disabled,#mermaid-svg-wN8FfbelzxEl4pRL .disabled circle,#mermaid-svg-wN8FfbelzxEl4pRL .disabled text{fill:lightgray;}#mermaid-svg-wN8FfbelzxEl4pRL .disabled text{fill:#efefef;}#mermaid-svg-wN8FfbelzxEl4pRL .section-0 rect,#mermaid-svg-wN8FfbelzxEl4pRL .section-0 path,#mermaid-svg-wN8FfbelzxEl4pRL .section-0 circle,#mermaid-svg-wN8FfbelzxEl4pRL .section-0 polygon,#mermaid-svg-wN8FfbelzxEl4pRL .section-0 path{fill:hsl(60, 100%, 73.5294117647%);}#mermaid-svg-wN8FfbelzxEl4pRL .section-0 text{fill:black;}#mermaid-svg-wN8FfbelzxEl4pRL .node-icon-0{font-size:40px;color:black;}#mermaid-svg-wN8FfbelzxEl4pRL .section-edge-0{stroke:hsl(60, 100%, 73.5294117647%);}#mermaid-svg-wN8FfbelzxEl4pRL .edge-depth-0{stroke-width:14;}#mermaid-svg-wN8FfbelzxEl4pRL .section-0 line{stroke:hsl(240, 100%, 83.5294117647%);stroke-width:3;}#mermaid-svg-wN8FfbelzxEl4pRL .disabled,#mermaid-svg-wN8FfbelzxEl4pRL .disabled circle,#mermaid-svg-wN8FfbelzxEl4pRL .disabled text{fill:lightgray;}#mermaid-svg-wN8FfbelzxEl4pRL .disabled text{fill:#efefef;}#mermaid-svg-wN8FfbelzxEl4pRL .section-1 rect,#mermaid-svg-wN8FfbelzxEl4pRL .section-1 path,#mermaid-svg-wN8FfbelzxEl4pRL .section-1 circle,#mermaid-svg-wN8FfbelzxEl4pRL .section-1 polygon,#mermaid-svg-wN8FfbelzxEl4pRL .section-1 path{fill:hsl(80, 100%, 76.2745098039%);}#mermaid-svg-wN8FfbelzxEl4pRL .section-1 text{fill:black;}#mermaid-svg-wN8FfbelzxEl4pRL .node-icon-1{font-size:40px;color:black;}#mermaid-svg-wN8FfbelzxEl4pRL .section-edge-1{stroke:hsl(80, 100%, 76.2745098039%);}#mermaid-svg-wN8FfbelzxEl4pRL .edge-depth-1{stroke-width:11;}#mermaid-svg-wN8FfbelzxEl4pRL .section-1 line{stroke:hsl(260, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-wN8FfbelzxEl4pRL .disabled,#mermaid-svg-wN8FfbelzxEl4pRL .disabled circle,#mermaid-svg-wN8FfbelzxEl4pRL .disabled text{fill:lightgray;}#mermaid-svg-wN8FfbelzxEl4pRL .disabled text{fill:#efefef;}#mermaid-svg-wN8FfbelzxEl4pRL .section-2 rect,#mermaid-svg-wN8FfbelzxEl4pRL .section-2 path,#mermaid-svg-wN8FfbelzxEl4pRL .section-2 circle,#mermaid-svg-wN8FfbelzxEl4pRL .section-2 polygon,#mermaid-svg-wN8FfbelzxEl4pRL .section-2 path{fill:hsl(270, 100%, 76.2745098039%);}#mermaid-svg-wN8FfbelzxEl4pRL .section-2 text{fill:#ffffff;}#mermaid-svg-wN8FfbelzxEl4pRL .node-icon-2{font-size:40px;color:#ffffff;}#mermaid-svg-wN8FfbelzxEl4pRL .section-edge-2{stroke:hsl(270, 100%, 76.2745098039%);}#mermaid-svg-wN8FfbelzxEl4pRL .edge-depth-2{stroke-width:8;}#mermaid-svg-wN8FfbelzxEl4pRL .section-2 line{stroke:hsl(90, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-wN8FfbelzxEl4pRL .disabled,#mermaid-svg-wN8FfbelzxEl4pRL .disabled circle,#mermaid-svg-wN8FfbelzxEl4pRL .disabled text{fill:lightgray;}#mermaid-svg-wN8FfbelzxEl4pRL .disabled text{fill:#efefef;}#mermaid-svg-wN8FfbelzxEl4pRL .section-3 rect,#mermaid-svg-wN8FfbelzxEl4pRL .section-3 path,#mermaid-svg-wN8FfbelzxEl4pRL .section-3 circle,#mermaid-svg-wN8FfbelzxEl4pRL .section-3 polygon,#mermaid-svg-wN8FfbelzxEl4pRL .section-3 path{fill:hsl(300, 100%, 76.2745098039%);}#mermaid-svg-wN8FfbelzxEl4pRL .section-3 text{fill:black;}#mermaid-svg-wN8FfbelzxEl4pRL .node-icon-3{font-size:40px;color:black;}#mermaid-svg-wN8FfbelzxEl4pRL .section-edge-3{stroke:hsl(300, 100%, 76.2745098039%);}#mermaid-svg-wN8FfbelzxEl4pRL .edge-depth-3{stroke-width:5;}#mermaid-svg-wN8FfbelzxEl4pRL .section-3 line{stroke:hsl(120, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-wN8FfbelzxEl4pRL .disabled,#mermaid-svg-wN8FfbelzxEl4pRL .disabled circle,#mermaid-svg-wN8FfbelzxEl4pRL .disabled text{fill:lightgray;}#mermaid-svg-wN8FfbelzxEl4pRL .disabled text{fill:#efefef;}#mermaid-svg-wN8FfbelzxEl4pRL .section-4 rect,#mermaid-svg-wN8FfbelzxEl4pRL .section-4 path,#mermaid-svg-wN8FfbelzxEl4pRL .section-4 circle,#mermaid-svg-wN8FfbelzxEl4pRL .section-4 polygon,#mermaid-svg-wN8FfbelzxEl4pRL .section-4 path{fill:hsl(330, 100%, 76.2745098039%);}#mermaid-svg-wN8FfbelzxEl4pRL .section-4 text{fill:black;}#mermaid-svg-wN8FfbelzxEl4pRL .node-icon-4{font-size:40px;color:black;}#mermaid-svg-wN8FfbelzxEl4pRL .section-edge-4{stroke:hsl(330, 100%, 76.2745098039%);}#mermaid-svg-wN8FfbelzxEl4pRL .edge-depth-4{stroke-width:2;}#mermaid-svg-wN8FfbelzxEl4pRL .section-4 line{stroke:hsl(150, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-wN8FfbelzxEl4pRL .disabled,#mermaid-svg-wN8FfbelzxEl4pRL .disabled circle,#mermaid-svg-wN8FfbelzxEl4pRL .disabled text{fill:lightgray;}#mermaid-svg-wN8FfbelzxEl4pRL .disabled text{fill:#efefef;}#mermaid-svg-wN8FfbelzxEl4pRL .section-5 rect,#mermaid-svg-wN8FfbelzxEl4pRL .section-5 path,#mermaid-svg-wN8FfbelzxEl4pRL .section-5 circle,#mermaid-svg-wN8FfbelzxEl4pRL .section-5 polygon,#mermaid-svg-wN8FfbelzxEl4pRL .section-5 path{fill:hsl(0, 100%, 76.2745098039%);}#mermaid-svg-wN8FfbelzxEl4pRL .section-5 text{fill:black;}#mermaid-svg-wN8FfbelzxEl4pRL .node-icon-5{font-size:40px;color:black;}#mermaid-svg-wN8FfbelzxEl4pRL .section-edge-5{stroke:hsl(0, 100%, 76.2745098039%);}#mermaid-svg-wN8FfbelzxEl4pRL .edge-depth-5{stroke-width:-1;}#mermaid-svg-wN8FfbelzxEl4pRL .section-5 line{stroke:hsl(180, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-wN8FfbelzxEl4pRL .disabled,#mermaid-svg-wN8FfbelzxEl4pRL .disabled circle,#mermaid-svg-wN8FfbelzxEl4pRL .disabled text{fill:lightgray;}#mermaid-svg-wN8FfbelzxEl4pRL .disabled text{fill:#efefef;}#mermaid-svg-wN8FfbelzxEl4pRL .section-6 rect,#mermaid-svg-wN8FfbelzxEl4pRL .section-6 path,#mermaid-svg-wN8FfbelzxEl4pRL .section-6 circle,#mermaid-svg-wN8FfbelzxEl4pRL .section-6 polygon,#mermaid-svg-wN8FfbelzxEl4pRL .section-6 path{fill:hsl(30, 100%, 76.2745098039%);}#mermaid-svg-wN8FfbelzxEl4pRL .section-6 text{fill:black;}#mermaid-svg-wN8FfbelzxEl4pRL .node-icon-6{font-size:40px;color:black;}#mermaid-svg-wN8FfbelzxEl4pRL .section-edge-6{stroke:hsl(30, 100%, 76.2745098039%);}#mermaid-svg-wN8FfbelzxEl4pRL .edge-depth-6{stroke-width:-4;}#mermaid-svg-wN8FfbelzxEl4pRL .section-6 line{stroke:hsl(210, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-wN8FfbelzxEl4pRL .disabled,#mermaid-svg-wN8FfbelzxEl4pRL .disabled circle,#mermaid-svg-wN8FfbelzxEl4pRL .disabled text{fill:lightgray;}#mermaid-svg-wN8FfbelzxEl4pRL .disabled text{fill:#efefef;}#mermaid-svg-wN8FfbelzxEl4pRL .section-7 rect,#mermaid-svg-wN8FfbelzxEl4pRL .section-7 path,#mermaid-svg-wN8FfbelzxEl4pRL .section-7 circle,#mermaid-svg-wN8FfbelzxEl4pRL .section-7 polygon,#mermaid-svg-wN8FfbelzxEl4pRL .section-7 path{fill:hsl(90, 100%, 76.2745098039%);}#mermaid-svg-wN8FfbelzxEl4pRL .section-7 text{fill:black;}#mermaid-svg-wN8FfbelzxEl4pRL .node-icon-7{font-size:40px;color:black;}#mermaid-svg-wN8FfbelzxEl4pRL .section-edge-7{stroke:hsl(90, 100%, 76.2745098039%);}#mermaid-svg-wN8FfbelzxEl4pRL .edge-depth-7{stroke-width:-7;}#mermaid-svg-wN8FfbelzxEl4pRL .section-7 line{stroke:hsl(270, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-wN8FfbelzxEl4pRL .disabled,#mermaid-svg-wN8FfbelzxEl4pRL .disabled circle,#mermaid-svg-wN8FfbelzxEl4pRL .disabled text{fill:lightgray;}#mermaid-svg-wN8FfbelzxEl4pRL .disabled text{fill:#efefef;}#mermaid-svg-wN8FfbelzxEl4pRL .section-8 rect,#mermaid-svg-wN8FfbelzxEl4pRL .section-8 path,#mermaid-svg-wN8FfbelzxEl4pRL .section-8 circle,#mermaid-svg-wN8FfbelzxEl4pRL .section-8 polygon,#mermaid-svg-wN8FfbelzxEl4pRL .section-8 path{fill:hsl(150, 100%, 76.2745098039%);}#mermaid-svg-wN8FfbelzxEl4pRL .section-8 text{fill:black;}#mermaid-svg-wN8FfbelzxEl4pRL .node-icon-8{font-size:40px;color:black;}#mermaid-svg-wN8FfbelzxEl4pRL .section-edge-8{stroke:hsl(150, 100%, 76.2745098039%);}#mermaid-svg-wN8FfbelzxEl4pRL .edge-depth-8{stroke-width:-10;}#mermaid-svg-wN8FfbelzxEl4pRL .section-8 line{stroke:hsl(330, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-wN8FfbelzxEl4pRL .disabled,#mermaid-svg-wN8FfbelzxEl4pRL .disabled circle,#mermaid-svg-wN8FfbelzxEl4pRL .disabled text{fill:lightgray;}#mermaid-svg-wN8FfbelzxEl4pRL .disabled text{fill:#efefef;}#mermaid-svg-wN8FfbelzxEl4pRL .section-9 rect,#mermaid-svg-wN8FfbelzxEl4pRL .section-9 path,#mermaid-svg-wN8FfbelzxEl4pRL .section-9 circle,#mermaid-svg-wN8FfbelzxEl4pRL .section-9 polygon,#mermaid-svg-wN8FfbelzxEl4pRL .section-9 path{fill:hsl(180, 100%, 76.2745098039%);}#mermaid-svg-wN8FfbelzxEl4pRL .section-9 text{fill:black;}#mermaid-svg-wN8FfbelzxEl4pRL .node-icon-9{font-size:40px;color:black;}#mermaid-svg-wN8FfbelzxEl4pRL .section-edge-9{stroke:hsl(180, 100%, 76.2745098039%);}#mermaid-svg-wN8FfbelzxEl4pRL .edge-depth-9{stroke-width:-13;}#mermaid-svg-wN8FfbelzxEl4pRL .section-9 line{stroke:hsl(0, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-wN8FfbelzxEl4pRL .disabled,#mermaid-svg-wN8FfbelzxEl4pRL .disabled circle,#mermaid-svg-wN8FfbelzxEl4pRL .disabled text{fill:lightgray;}#mermaid-svg-wN8FfbelzxEl4pRL .disabled text{fill:#efefef;}#mermaid-svg-wN8FfbelzxEl4pRL .section-10 rect,#mermaid-svg-wN8FfbelzxEl4pRL .section-10 path,#mermaid-svg-wN8FfbelzxEl4pRL .section-10 circle,#mermaid-svg-wN8FfbelzxEl4pRL .section-10 polygon,#mermaid-svg-wN8FfbelzxEl4pRL .section-10 path{fill:hsl(210, 100%, 76.2745098039%);}#mermaid-svg-wN8FfbelzxEl4pRL .section-10 text{fill:black;}#mermaid-svg-wN8FfbelzxEl4pRL .node-icon-10{font-size:40px;color:black;}#mermaid-svg-wN8FfbelzxEl4pRL .section-edge-10{stroke:hsl(210, 100%, 76.2745098039%);}#mermaid-svg-wN8FfbelzxEl4pRL .edge-depth-10{stroke-width:-16;}#mermaid-svg-wN8FfbelzxEl4pRL .section-10 line{stroke:hsl(30, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-wN8FfbelzxEl4pRL .disabled,#mermaid-svg-wN8FfbelzxEl4pRL .disabled circle,#mermaid-svg-wN8FfbelzxEl4pRL .disabled text{fill:lightgray;}#mermaid-svg-wN8FfbelzxEl4pRL .disabled text{fill:#efefef;}#mermaid-svg-wN8FfbelzxEl4pRL .section-root rect,#mermaid-svg-wN8FfbelzxEl4pRL .section-root path,#mermaid-svg-wN8FfbelzxEl4pRL .section-root circle,#mermaid-svg-wN8FfbelzxEl4pRL .section-root polygon{fill:hsl(240, 100%, 46.2745098039%);}#mermaid-svg-wN8FfbelzxEl4pRL .section-root text{fill:#ffffff;}#mermaid-svg-wN8FfbelzxEl4pRL .section-root span{color:#ffffff;}#mermaid-svg-wN8FfbelzxEl4pRL .section-2 span{color:#ffffff;}#mermaid-svg-wN8FfbelzxEl4pRL .icon-container{height:100%;display:flex;justify-content:center;align-items:center;}#mermaid-svg-wN8FfbelzxEl4pRL .edge{fill:none;}#mermaid-svg-wN8FfbelzxEl4pRL .mindmap-node-label{dy:1em;alignment-baseline:middle;text-anchor:middle;dominant-baseline:middle;text-align:center;}#mermaid-svg-wN8FfbelzxEl4pRL :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} nvidia.com/*
cuda
driver-version
major
minor
revision
full
runtime-version
major
minor
full
deprecated
major
minor
rev
deprecated
major
minor
mig
capable
strategy
mps
capable
gpu
machine
mode
clique
product
count
replicas
sharing-strategy
memory
family
compute.major
compute.minor
mig-profile
product
count
replicas
sharing-strategy
memory
attributes
vgpu
present
host-driver-version
host-driver-branch
gfd
timestamp
3.4 设计模式总结
#mermaid-svg-q8QVz9MWsPLh3x8c{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-q8QVz9MWsPLh3x8c .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-q8QVz9MWsPLh3x8c .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-q8QVz9MWsPLh3x8c .error-icon{fill:#552222;}#mermaid-svg-q8QVz9MWsPLh3x8c .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-q8QVz9MWsPLh3x8c .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-q8QVz9MWsPLh3x8c .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-q8QVz9MWsPLh3x8c .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-q8QVz9MWsPLh3x8c .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-q8QVz9MWsPLh3x8c .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-q8QVz9MWsPLh3x8c .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-q8QVz9MWsPLh3x8c .marker{fill:#333333;stroke:#333333;}#mermaid-svg-q8QVz9MWsPLh3x8c .marker.cross{stroke:#333333;}#mermaid-svg-q8QVz9MWsPLh3x8c svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-q8QVz9MWsPLh3x8c p{margin:0;}#mermaid-svg-q8QVz9MWsPLh3x8c g.classGroup text{fill:#9370DB;stroke:none;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:10px;}#mermaid-svg-q8QVz9MWsPLh3x8c g.classGroup text .title{font-weight:bolder;}#mermaid-svg-q8QVz9MWsPLh3x8c .cluster-label text{fill:#333;}#mermaid-svg-q8QVz9MWsPLh3x8c .cluster-label span{color:#333;}#mermaid-svg-q8QVz9MWsPLh3x8c .cluster-label span p{background-color:transparent;}#mermaid-svg-q8QVz9MWsPLh3x8c .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-q8QVz9MWsPLh3x8c .cluster text{fill:#333;}#mermaid-svg-q8QVz9MWsPLh3x8c .cluster span{color:#333;}#mermaid-svg-q8QVz9MWsPLh3x8c .nodeLabel,#mermaid-svg-q8QVz9MWsPLh3x8c .edgeLabel{color:#131300;}#mermaid-svg-q8QVz9MWsPLh3x8c .edgeLabel .label rect{fill:#ECECFF;}#mermaid-svg-q8QVz9MWsPLh3x8c .label text{fill:#131300;}#mermaid-svg-q8QVz9MWsPLh3x8c .labelBkg{background:#ECECFF;}#mermaid-svg-q8QVz9MWsPLh3x8c .edgeLabel .label span{background:#ECECFF;}#mermaid-svg-q8QVz9MWsPLh3x8c .classTitle{font-weight:bolder;}#mermaid-svg-q8QVz9MWsPLh3x8c .node rect,#mermaid-svg-q8QVz9MWsPLh3x8c .node circle,#mermaid-svg-q8QVz9MWsPLh3x8c .node ellipse,#mermaid-svg-q8QVz9MWsPLh3x8c .node polygon,#mermaid-svg-q8QVz9MWsPLh3x8c .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-q8QVz9MWsPLh3x8c .divider{stroke:#9370DB;stroke-width:1;}#mermaid-svg-q8QVz9MWsPLh3x8c g.clickable{cursor:pointer;}#mermaid-svg-q8QVz9MWsPLh3x8c g.classGroup rect{fill:#ECECFF;stroke:#9370DB;}#mermaid-svg-q8QVz9MWsPLh3x8c g.classGroup line{stroke:#9370DB;stroke-width:1;}#mermaid-svg-q8QVz9MWsPLh3x8c .classLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.5;}#mermaid-svg-q8QVz9MWsPLh3x8c .classLabel .label{fill:#9370DB;font-size:10px;}#mermaid-svg-q8QVz9MWsPLh3x8c .relation{stroke:#333333;stroke-width:1;fill:none;}#mermaid-svg-q8QVz9MWsPLh3x8c .dashed-line{stroke-dasharray:3;}#mermaid-svg-q8QVz9MWsPLh3x8c .dotted-line{stroke-dasharray:1 2;}#mermaid-svg-q8QVz9MWsPLh3x8c #compositionStart,#mermaid-svg-q8QVz9MWsPLh3x8c .composition{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-q8QVz9MWsPLh3x8c #compositionEnd,#mermaid-svg-q8QVz9MWsPLh3x8c .composition{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-q8QVz9MWsPLh3x8c #dependencyStart,#mermaid-svg-q8QVz9MWsPLh3x8c .dependency{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-q8QVz9MWsPLh3x8c #dependencyStart,#mermaid-svg-q8QVz9MWsPLh3x8c .dependency{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-q8QVz9MWsPLh3x8c #extensionStart,#mermaid-svg-q8QVz9MWsPLh3x8c .extension{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-q8QVz9MWsPLh3x8c #extensionEnd,#mermaid-svg-q8QVz9MWsPLh3x8c .extension{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-q8QVz9MWsPLh3x8c #aggregationStart,#mermaid-svg-q8QVz9MWsPLh3x8c .aggregation{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-q8QVz9MWsPLh3x8c #aggregationEnd,#mermaid-svg-q8QVz9MWsPLh3x8c .aggregation{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-q8QVz9MWsPLh3x8c #lollipopStart,#mermaid-svg-q8QVz9MWsPLh3x8c .lollipop{fill:#ECECFF!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-q8QVz9MWsPLh3x8c #lollipopEnd,#mermaid-svg-q8QVz9MWsPLh3x8c .lollipop{fill:#ECECFF!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-q8QVz9MWsPLh3x8c .edgeTerminals{font-size:11px;line-height:initial;}#mermaid-svg-q8QVz9MWsPLh3x8c .classTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-q8QVz9MWsPLh3x8c .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-q8QVz9MWsPLh3x8c .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-q8QVz9MWsPLh3x8c :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} <<Pattern>>
StrategyPattern
Labeler接口
不同标签器实现
<<Pattern>>
CompositePattern
list类型
Merge函数
<<Pattern>>
NullObjectPattern
empty结构体
<<Pattern>>
FactoryMethodPattern
NewLabelers
NewDeviceLabeler
NewResourceLabeler
NewOutputer
<<Pattern>>
BuilderPattern
resourceLabeler
baseLabeler/labels/single
<<Pattern>>
ObserverPattern
NodeFeature CR
NFD Controller Watch
labeler.go
list.go
empty.go
nvml.go
resource.go
output.go
| 设计模式 | 应用位置 | 作用 |
|---|---|---|
| 策略模式 | Labeler 接口 + 各种实现 |
统一标签生成接口,支持多种标签源 |
| 组合模式 | list + Merge |
递归组合多个 Labeler,形成树形结构 |
| 空对象模式 | empty{} |
替代 nil,简化空值处理 |
| 工厂方法模式 | New* 系列函数 |
封装标签器构造逻辑 |
| 建造者模式 | resourceLabeler 方法链 |
分步构建复杂标签集合 |
| 观察者模式 | NodeFeature CR + NFD Controller | 通过 CR 声明式同步标签 |
3.5 标签生成完整流程时序图
Outputer Merge NewVGPULabeler newMigLabeler NewResourceLabeler resource.Manager NewDeviceLabeler NewLabelers GFD Main Outputer Merge NewVGPULabeler newMigLabeler NewResourceLabeler resource.Manager NewDeviceLabeler NewLabelers GFD Main #mermaid-svg-f0BlhT4RlA2lssSk{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-f0BlhT4RlA2lssSk .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-f0BlhT4RlA2lssSk .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-f0BlhT4RlA2lssSk .error-icon{fill:#552222;}#mermaid-svg-f0BlhT4RlA2lssSk .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-f0BlhT4RlA2lssSk .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-f0BlhT4RlA2lssSk .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-f0BlhT4RlA2lssSk .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-f0BlhT4RlA2lssSk .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-f0BlhT4RlA2lssSk .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-f0BlhT4RlA2lssSk .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-f0BlhT4RlA2lssSk .marker{fill:#333333;stroke:#333333;}#mermaid-svg-f0BlhT4RlA2lssSk .marker.cross{stroke:#333333;}#mermaid-svg-f0BlhT4RlA2lssSk svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-f0BlhT4RlA2lssSk p{margin:0;}#mermaid-svg-f0BlhT4RlA2lssSk .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-f0BlhT4RlA2lssSk text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-f0BlhT4RlA2lssSk .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-f0BlhT4RlA2lssSk .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-f0BlhT4RlA2lssSk .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-f0BlhT4RlA2lssSk .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-f0BlhT4RlA2lssSk #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-f0BlhT4RlA2lssSk .sequenceNumber{fill:white;}#mermaid-svg-f0BlhT4RlA2lssSk #sequencenumber{fill:#333;}#mermaid-svg-f0BlhT4RlA2lssSk #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-f0BlhT4RlA2lssSk .messageText{fill:#333;stroke:none;}#mermaid-svg-f0BlhT4RlA2lssSk .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-f0BlhT4RlA2lssSk .labelText,#mermaid-svg-f0BlhT4RlA2lssSk .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-f0BlhT4RlA2lssSk .loopText,#mermaid-svg-f0BlhT4RlA2lssSk .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-f0BlhT4RlA2lssSk .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-f0BlhT4RlA2lssSk .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-f0BlhT4RlA2lssSk .noteText,#mermaid-svg-f0BlhT4RlA2lssSk .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-f0BlhT4RlA2lssSk .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-f0BlhT4RlA2lssSk .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-f0BlhT4RlA2lssSk .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-f0BlhT4RlA2lssSk .actorPopupMenu{position:absolute;}#mermaid-svg-f0BlhT4RlA2lssSk .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-f0BlhT4RlA2lssSk .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-f0BlhT4RlA2lssSk .actor-man circle,#mermaid-svg-f0BlhT4RlA2lssSk line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-f0BlhT4RlA2lssSk :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} altMigStrategy == singleMigStrategy == mixed altMigStrategy != none alt文件模式NFD API模式 NewLabelers(manager, vgpu, config)NewDeviceLabeler(manager, config)Init()OKGetDevices()\[\]Device (N个)newMachineTypeLabeler()newVersionLabeler(manager)newMigCapabilityLabeler(manager)newSharingLabeler(manager, config)newGPUModeLabeler(devices)newImexLabeler(config, devices)NewResourceLabeler(manager, config)newGPULabelers(manager, config)newMigLabeler(manager, config)newMigStrategySingleLabeler()newMigStrategyMixedLabeler()Merge(所有子labeler)Shutdown()NewVGPULabeler(vgpu)Merge(deviceLabeler, vgpuLabeler)LabelerLabeler.Labels()遍历所有子LabelerLabels (mapstringstring)NewOutputer(config, ...)Output(labels)renameio.WriteFile (原子写入)Create/Update NodeFeature CRnil (成功)
四、输出格式汇总
4.1 文件输出格式
nvidia.com/cuda.driver.major=535
nvidia.com/cuda.driver.minor=129
nvidia.com/cuda.driver.rev=03
nvidia.com/cuda.driver-version.full=535.129.03
nvidia.com/cuda.driver-version.major=535
nvidia.com/cuda.driver-version.minor=129
nvidia.com/cuda.driver-version.revision=03
nvidia.com/cuda.runtime.major=12
nvidia.com/cuda.runtime.minor=2
nvidia.com/cuda.runtime-version.full=12.2
nvidia.com/cuda.runtime-version.major=12
nvidia.com/cuda.runtime-version.minor=2
nvidia.com/gpu.machine=PowerEdge-R750
nvidia.com/gpu.mode=compute
nvidia.com/gpu.clique=cluster-abc.clique-xyz
nvidia.com/mig.capable=true
nvidia.com/mig.strategy=mixed
nvidia.com/mps.capable=false
nvidia.com/gpu.product=NVIDIA-A100-SXM4-40GB
nvidia.com/gpu.count=4
nvidia.com/gpu.replicas=1
nvidia.com/gpu.sharing-strategy=none
nvidia.com/gpu.memory=40960
nvidia.com/gpu.family=ampere
nvidia.com/gpu.compute.major=8
nvidia.com/gpu.compute.minor=0
nvidia.com/mig-1g.10gb.product=NVIDIA-A100-SXM4-40GB-MIG-1g.10gb
nvidia.com/mig-1g.10gb.count=12
nvidia.com/mig-1g.10gb.replicas=1
nvidia.com/mig-1g.10gb.sharing-strategy=none
nvidia.com/mig-1g.10gb.memory=960
nvidia.com/mig-1g.10gb.engines=1
nvidia.com/vgpu.present=false
nvidia.com/gfd.timestamp=1721958480
4.2 NodeFeature CR 格式
yaml
apiVersion: nfd.k8s-sigs.io/v1alpha1
kind: NodeFeature
metadata:
name: nvidia-features-for-node1
namespace: node-feature-discovery
labels:
nfd.node-feature-discovery.io/node-name: node1
ownerReferences:
- apiVersion: apps/v1
kind: DaemonSet
name: nvidia-device-plugin
uid: xxx-xxx-xxx
controller: true
- apiVersion: v1
kind: Pod
name: nvidia-device-plugin-xxxxx
uid: yyy-yyy-yyy
spec:
features: {}
labels:
nvidia.com/cuda.driver-version.full: "535.129.03"
nvidia.com/gpu.product: "NVIDIA-A100-SXM4-40GB"
nvidia.com/gpu.count: "4"
# ... 其他标签
五、架构亮点与设计评价
5.1 优点
- 高度模块化:每个标签器职责单一,可独立测试和复用
- 组合模式优雅 :
Merge函数让标签器的组合极其灵活,支持任意层级嵌套 - 错误容忍:多处使用降级处理(machine-type、gpu-mode、vgpu),避免非关键错误阻断整个标签生成
- 原子写入 :文件输出使用
renameio确保原子性 - 幂等输出:NodeFeature CR 输出前做 DeepEqual 检查,减少不必要的 API 调用
- 垃圾回收:通过 OwnerReferences 实现 NodeFeature CR 的自动清理
- 向后兼容:同时维护新旧两套版本标签
5.2 潜在改进点
- vGPU 错误吞没 :
vgpuLabeler.Labels()在Devices()出错时返回nil, nil,可能掩盖真实问题 - MIG 属性标签未清理 :
newMigAttributeLabels直接使用device.GetAttributes()返回的 map,未对 key/value 做 sanitize - 单 GPU 类型假设 :
newGPULabelers对多 GPU 类型只 Warning 不报错,可能导致标签不准确 - Timestamp 不在主链路 :
NewTimestampLabeler需要主程序单独调用,设计上不够内聚
本分析基于 NVIDIA k8s-device-plugin v0.19.3 源码,覆盖
internal/lm模块全部 13 个文件,严格依据代码逻辑生成。