【NVIDIA】k8s-device-plugin v0.19.3 标签管理模块 (internal/lm) 深度分析之六

NVIDIA k8s-device-plugin v0.19.3 标签管理模块 (internal/lm) 深度分析

一、模块定位

1.1 业务职责

internal/lm(Label Manager)模块是 NVIDIA k8s-device-plugin 的标签生成与管理核心模块。其核心职责是:

  1. 采集节点 GPU 硬件信息:驱动版本、CUDA 版本、GPU 型号、显存、架构族、MIG 能力、GPU 模式(Graphics/Compute)、vGPU 信息、IMEX Fabric 拓扑等
  2. 生成 Kubernetes Node Labels :将采集的硬件信息转换为 nvidia.com/* 前缀的节点标签
  3. 处理 MIG 策略 :根据 mig-strategy(none/single/mixed)生成不同的 MIG 相关标签
  4. 处理共享策略 :根据 Time-Slicing / MPS 共享配置生成 replicassharing-strategy 等标签
  5. 输出标签:支持文件输出(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 是对外暴露的唯一入口。它只组合 deviceLabelervgpuLabeler 两个顶层标签器。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
}

设计精妙之处

  1. Labels 既是数据载体(map[string]string),又自身实现 Labeler 接口
  2. 这意味着任何 Labels 实例可以直接当作 Labeler 使用
  3. 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 时
  • NewTimestampLabelerNoTimestamp 为 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
}

关键逻辑

  1. MIG 启用的设备生成标签时不包含共享信息(因为 MIG 分区本身就是一种资源切分)
  2. 全 GPU 设备生成标签时包含共享信息(Time-Slicing/MPS)
  3. 全 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 策略的有效条件

  1. 所有 GPU 都启用了 MIG
  2. 所有 MIG 设备都不为空(已创建 MIG 实例)
  3. 所有 MIG 实例属于同一种类型
  4. 满足以上条件时,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 40GBNVIDIA-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-R750ThinkPad-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
}

业务逻辑

  1. 遍历所有设备,检查是否连接到 NVLink Fabric
  2. 收集所有 Fabric 设备的 Cluster UUID 和 Clique ID
  3. 约束:Cluster UUID 必须唯一(所有 GPU 属于同一集群),Clique ID 必须唯一(所有 GPU 属于同一 Clique)
  4. 不满足约束时返回空(不生成标签,而非报错)
  5. 标签格式: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
}

设计要点

  1. 幂等性:先 Get 再 Create/Update,避免重复创建
  2. 差异检测 :使用 apiequality.Semantic.DeepEqual 比较新旧对象,无变化时跳过更新,减少 API Server 负载
  3. 垃圾回收 :设置 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 CRPodDaemonSet。当 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 优点

  1. 高度模块化:每个标签器职责单一,可独立测试和复用
  2. 组合模式优雅Merge 函数让标签器的组合极其灵活,支持任意层级嵌套
  3. 错误容忍:多处使用降级处理(machine-type、gpu-mode、vgpu),避免非关键错误阻断整个标签生成
  4. 原子写入 :文件输出使用 renameio 确保原子性
  5. 幂等输出:NodeFeature CR 输出前做 DeepEqual 检查,减少不必要的 API 调用
  6. 垃圾回收:通过 OwnerReferences 实现 NodeFeature CR 的自动清理
  7. 向后兼容:同时维护新旧两套版本标签

5.2 潜在改进点

  1. vGPU 错误吞没vgpuLabeler.Labels()Devices() 出错时返回 nil, nil,可能掩盖真实问题
  2. MIG 属性标签未清理newMigAttributeLabels 直接使用 device.GetAttributes() 返回的 map,未对 key/value 做 sanitize
  3. 单 GPU 类型假设newGPULabelers 对多 GPU 类型只 Warning 不报错,可能导致标签不准确
  4. Timestamp 不在主链路NewTimestampLabeler 需要主程序单独调用,设计上不够内聚

本分析基于 NVIDIA k8s-device-plugin v0.19.3 源码,覆盖 internal/lm 模块全部 13 个文件,严格依据代码逻辑生成。

相关推荐
小匠石钧知1 小时前
02_在多个RockyLinux10虚拟机上安装k8s集群
云原生·容器·kubernetes·k8s
阿里云云原生2 小时前
一周上线!信永中和基于阿里云 AgentTeams + AI 网关打造多智能体 AI 平台
阿里云·云原生·ai网关·agentteams
qq_454245033 小时前
认知自举:LLM自我指令泛化的三层逻辑
人工智能·架构·prompt
XUHUOJUN3 小时前
Azure Stack Hub 存储服务:托管磁盘、Blob/Table/Queue 与容量管理(下篇)
架构·azure stack
霁月的小屋3 小时前
Docker 工程化实践(六):Docker Compose 管理多容器应用
运维·docker·容器
XUHUOJUN3 小时前
Azure Stack Hub 存储服务:从 S2D 物理栈到租户服务全景(上篇)
架构·azure stack
江湖有缘4 小时前
Docker实战 | 使用Docker部署EasyNode服务器面板工具
服务器·docker·容器
上海云盾商务经理杨杨4 小时前
WAF+DDoS 双层防护架构!解决应用层混合攻击漏杀误杀问题
架构·ddos
心机之蛙qee4 小时前
Docker Compose 多容器编排
java·docker·容器