NVIDIA k8s-device-plugin v0.19.3 配置API模块深度分析
分析范围:
api/config/v1/目录下全部 9 个 Go 源文件分析日期:2026-07-26
分析版本:v0.19.3
一、模块定位
1.1 业务职责
配置API模块(api/config/v1)是 NVIDIA k8s-device-plugin 的配置中枢,承担以下核心职责:
| 职责 | 说明 |
|---|---|
| 配置数据模型定义 | 定义插件运行所需的全部配置数据结构(Flags、Resources、Sharing、Imex 等) |
| 配置加载与解析 | 从 YAML/JSON 配置文件读取配置,支持命令行参数覆盖 |
| 配置校验 | 对配置值进行格式校验、版本校验、语义校验 |
| 配置优先级合并 | 按命令行 > 环境变量 > 配置文件的优先级合并配置 |
| 常量定义 | 集中管理资源名前缀、MIG 策略、设备列表策略、设备ID策略等常量 |
| 资源共享模型 | 定义时间分片(TimeSlicing)和 MPS 两种 GPU 共享策略的数据模型 |
1.2 在系统中的位置
#mermaid-svg-Ppr2aJaXxOppXnZf{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-Ppr2aJaXxOppXnZf .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-Ppr2aJaXxOppXnZf .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-Ppr2aJaXxOppXnZf .error-icon{fill:#552222;}#mermaid-svg-Ppr2aJaXxOppXnZf .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-Ppr2aJaXxOppXnZf .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-Ppr2aJaXxOppXnZf .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-Ppr2aJaXxOppXnZf .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-Ppr2aJaXxOppXnZf .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-Ppr2aJaXxOppXnZf .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-Ppr2aJaXxOppXnZf .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-Ppr2aJaXxOppXnZf .marker{fill:#333333;stroke:#333333;}#mermaid-svg-Ppr2aJaXxOppXnZf .marker.cross{stroke:#333333;}#mermaid-svg-Ppr2aJaXxOppXnZf svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-Ppr2aJaXxOppXnZf p{margin:0;}#mermaid-svg-Ppr2aJaXxOppXnZf .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-Ppr2aJaXxOppXnZf .cluster-label text{fill:#333;}#mermaid-svg-Ppr2aJaXxOppXnZf .cluster-label span{color:#333;}#mermaid-svg-Ppr2aJaXxOppXnZf .cluster-label span p{background-color:transparent;}#mermaid-svg-Ppr2aJaXxOppXnZf .label text,#mermaid-svg-Ppr2aJaXxOppXnZf span{fill:#333;color:#333;}#mermaid-svg-Ppr2aJaXxOppXnZf .node rect,#mermaid-svg-Ppr2aJaXxOppXnZf .node circle,#mermaid-svg-Ppr2aJaXxOppXnZf .node ellipse,#mermaid-svg-Ppr2aJaXxOppXnZf .node polygon,#mermaid-svg-Ppr2aJaXxOppXnZf .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-Ppr2aJaXxOppXnZf .rough-node .label text,#mermaid-svg-Ppr2aJaXxOppXnZf .node .label text,#mermaid-svg-Ppr2aJaXxOppXnZf .image-shape .label,#mermaid-svg-Ppr2aJaXxOppXnZf .icon-shape .label{text-anchor:middle;}#mermaid-svg-Ppr2aJaXxOppXnZf .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-Ppr2aJaXxOppXnZf .rough-node .label,#mermaid-svg-Ppr2aJaXxOppXnZf .node .label,#mermaid-svg-Ppr2aJaXxOppXnZf .image-shape .label,#mermaid-svg-Ppr2aJaXxOppXnZf .icon-shape .label{text-align:center;}#mermaid-svg-Ppr2aJaXxOppXnZf .node.clickable{cursor:pointer;}#mermaid-svg-Ppr2aJaXxOppXnZf .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-Ppr2aJaXxOppXnZf .arrowheadPath{fill:#333333;}#mermaid-svg-Ppr2aJaXxOppXnZf .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-Ppr2aJaXxOppXnZf .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-Ppr2aJaXxOppXnZf .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Ppr2aJaXxOppXnZf .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-Ppr2aJaXxOppXnZf .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Ppr2aJaXxOppXnZf .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-Ppr2aJaXxOppXnZf .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-Ppr2aJaXxOppXnZf .cluster text{fill:#333;}#mermaid-svg-Ppr2aJaXxOppXnZf .cluster span{color:#333;}#mermaid-svg-Ppr2aJaXxOppXnZf 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-Ppr2aJaXxOppXnZf .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-Ppr2aJaXxOppXnZf rect.text{fill:none;stroke-width:0;}#mermaid-svg-Ppr2aJaXxOppXnZf .icon-shape,#mermaid-svg-Ppr2aJaXxOppXnZf .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Ppr2aJaXxOppXnZf .icon-shape p,#mermaid-svg-Ppr2aJaXxOppXnZf .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-Ppr2aJaXxOppXnZf .icon-shape .label rect,#mermaid-svg-Ppr2aJaXxOppXnZf .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Ppr2aJaXxOppXnZf .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-Ppr2aJaXxOppXnZf .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-Ppr2aJaXxOppXnZf :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} k8s-device-plugin 系统架构
CLI 命令行参数
config/v1 配置API模块
YAML/JSON 配置文件
环境变量
Plugin Server 运行时
GFD GPU Feature Discovery
Device Manager 设备管理
CDI Spec Generator
Kubelet Device Plugin API
Node Labels
GPU/MIG 设备枚举
Container Runtime CDI
配置API模块处于系统的入口层 ,是所有运行时行为的数据源头。Plugin Server、GFD、Device Manager、CDI 生成器等核心组件均依赖此模块输出的 Config 对象。
二、模块整体结构
2.1 文件清单与职责
| 文件 | 行数 | 核心职责 |
|---|---|---|
config.go |
~120 | 顶层 Config 结构体、配置加载入口 NewConfig、文件解析 parseConfig |
consts.go |
~40 | 全局常量定义(资源名前缀、MIG策略、设备列表策略等) |
flags.go |
~170 | 命令行标志数据模型、CLI→Config 映射逻辑 UpdateFromCLIFlags |
resources.go |
~150 | 资源命名模型、GPU/MIG 资源模式匹配、通配符转正则 |
sharing.go |
~50 | 共享策略顶层模型、策略选择逻辑 |
strategy.go |
~60 | DeviceListStrategies 集合类型、CDI 启用判定 |
replicas.go |
~250 | 复制资源模型、设备引用类型、自定义 JSON 序列化 |
imex.go |
~45 | IMEX(fabric-attached设备)通道配置与校验 |
duration.go |
~90 | 自定义 Duration 类型,支持 "infinite" 特殊值 |
2.2 核心类型结构图(类图)
#mermaid-svg-2DiRU7fWgCgv22dI{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-2DiRU7fWgCgv22dI .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-2DiRU7fWgCgv22dI .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-2DiRU7fWgCgv22dI .error-icon{fill:#552222;}#mermaid-svg-2DiRU7fWgCgv22dI .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-2DiRU7fWgCgv22dI .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-2DiRU7fWgCgv22dI .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-2DiRU7fWgCgv22dI .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-2DiRU7fWgCgv22dI .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-2DiRU7fWgCgv22dI .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-2DiRU7fWgCgv22dI .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-2DiRU7fWgCgv22dI .marker{fill:#333333;stroke:#333333;}#mermaid-svg-2DiRU7fWgCgv22dI .marker.cross{stroke:#333333;}#mermaid-svg-2DiRU7fWgCgv22dI svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-2DiRU7fWgCgv22dI p{margin:0;}#mermaid-svg-2DiRU7fWgCgv22dI g.classGroup text{fill:#9370DB;stroke:none;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:10px;}#mermaid-svg-2DiRU7fWgCgv22dI g.classGroup text .title{font-weight:bolder;}#mermaid-svg-2DiRU7fWgCgv22dI .cluster-label text{fill:#333;}#mermaid-svg-2DiRU7fWgCgv22dI .cluster-label span{color:#333;}#mermaid-svg-2DiRU7fWgCgv22dI .cluster-label span p{background-color:transparent;}#mermaid-svg-2DiRU7fWgCgv22dI .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-2DiRU7fWgCgv22dI .cluster text{fill:#333;}#mermaid-svg-2DiRU7fWgCgv22dI .cluster span{color:#333;}#mermaid-svg-2DiRU7fWgCgv22dI .nodeLabel,#mermaid-svg-2DiRU7fWgCgv22dI .edgeLabel{color:#131300;}#mermaid-svg-2DiRU7fWgCgv22dI .edgeLabel .label rect{fill:#ECECFF;}#mermaid-svg-2DiRU7fWgCgv22dI .label text{fill:#131300;}#mermaid-svg-2DiRU7fWgCgv22dI .labelBkg{background:#ECECFF;}#mermaid-svg-2DiRU7fWgCgv22dI .edgeLabel .label span{background:#ECECFF;}#mermaid-svg-2DiRU7fWgCgv22dI .classTitle{font-weight:bolder;}#mermaid-svg-2DiRU7fWgCgv22dI .node rect,#mermaid-svg-2DiRU7fWgCgv22dI .node circle,#mermaid-svg-2DiRU7fWgCgv22dI .node ellipse,#mermaid-svg-2DiRU7fWgCgv22dI .node polygon,#mermaid-svg-2DiRU7fWgCgv22dI .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-2DiRU7fWgCgv22dI .divider{stroke:#9370DB;stroke-width:1;}#mermaid-svg-2DiRU7fWgCgv22dI g.clickable{cursor:pointer;}#mermaid-svg-2DiRU7fWgCgv22dI g.classGroup rect{fill:#ECECFF;stroke:#9370DB;}#mermaid-svg-2DiRU7fWgCgv22dI g.classGroup line{stroke:#9370DB;stroke-width:1;}#mermaid-svg-2DiRU7fWgCgv22dI .classLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.5;}#mermaid-svg-2DiRU7fWgCgv22dI .classLabel .label{fill:#9370DB;font-size:10px;}#mermaid-svg-2DiRU7fWgCgv22dI .relation{stroke:#333333;stroke-width:1;fill:none;}#mermaid-svg-2DiRU7fWgCgv22dI .dashed-line{stroke-dasharray:3;}#mermaid-svg-2DiRU7fWgCgv22dI .dotted-line{stroke-dasharray:1 2;}#mermaid-svg-2DiRU7fWgCgv22dI #compositionStart,#mermaid-svg-2DiRU7fWgCgv22dI .composition{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-2DiRU7fWgCgv22dI #compositionEnd,#mermaid-svg-2DiRU7fWgCgv22dI .composition{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-2DiRU7fWgCgv22dI #dependencyStart,#mermaid-svg-2DiRU7fWgCgv22dI .dependency{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-2DiRU7fWgCgv22dI #dependencyStart,#mermaid-svg-2DiRU7fWgCgv22dI .dependency{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-2DiRU7fWgCgv22dI #extensionStart,#mermaid-svg-2DiRU7fWgCgv22dI .extension{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-2DiRU7fWgCgv22dI #extensionEnd,#mermaid-svg-2DiRU7fWgCgv22dI .extension{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-2DiRU7fWgCgv22dI #aggregationStart,#mermaid-svg-2DiRU7fWgCgv22dI .aggregation{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-2DiRU7fWgCgv22dI #aggregationEnd,#mermaid-svg-2DiRU7fWgCgv22dI .aggregation{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-2DiRU7fWgCgv22dI #lollipopStart,#mermaid-svg-2DiRU7fWgCgv22dI .lollipop{fill:#ECECFF!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-2DiRU7fWgCgv22dI #lollipopEnd,#mermaid-svg-2DiRU7fWgCgv22dI .lollipop{fill:#ECECFF!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-2DiRU7fWgCgv22dI .edgeTerminals{font-size:11px;line-height:initial;}#mermaid-svg-2DiRU7fWgCgv22dI .classTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-2DiRU7fWgCgv22dI .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-2DiRU7fWgCgv22dI .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-2DiRU7fWgCgv22dI :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Config
+string Version
+Flags Flags
+Resources Resources
+Sharing Sharing
+Imex Imex
Flags
+CommandLineFlags CommandLineFlags
CommandLineFlags
+*string MigStrategy
+*bool FailOnInitError
+*string MpsRoot
+*string NvidiaDriverRoot
+*string NvidiaDevRoot
+*bool GDRCopyEnabled
+*bool GDSEnabled
+*bool MOFEDEnabled
+*bool UseNodeFeatureAPI
+*string DeviceDiscoveryStrategy
+*PluginCommandLineFlags Plugin
+*GFDCommandLineFlags GFD
PluginCommandLineFlags
+*bool PassDeviceSpecs
+*deviceListStrategyFlag DeviceListStrategy
+*string DeviceIDStrategy
+*string CDIAnnotationPrefix
+*string NvidiaCTKPath
+*string ContainerDriverRoot
GFDCommandLineFlags
+*bool Oneshot
+*bool NoTimestamp
+*Duration SleepInterval
+*string OutputFile
+*string MachineTypeFile
Resources
+\[\]Resource GPUs
+\[\]Resource MIGs
Resource
+ResourcePattern Pattern
+ResourceName Name
Sharing
+ReplicatedResources TimeSlicing
+*ReplicatedResources MPS
ReplicatedResources
+bool RenameByDefault
+bool FailRequestsGreaterThanOne
+\[\]ReplicatedResource Resources
ReplicatedResource
+ResourceName Name
+ResourceName Rename
+ReplicatedDevices Devices
+int Replicas
ReplicatedDevices
+bool All
+int Count
+\[\]ReplicatedDeviceRef List
Imex
+\[\]int ChannelIDs
+bool Required
Duration
-time.Duration inner
ResourceName
ResourcePattern
2.3 核心方法清单
| 类型 | 方法 | 作用 |
|---|---|---|
Config (函数) |
NewConfig(c *cli.Context, flags []cli.Flag) (*Config, error) |
配置加载入口,合并文件+CLI配置 |
Config (函数) |
DisableResourceNamingInConfig(config *Config) |
临时禁用资源重命名功能 |
Config (函数) |
parseConfig(configFile string) (*Config, error) |
打开并解析配置文件 |
Config (函数) |
parseConfigFrom(reader io.Reader) (*Config, error) |
从 Reader 读取并反序列化 YAML |
Flags |
UpdateFromCLIFlags(c *cli.Context, flags []cli.Flag) |
用 CLI 标志值更新 Flags 结构 |
Sharing |
SharingStrategy() SharingStrategy |
返回当前生效的共享策略 |
Sharing |
ReplicatedResources() *ReplicatedResources |
返回当前共享策略对应的资源列表 |
ResourceName |
Split() (string, string) |
拆分资源名为前缀和名称 |
ResourceName |
DefaultSharedRename() ResourceName |
返回默认共享重命名名称 |
ResourcePattern |
Matches(s string) bool |
通配符模式匹配 |
ReplicatedResources |
isReplicated() bool |
判断是否有复制(replicas>1) |
ReplicatedResources |
disableResoureRenaming(id string) |
禁用资源重命名和设备选择 |
ReplicatedDeviceRef |
IsGPUIndex() / IsMigIndex() / IsUUID() / IsGpuUUID() / IsMigUUID() |
设备引用类型判定 |
DeviceListStrategies |
Includes(strategy string) bool |
判断是否包含某策略 |
DeviceListStrategies |
AnyCDIEnabled() / AllCDIEnabled() bool |
CDI 策略启用判定 |
Duration |
IsInfinite() bool |
判断是否为无限时长 |
Duration |
MarshalJSON() / UnmarshalJSON() |
自定义 JSON 序列化 |
DurationValue |
Set(value string) error |
CLI Generic 接口实现 |
Imex (函数) |
AssertChannelIDsValid(ids []int) error |
校验 IMEX 通道ID列表 |
2.4 数据流入流出
#mermaid-svg-xWR1X1QpKZYToAeE{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-xWR1X1QpKZYToAeE .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-xWR1X1QpKZYToAeE .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-xWR1X1QpKZYToAeE .error-icon{fill:#552222;}#mermaid-svg-xWR1X1QpKZYToAeE .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-xWR1X1QpKZYToAeE .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-xWR1X1QpKZYToAeE .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-xWR1X1QpKZYToAeE .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-xWR1X1QpKZYToAeE .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-xWR1X1QpKZYToAeE .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-xWR1X1QpKZYToAeE .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-xWR1X1QpKZYToAeE .marker{fill:#333333;stroke:#333333;}#mermaid-svg-xWR1X1QpKZYToAeE .marker.cross{stroke:#333333;}#mermaid-svg-xWR1X1QpKZYToAeE svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-xWR1X1QpKZYToAeE p{margin:0;}#mermaid-svg-xWR1X1QpKZYToAeE .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-xWR1X1QpKZYToAeE .cluster-label text{fill:#333;}#mermaid-svg-xWR1X1QpKZYToAeE .cluster-label span{color:#333;}#mermaid-svg-xWR1X1QpKZYToAeE .cluster-label span p{background-color:transparent;}#mermaid-svg-xWR1X1QpKZYToAeE .label text,#mermaid-svg-xWR1X1QpKZYToAeE span{fill:#333;color:#333;}#mermaid-svg-xWR1X1QpKZYToAeE .node rect,#mermaid-svg-xWR1X1QpKZYToAeE .node circle,#mermaid-svg-xWR1X1QpKZYToAeE .node ellipse,#mermaid-svg-xWR1X1QpKZYToAeE .node polygon,#mermaid-svg-xWR1X1QpKZYToAeE .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-xWR1X1QpKZYToAeE .rough-node .label text,#mermaid-svg-xWR1X1QpKZYToAeE .node .label text,#mermaid-svg-xWR1X1QpKZYToAeE .image-shape .label,#mermaid-svg-xWR1X1QpKZYToAeE .icon-shape .label{text-anchor:middle;}#mermaid-svg-xWR1X1QpKZYToAeE .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-xWR1X1QpKZYToAeE .rough-node .label,#mermaid-svg-xWR1X1QpKZYToAeE .node .label,#mermaid-svg-xWR1X1QpKZYToAeE .image-shape .label,#mermaid-svg-xWR1X1QpKZYToAeE .icon-shape .label{text-align:center;}#mermaid-svg-xWR1X1QpKZYToAeE .node.clickable{cursor:pointer;}#mermaid-svg-xWR1X1QpKZYToAeE .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-xWR1X1QpKZYToAeE .arrowheadPath{fill:#333333;}#mermaid-svg-xWR1X1QpKZYToAeE .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-xWR1X1QpKZYToAeE .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-xWR1X1QpKZYToAeE .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-xWR1X1QpKZYToAeE .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-xWR1X1QpKZYToAeE .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-xWR1X1QpKZYToAeE .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-xWR1X1QpKZYToAeE .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-xWR1X1QpKZYToAeE .cluster text{fill:#333;}#mermaid-svg-xWR1X1QpKZYToAeE .cluster span{color:#333;}#mermaid-svg-xWR1X1QpKZYToAeE 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-xWR1X1QpKZYToAeE .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-xWR1X1QpKZYToAeE rect.text{fill:none;stroke-width:0;}#mermaid-svg-xWR1X1QpKZYToAeE .icon-shape,#mermaid-svg-xWR1X1QpKZYToAeE .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-xWR1X1QpKZYToAeE .icon-shape p,#mermaid-svg-xWR1X1QpKZYToAeE .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-xWR1X1QpKZYToAeE .icon-shape .label rect,#mermaid-svg-xWR1X1QpKZYToAeE .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-xWR1X1QpKZYToAeE .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-xWR1X1QpKZYToAeE .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-xWR1X1QpKZYToAeE :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 输出
处理
输入
config-file YAML/JSON
CLI Flags
环境变量
parseConfig
Config struct
UpdateFromCLIFlags
默认值填充/后处理
Config.Flags → Plugin/GFD 运行时
Config.Resources → Device Manager
Config.Sharing → Sharing Manager
Config.Imex → IMEX Controller
三、核心业务逻辑深度解析
3.1 config.go --- 顶层配置加载
3.1.1 Config 结构体
go
const Version = "v1"
type Config struct {
Version string `json:"version" yaml:"version"`
Flags Flags `json:"flags,omitempty" yaml:"flags,omitempty"`
Resources Resources `json:"resources,omitempty" yaml:"resources,omitempty"`
Sharing Sharing `json:"sharing,omitempty" yaml:"sharing,omitempty"`
Imex Imex `json:"imex,omitempty" yaml:"imex,omitempty"`
}
逐行解析:
Version = "v1":配置结构体版本标识,用于未来配置迁移和版本校验。Version字段:配置文件中必须声明"version": "v1",解析时会校验。Flags:命令行标志配置(非可选,总是存在)。Resources:GPU/MIG 资源命名映射(可选,omitempty表示省略时使用零值)。Sharing:GPU 共享策略配置(可选)。Imex:IMEX 通道配置(可选,v0.19 新增的 fabric-attached 设备支持)。
设计意图: 使用 omitempty 标签使得最小化配置文件只需声明版本号,所有其他字段都有合理的默认零值。
3.1.2 NewConfig --- 配置加载入口
go
func NewConfig(c *cli.Context, flags []cli.Flag) (*Config, error) {
config := &Config{Version: Version}
if configFile := c.String("config-file"); configFile != "" {
var err error
config, err = parseConfig(configFile)
if err != nil {
return nil, fmt.Errorf("unable to parse config file: %v", err)
}
}
逐行解析:
- 初始化默认 Config,设置
Version = "v1"。 - 检查 CLI 是否传入
--config-file。 - 如果传入了配置文件,调用
parseConfig解析,完全替换默认 config(而非合并)。 - 解析失败返回包装错误。
go
config.Flags.UpdateFromCLIFlags(c, flags)
- 调用
UpdateFromCLIFlags用命令行标志覆盖配置文件中的值。这是优先级合并的核心------CLI 标志会覆盖配置文件的值。
go
if c.IsSet("imex-channel-ids") {
config.Imex.ChannelIDs = c.IntSlice("imex-channel-ids")
}
if c.IsSet("imex-required") {
config.Imex.Required = c.Bool("imex-required")
}
- IMEX 通道配置的 CLI 覆盖。注释
// TODO: This is currently not at the flags level?表明这段代码的位置存在架构争议------IMEX 配置应该像其他 Flags 一样归入 Flags 结构体,但目前直接挂在 Config 层级。
go
if config.Flags.NvidiaDevRoot == nil || *config.Flags.NvidiaDevRoot == "" {
config.Flags.NvidiaDevRoot = config.Flags.NvidiaDriverRoot
}
- 默认值后处理 :如果
NvidiaDevRoot(设备节点路径)未设置,则默认使用NvidiaDriverRoot(驱动根路径)。这处理了大多数场景下设备节点和驱动在同一根目录的情况。
go
if config.Sharing.MPS != nil {
config.Sharing.MPS.FailRequestsGreaterThanOne = true
}
- 强制安全策略 :如果启用了 MPS 共享,强制设置
FailRequestsGreaterThanOne = true。注释解释:单 GPU 场景可以放宽,但需要额外逻辑判断何时可以合并请求,且会使请求语义不清晰。因此默认保守策略------拒绝请求超过1个 GPU。
3.1.3 parseConfig --- 文件解析
go
func parseConfig(configFile string) (*Config, error) {
reader, err := os.Open(configFile)
// ...
defer reader.Close()
config, err := parseConfigFrom(reader)
// ...
return config, nil
}
打开配置文件,委托给 parseConfigFrom 处理实际的读取和反序列化。使用 defer 确保文件句柄关闭。
go
func parseConfigFrom(reader io.Reader) (*Config, error) {
configYaml, err := io.ReadAll(reader)
// ...
var config Config
err = yaml.Unmarshal(configYaml, &config)
// ...
if config.Version == "" {
config.Version = Version
}
if config.Version != Version {
return nil, fmt.Errorf("unknown version: %v", config.Version)
}
return &config, nil
}
逐行解析:
io.ReadAll读取全部内容到[]byte。yaml.Unmarshal使用sigs.k8s.io/yaml库反序列化。该库支持 YAML 和 JSON 两种格式(JSON 是 YAML 的子集),因此函数注释说 "either YAML of JSON"。- 版本校验:空版本号自动填充为
"v1"(向后兼容),非"v1"版本直接报错。
3.1.4 DisableResourceNamingInConfig --- 禁用资源重命名
go
func DisableResourceNamingInConfig(config *Config) {
if len(config.Resources.GPUs) > 0 || len(config.Resources.MIGs) > 0 {
klog.Warning("Customizing the 'resources' field is not yet supported in the config. Ignoring...")
}
config.Resources.GPUs = nil
config.Resources.MIGs = nil
config.Sharing.TimeSlicing.disableResoureRenaming("timeSlicing")
config.Sharing.MPS.disableResoureRenaming("mps")
}
设计意图: 这是一个功能降级函数。资源重命名功能在当前版本被临时禁用(注释说 "may be reenabled in a future release"),但数据结构仍然保留。此函数:
- 清空 Resources 中的 GPU/MIG 自定义映射,并打印警告。
- 对 TimeSlicing 和 MPS 的 ReplicatedResources 调用
disableResoureRenaming,禁用其中的重命名和设备选择。
3.2 consts.go --- 常量定义
go
const (
ResourceNamePrefix = "nvidia.com"
DefaultSharedResourceNameSuffix = ".shared"
MaxResourceNameLength = 63
)
ResourceNamePrefix:Kubernetes Extended Resource 名称前缀,所有 NVIDIA 资源名必须以nvidia.com/开头。DefaultSharedResourceNameSuffix:共享资源默认重命名后缀,例如nvidia.com/gpu.shared。MaxResourceNameLength = 63:Kubernetes 资源名最大长度限制(对应 DNS 子域名长度)。
go
const (
MigStrategyNone = "none"
MigStrategySingle = "single"
MigStrategyMixed = "mixed"
)
MIG(Multi-Instance GPU)策略:
none:不使用 MIG,GPU 作为完整设备暴露。single:所有 GPU 使用相同的 MIG 切片配置。mixed:不同 GPU 可使用不同的 MIG 切片配置。
go
const (
DeviceListStrategyEnvVar = "envvar"
DeviceListStrategyVolumeMounts = "volume-mounts"
DeviceListStrategyCDIAnnotations = "cdi-annotations"
DeviceListStrategyCDICRI = "cdi-cri"
)
设备列表传递策略(控制如何将 GPU 设备信息传递给容器):
envvar:通过环境变量NVIDIA_VISIBLE_DEVICES传递。volume-mounts:通过 VolumeMounts 传递设备列表文件。cdi-annotations:通过 CDI(Container Device Interface)注解传递。cdi-cri:通过 CRI(Container Runtime Interface)原生 CDI 支持传递。
go
const (
DeviceIDStrategyUUID = "uuid"
DeviceIDStrategyIndex = "index"
)
设备标识策略:使用 UUID 还是 GPU 索引来标识设备。
go
const (
DefaultCDIAnnotationPrefix = cdiapi.AnnotationPrefix
DefaultNvidiaCTKPath = "/usr/bin/nvidia-ctk"
DefaultContainerDriverRoot = "/driver-root"
)
CDI 相关默认值,引用了 tags.cncf.io/container-device-interface/pkg/cdi 包的 AnnotationPrefix 常量。
3.3 flags.go --- 命令行标志模型
3.3.1 泛型辅助函数
go
func ptr[T any](x T) *T {
return &x
}
Go 1.18+ 泛型函数,返回任意类型值的指针。用于将 CLI 标志值转换为指针存储。
go
func updateFromCLIFlag[T any](pflag **T, c *cli.Context, flagName string) {
if c.IsSet(flagName) || *pflag == (*T)(nil) {
switch flag := any(pflag).(type) {
case **string:
*flag = ptr(c.String(flagName))
case **[]string:
*flag = ptr(c.StringSlice(flagName))
case **bool:
*flag = ptr(c.Bool(flagName))
case **Duration:
if gf, ok := c.Generic(flagName).(*DurationValue); ok && gf.Value != nil {
*flag = gf.Value
}
case **deviceListStrategyFlag:
*flag = ptr((deviceListStrategyFlag)(c.StringSlice(flagName)))
default:
panic(fmt.Errorf("unsupported flag type for %v: %T", flagName, flag))
}
}
}
逐行解析:
- 泛型函数,接受
**T(指向指针的指针),实现"如果 CLI 设置了该标志 或 配置中该字段为 nil(未设置),则用 CLI 值更新"。 c.IsSet(flagName):用户在命令行显式设置了该标志 → 覆盖配置文件的值。*pflag == (*T)(nil):配置文件未设置该字段 → 用 CLI 默认值填充。- 类型断言分支处理不同类型的标志值提取。
Duration类型特殊处理:通过cli.Generic接口获取自定义的DurationValue。deviceListStrategyFlag特殊处理:从StringSlice转换。default分支panic:遇到不支持的类型直接 panic,这是设计期类型安全保证------新增标志类型必须在此添加处理分支。
设计意图: 该函数是优先级合并 的核心机制。条件 c.IsSet(flagName) || *pflag == nil 实现了:CLI 显式设置 → 总是覆盖;CLI 未设置但配置文件也未设置 → 用 CLI 默认值填充;CLI 未设置但配置文件已设置 → 保留配置文件值。
3.3.2 Flags 结构体层级
go
type Flags struct {
CommandLineFlags
}
Flags 仅嵌套 CommandLineFlags,没有额外字段。这种设计允许未来在 Flags 层级添加非命令行标志(如内部计算标志),同时保持 CommandLineFlags 的纯命令行映射。
go
type CommandLineFlags struct {
MigStrategy *string
FailOnInitError *bool
MpsRoot *string
NvidiaDriverRoot *string
NvidiaDevRoot *string
GDRCopyEnabled *bool
GDSEnabled *bool
MOFEDEnabled *bool
UseNodeFeatureAPI *bool
DeviceDiscoveryStrategy *string
Plugin *PluginCommandLineFlags
GFD *GFDCommandLineFlags
}
所有字段都是指针类型------这是关键设计决策:
nil表示"未设置",可以区分"用户未设置"和"用户设置为零值(如 false/空字符串)"。- 这使得优先级合并逻辑可以精确判断哪些值来自配置文件、哪些需要从 CLI 填充。
3.3.3 deviceListStrategyFlag 自定义类型
go
type deviceListStrategyFlag []string
func (f *deviceListStrategyFlag) UnmarshalJSON(b []byte) error {
var single string
err := json.Unmarshal(b, &single)
if err == nil {
*f = []string{single}
return nil
}
var multi []string
if err := json.Unmarshal(b, &multi); err == nil {
*f = multi
return nil
}
return fmt.Errorf("invalid deviceListStrategy: %v", string(b))
}
设计意图: deviceListStrategy 在配置文件中可以写成单字符串 "envvar" 或字符串列表 ["envvar", "cdi-annotations"]。自定义反序列化器先尝试单字符串解析,失败后再尝试列表解析,两者都失败则报错。
3.3.4 UpdateFromCLIFlags 方法
go
func (f *Flags) UpdateFromCLIFlags(c *cli.Context, flags []cli.Flag) {
for _, flag := range flags {
for _, n := range flag.Names() {
switch n {
case "mig-strategy":
updateFromCLIFlag(&f.MigStrategy, c, n)
// ... 其他 common flags
}
// Plugin specific flags
if f.Plugin == nil {
f.Plugin = &PluginCommandLineFlags{}
}
switch n {
case "pass-device-specs":
updateFromCLIFlag(&f.Plugin.PassDeviceSpecs, c, n)
// ... 其他 plugin flags
}
// GFD specific flags
if f.GFD == nil {
f.GFD = &GFDCommandLineFlags{}
}
switch n {
// ... gfd flags
}
}
}
}
执行流程:
- 遍历所有注册的 CLI 标志。
- 每个标志可能有多个名称(如
"driver-root"和"nvidia-driver-root"),遍历所有名称。 - 按三类分组匹配:通用标志、Plugin 专属标志、GFD 专属标志。
- 匹配到后调用
updateFromCLIFlag进行条件更新。 - Plugin 和 GFD 子结构按需懒初始化。
#mermaid-svg-bLHrFypLAFfsGUOG{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-bLHrFypLAFfsGUOG .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-bLHrFypLAFfsGUOG .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-bLHrFypLAFfsGUOG .error-icon{fill:#552222;}#mermaid-svg-bLHrFypLAFfsGUOG .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-bLHrFypLAFfsGUOG .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-bLHrFypLAFfsGUOG .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-bLHrFypLAFfsGUOG .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-bLHrFypLAFfsGUOG .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-bLHrFypLAFfsGUOG .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-bLHrFypLAFfsGUOG .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-bLHrFypLAFfsGUOG .marker{fill:#333333;stroke:#333333;}#mermaid-svg-bLHrFypLAFfsGUOG .marker.cross{stroke:#333333;}#mermaid-svg-bLHrFypLAFfsGUOG svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-bLHrFypLAFfsGUOG p{margin:0;}#mermaid-svg-bLHrFypLAFfsGUOG .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-bLHrFypLAFfsGUOG .cluster-label text{fill:#333;}#mermaid-svg-bLHrFypLAFfsGUOG .cluster-label span{color:#333;}#mermaid-svg-bLHrFypLAFfsGUOG .cluster-label span p{background-color:transparent;}#mermaid-svg-bLHrFypLAFfsGUOG .label text,#mermaid-svg-bLHrFypLAFfsGUOG span{fill:#333;color:#333;}#mermaid-svg-bLHrFypLAFfsGUOG .node rect,#mermaid-svg-bLHrFypLAFfsGUOG .node circle,#mermaid-svg-bLHrFypLAFfsGUOG .node ellipse,#mermaid-svg-bLHrFypLAFfsGUOG .node polygon,#mermaid-svg-bLHrFypLAFfsGUOG .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-bLHrFypLAFfsGUOG .rough-node .label text,#mermaid-svg-bLHrFypLAFfsGUOG .node .label text,#mermaid-svg-bLHrFypLAFfsGUOG .image-shape .label,#mermaid-svg-bLHrFypLAFfsGUOG .icon-shape .label{text-anchor:middle;}#mermaid-svg-bLHrFypLAFfsGUOG .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-bLHrFypLAFfsGUOG .rough-node .label,#mermaid-svg-bLHrFypLAFfsGUOG .node .label,#mermaid-svg-bLHrFypLAFfsGUOG .image-shape .label,#mermaid-svg-bLHrFypLAFfsGUOG .icon-shape .label{text-align:center;}#mermaid-svg-bLHrFypLAFfsGUOG .node.clickable{cursor:pointer;}#mermaid-svg-bLHrFypLAFfsGUOG .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-bLHrFypLAFfsGUOG .arrowheadPath{fill:#333333;}#mermaid-svg-bLHrFypLAFfsGUOG .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-bLHrFypLAFfsGUOG .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-bLHrFypLAFfsGUOG .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-bLHrFypLAFfsGUOG .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-bLHrFypLAFfsGUOG .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-bLHrFypLAFfsGUOG .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-bLHrFypLAFfsGUOG .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-bLHrFypLAFfsGUOG .cluster text{fill:#333;}#mermaid-svg-bLHrFypLAFfsGUOG .cluster span{color:#333;}#mermaid-svg-bLHrFypLAFfsGUOG 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-bLHrFypLAFfsGUOG .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-bLHrFypLAFfsGUOG rect.text{fill:none;stroke-width:0;}#mermaid-svg-bLHrFypLAFfsGUOG .icon-shape,#mermaid-svg-bLHrFypLAFfsGUOG .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-bLHrFypLAFfsGUOG .icon-shape p,#mermaid-svg-bLHrFypLAFfsGUOG .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-bLHrFypLAFfsGUOG .icon-shape .label rect,#mermaid-svg-bLHrFypLAFfsGUOG .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-bLHrFypLAFfsGUOG .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-bLHrFypLAFfsGUOG .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-bLHrFypLAFfsGUOG :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 通用
Plugin
是
否
GFD
是
否
遍历 flags 列表
遍历每个 flag 的 names
name 属于哪类?
updateFromCLIFlag 更新 CommandLineFlags
Plugin == nil?
初始化 PluginCommandLineFlags
updateFromCLIFlag 更新 Plugin flags
GFD == nil?
初始化 GFDCommandLineFlags
updateFromCLIFlag 更新 GFD flags
3.4 resources.go --- 资源命名模型
3.4.1 类型定义
go
type ResourcePattern string // 通配符模式,如 "*"
type ResourceName string // Kubernetes 资源名,如 "nvidia.com/gpu"
3.4.2 NewResourceName --- 资源名校验
go
func NewResourceName(n string) (ResourceName, error) {
if !strings.HasPrefix(n, ResourceNamePrefix+"/") {
n = ResourceNamePrefix + "/" + n
}
如果未带前缀 nvidia.com/,自动补全。
go
if len(n) > MaxResourceNameLength {
return "", fmt.Errorf("fully-qualified resource name must be %v characters or less: %v", MaxResourceNameLength, n)
}
长度校验:Kubernetes 资源名不超过 63 字符。
go
_, name := ResourceName(n).Split()
invalid := k8s.NameIsDNSSubdomain(name, false)
if len(invalid) != 0 {
return "", fmt.Errorf("incorrect format for resource name '%v': %v", n, invalid)
}
调用 Kubernetes API 的 NameIsDNSSubdomain 校验名称是否符合 DNS 子域名规范。invalid 是错误消息切片,非空表示有校验错误。
3.4.3 Split 与 DefaultSharedRename
go
func (r ResourceName) Split() (string, string) {
split := strings.SplitN(string(r), "/", 2)
if len(split) != 2 {
return "", string(r)
}
return split[0], split[1]
}
func (r ResourceName) DefaultSharedRename() ResourceName {
return r + DefaultSharedResourceNameSuffix
}
Split:"nvidia.com/gpu"→("nvidia.com", "gpu")。DefaultSharedRename:"nvidia.com/gpu"→"nvidia.com/gpu.shared"。
3.4.4 Resource.UnmarshalJSON --- 自定义反序列化
go
func (r *Resource) UnmarshalJSON(b []byte) error {
res := make(map[string]json.RawMessage)
err := json.Unmarshal(b, &res)
// ...
pattern, patternExists := res["pattern"]
name, nameExists := res["name"]
if !patternExists {
return fmt.Errorf("resources must have a 'pattern' field set")
}
if !nameExists {
return fmt.Errorf("resources must have a 'name' field set")
}
// ...
}
设计意图: 自定义反序列化确保 pattern 和 name 两个字段都必须存在。标准 Go JSON 反序列化不会区分"字段不存在"和"字段为零值",这里通过 map[string]json.RawMessage 精确检测字段存在性。
3.4.5 ResourcePattern.Matches --- 通配符匹配
go
func (p ResourcePattern) Matches(s string) bool {
result, _ := regexp.MatchString(wildCardToRegexp(string(p)), s)
return result
}
func wildCardToRegexp(pattern string) string {
var result strings.Builder
for i, literal := range strings.Split(pattern, "*") {
if i > 0 {
result.WriteString(".*")
}
result.WriteString(regexp.QuoteMeta(literal))
}
return result.String()
}
逐行解析:
wildCardToRegexp将通配符模式转为正则表达式:按*分割,每段用regexp.QuoteMeta转义正则元字符,*替换为.*。- 例如
"*"→".*","gpu-*"→"gpu\-.*"。 Matches使用regexp.MatchString进行匹配,忽略错误(正则已保证合法)。
3.5 sharing.go --- 共享策略模型
go
type Sharing struct {
TimeSlicing ReplicatedResources `json:"timeSlicing,omitempty" yaml:"timeSlicing,omitempty"`
MPS *ReplicatedResources `json:"mps,omitempty" yaml:"mps,omitempty"`
}
关键设计差异:
TimeSlicing是值类型(非指针),始终存在,即使未配置也是空结构体。MPS是指针类型 ,nil表示未启用 MPS。这允许通过MPS != nil判断 MPS 是否配置。
go
type SharingStrategy string
const (
SharingStrategyMPS = SharingStrategy("mps")
SharingStrategyNone = SharingStrategy("none")
SharingStrategyTimeSlicing = SharingStrategy("time-slicing")
)
func (s *Sharing) SharingStrategy() SharingStrategy {
if s.MPS != nil && s.MPS.isReplicated() {
return SharingStrategyMPS
}
if s.TimeSlicing.isReplicated() {
return SharingStrategyTimeSlicing
}
return SharingStrategyNone
}
策略选择逻辑:
- MPS 优先:如果 MPS 已配置且存在 replicas > 1 的资源,返回 MPS 策略。
- 其次 TimeSlicing:如果 TimeSlicing 存在 replicas > 1 的资源,返回 TimeSlicing 策略。
- 否则返回 None。
go
func (s *Sharing) ReplicatedResources() *ReplicatedResources {
if s.MPS != nil {
return s.MPS
}
return &s.TimeSlicing
}
返回当前共享策略对应的 ReplicatedResources。MPS 优先(即使 MPS 未实际复制,只要非 nil 就返回 MPS)。这与 SharingStrategy() 的逻辑有微妙差异------后者额外检查 isReplicated()。
#mermaid-svg-yEWkFiEQIbhWEITS{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-yEWkFiEQIbhWEITS .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-yEWkFiEQIbhWEITS .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-yEWkFiEQIbhWEITS .error-icon{fill:#552222;}#mermaid-svg-yEWkFiEQIbhWEITS .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-yEWkFiEQIbhWEITS .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-yEWkFiEQIbhWEITS .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-yEWkFiEQIbhWEITS .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-yEWkFiEQIbhWEITS .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-yEWkFiEQIbhWEITS .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-yEWkFiEQIbhWEITS .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-yEWkFiEQIbhWEITS .marker{fill:#333333;stroke:#333333;}#mermaid-svg-yEWkFiEQIbhWEITS .marker.cross{stroke:#333333;}#mermaid-svg-yEWkFiEQIbhWEITS svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-yEWkFiEQIbhWEITS p{margin:0;}#mermaid-svg-yEWkFiEQIbhWEITS .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-yEWkFiEQIbhWEITS .cluster-label text{fill:#333;}#mermaid-svg-yEWkFiEQIbhWEITS .cluster-label span{color:#333;}#mermaid-svg-yEWkFiEQIbhWEITS .cluster-label span p{background-color:transparent;}#mermaid-svg-yEWkFiEQIbhWEITS .label text,#mermaid-svg-yEWkFiEQIbhWEITS span{fill:#333;color:#333;}#mermaid-svg-yEWkFiEQIbhWEITS .node rect,#mermaid-svg-yEWkFiEQIbhWEITS .node circle,#mermaid-svg-yEWkFiEQIbhWEITS .node ellipse,#mermaid-svg-yEWkFiEQIbhWEITS .node polygon,#mermaid-svg-yEWkFiEQIbhWEITS .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-yEWkFiEQIbhWEITS .rough-node .label text,#mermaid-svg-yEWkFiEQIbhWEITS .node .label text,#mermaid-svg-yEWkFiEQIbhWEITS .image-shape .label,#mermaid-svg-yEWkFiEQIbhWEITS .icon-shape .label{text-anchor:middle;}#mermaid-svg-yEWkFiEQIbhWEITS .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-yEWkFiEQIbhWEITS .rough-node .label,#mermaid-svg-yEWkFiEQIbhWEITS .node .label,#mermaid-svg-yEWkFiEQIbhWEITS .image-shape .label,#mermaid-svg-yEWkFiEQIbhWEITS .icon-shape .label{text-align:center;}#mermaid-svg-yEWkFiEQIbhWEITS .node.clickable{cursor:pointer;}#mermaid-svg-yEWkFiEQIbhWEITS .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-yEWkFiEQIbhWEITS .arrowheadPath{fill:#333333;}#mermaid-svg-yEWkFiEQIbhWEITS .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-yEWkFiEQIbhWEITS .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-yEWkFiEQIbhWEITS .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-yEWkFiEQIbhWEITS .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-yEWkFiEQIbhWEITS .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-yEWkFiEQIbhWEITS .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-yEWkFiEQIbhWEITS .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-yEWkFiEQIbhWEITS .cluster text{fill:#333;}#mermaid-svg-yEWkFiEQIbhWEITS .cluster span{color:#333;}#mermaid-svg-yEWkFiEQIbhWEITS 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-yEWkFiEQIbhWEITS .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-yEWkFiEQIbhWEITS rect.text{fill:none;stroke-width:0;}#mermaid-svg-yEWkFiEQIbhWEITS .icon-shape,#mermaid-svg-yEWkFiEQIbhWEITS .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-yEWkFiEQIbhWEITS .icon-shape p,#mermaid-svg-yEWkFiEQIbhWEITS .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-yEWkFiEQIbhWEITS .icon-shape .label rect,#mermaid-svg-yEWkFiEQIbhWEITS .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-yEWkFiEQIbhWEITS .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-yEWkFiEQIbhWEITS .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-yEWkFiEQIbhWEITS :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 是
否
是
否
Sharing.SharingStrategy
MPS != nil 且 MPS.isReplicated?
返回 SharingStrategyMPS
TimeSlicing.isReplicated?
返回 SharingStrategyTimeSlicing
返回 SharingStrategyNone
3.6 strategy.go --- 设备列表策略
go
type DeviceListStrategies map[string]bool
使用 map[string]bool 表示策略集合,键为策略名,值为是否启用。
go
func NewDeviceListStrategies(strategies []string) (DeviceListStrategies, error) {
ret := map[string]bool{
DeviceListStrategyEnvVar: false,
DeviceListStrategyVolumeMounts: false,
DeviceListStrategyCDIAnnotations: false,
DeviceListStrategyCDICRI: false,
}
for _, s := range strategies {
if _, ok := ret[s]; !ok {
return nil, fmt.Errorf("invalid strategy: %v", s)
}
ret[s] = true
}
return DeviceListStrategies(ret), nil
}
构造函数:初始化所有已知策略为 false,然后根据输入列表启用对应策略。未知策略名直接报错。
go
func (s DeviceListStrategies) AnyCDIEnabled() bool {
for k, v := range s {
if strings.HasPrefix(k, "cdi-") && v {
return true
}
}
return false
}
func (s DeviceListStrategies) AllCDIEnabled() bool {
for k, v := range s {
if !strings.HasPrefix(k, "cdi-") && v {
return false
}
}
return true
}
AnyCDIEnabled:任一cdi-*策略启用 → true。用于判断是否需要生成 CDI Spec。AllCDIEnabled:所有启用的策略都是cdi-*策略 → true。用于判断是否完全依赖 CDI(无需传统设备列表传递)。
注意 AllCDIEnabled 的逻辑微妙之处: 它检查的是"是否有非 CDI 策略被启用",如果有非 CDI 策略启用则返回 false。如果没有任何策略启用,返回 true(空集情况下循环不执行 return false)。
3.7 replicas.go --- 复制资源模型
这是配置模块中最复杂的文件,包含多个自定义 JSON 序列化/反序列化实现。
3.7.1 ReplicatedResources
go
type ReplicatedResources struct {
RenameByDefault bool `json:"renameByDefault,omitempty" yaml:"renameByDefault,omitempty"`
FailRequestsGreaterThanOne bool `json:"failRequestsGreaterThanOne,omitempty" yaml:"failRequestsGreaterThanOne,omitempty"`
Resources []ReplicatedResource `json:"resources,omitempty" yaml:"resources,omitempty"`
}
RenameByDefault:是否自动为共享资源添加.shared后缀。FailRequestsGreaterThanOne:是否拒绝请求超过1个 GPU(MPS 安全策略)。Resources:具体的复制资源配置列表。
3.7.2 disableResoureRenaming --- 禁用重命名
go
func (rrs *ReplicatedResources) disableResoureRenaming(id string) {
if rrs == nil {
return
}
renameByDefault := rrs.RenameByDefault
setsNonDefaultRename := false
setsDevices := false
for i, r := range rrs.Resources {
if !renameByDefault && r.Rename != "" {
setsNonDefaultRename = true
rrs.Resources[i].Rename = ""
}
if renameByDefault && r.Rename != r.Name.DefaultSharedRename() {
setsNonDefaultRename = true
rrs.Resources[i].Rename = r.Name.DefaultSharedRename()
}
if !r.Devices.All {
setsDevices = true
rrs.Resources[i].Devices.All = true
rrs.Resources[i].Devices.Count = 0
rrs.Resources[i].Devices.List = nil
}
}
if setsNonDefaultRename {
klog.Warningf("Setting the 'rename' field in sharing.%s.resources is not yet supported...", id)
}
if setsDevices {
klog.Warningf("Customizing the 'devices' field in sharing.%s.resources is not yet supported...", id)
}
}
逐行解析:
- nil 检查保护(MPS 可能为 nil)。
- 遍历所有 ReplicatedResource:
- 如果
RenameByDefault=false且用户设置了自定义 Rename → 清空 Rename,标记setsNonDefaultRename。 - 如果
RenameByDefault=true且 Rename 不等于默认共享名 → 强制设为默认共享名,标记setsNonDefaultRename。 - 如果用户自定义了设备选择(非 All)→ 强制设为 All,清空 Count 和 List,标记
setsDevices。
- 如果
- 打印警告日志告知用户哪些配置被忽略了。
设计意图: 这是功能降级的一部分。当前版本不允许用户自定义重命名和设备选择,但数据结构保留以支持未来版本。
3.7.3 isReplicated --- 判断是否实际复制
go
func (rrs *ReplicatedResources) isReplicated() bool {
if rrs == nil {
return false
}
for _, rr := range rrs.Resources {
if rr.Replicas > 1 {
return true
}
}
return false
}
只有存在 Replicas > 1 的资源才算真正启用了共享。Replicas = 1 意味着不复制,等同独占。
3.7.4 ReplicatedDeviceRef --- 设备引用类型判定
go
type ReplicatedDeviceRef string
设备引用是一个字符串,可以是以下格式之一:
go
func (d ReplicatedDeviceRef) IsGPUIndex() bool {
if _, err := strconv.ParseUint(string(d), 10, 0); err != nil {
return false
}
return true
}
IsGPUIndex:纯数字字符串,如 "0", "1"。
go
func (d ReplicatedDeviceRef) IsMigIndex() bool {
split := strings.SplitN(string(d), ":", 2)
if len(split) != 2 {
return false
}
for _, s := range split {
if _, err := strconv.ParseUint(s, 10, 0); err != nil {
return false
}
}
return true
}
IsMigIndex:格式 GPU索引:MIG索引,如 "0:0", "1:2"。
go
func (d ReplicatedDeviceRef) IsGpuUUID() bool {
if !strings.HasPrefix(string(d), "GPU-") {
return false
}
_, err := uuid.Parse(strings.TrimPrefix(string(d), "GPU-"))
return err == nil
}
IsGpuUUID:格式 GPU-<uuid>,如 GPU-b1028956-cfa2-0990-bf4a-5da9abb51763。
go
func (d ReplicatedDeviceRef) IsMigUUID() bool {
if !strings.HasPrefix(string(d), "MIG-") {
return false
}
suffix := strings.TrimPrefix(string(d), "MIG-")
_, err := uuid.Parse(suffix)
if err == nil {
return true
}
split := strings.SplitN(suffix, "/", 3)
if len(split) != 3 {
return false
}
if !ReplicatedDeviceRef(split[0]).IsGpuUUID() {
return false
}
for _, s := range split[1:] {
_, err := strconv.ParseUint(s, 10, 0)
if err != nil {
return false
}
}
return true
}
IsMigUUID:支持两种格式:
MIG-<uuid>(直接 UUID)MIG-GPU-<gpu-uuid>/<gi>/<ci>(GPU UUID + MIG 实例索引路径)
#mermaid-svg-vsnqNbDnhlnjXHZA{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-vsnqNbDnhlnjXHZA .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-vsnqNbDnhlnjXHZA .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-vsnqNbDnhlnjXHZA .error-icon{fill:#552222;}#mermaid-svg-vsnqNbDnhlnjXHZA .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-vsnqNbDnhlnjXHZA .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-vsnqNbDnhlnjXHZA .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-vsnqNbDnhlnjXHZA .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-vsnqNbDnhlnjXHZA .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-vsnqNbDnhlnjXHZA .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-vsnqNbDnhlnjXHZA .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-vsnqNbDnhlnjXHZA .marker{fill:#333333;stroke:#333333;}#mermaid-svg-vsnqNbDnhlnjXHZA .marker.cross{stroke:#333333;}#mermaid-svg-vsnqNbDnhlnjXHZA svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-vsnqNbDnhlnjXHZA p{margin:0;}#mermaid-svg-vsnqNbDnhlnjXHZA .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-vsnqNbDnhlnjXHZA .cluster-label text{fill:#333;}#mermaid-svg-vsnqNbDnhlnjXHZA .cluster-label span{color:#333;}#mermaid-svg-vsnqNbDnhlnjXHZA .cluster-label span p{background-color:transparent;}#mermaid-svg-vsnqNbDnhlnjXHZA .label text,#mermaid-svg-vsnqNbDnhlnjXHZA span{fill:#333;color:#333;}#mermaid-svg-vsnqNbDnhlnjXHZA .node rect,#mermaid-svg-vsnqNbDnhlnjXHZA .node circle,#mermaid-svg-vsnqNbDnhlnjXHZA .node ellipse,#mermaid-svg-vsnqNbDnhlnjXHZA .node polygon,#mermaid-svg-vsnqNbDnhlnjXHZA .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-vsnqNbDnhlnjXHZA .rough-node .label text,#mermaid-svg-vsnqNbDnhlnjXHZA .node .label text,#mermaid-svg-vsnqNbDnhlnjXHZA .image-shape .label,#mermaid-svg-vsnqNbDnhlnjXHZA .icon-shape .label{text-anchor:middle;}#mermaid-svg-vsnqNbDnhlnjXHZA .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-vsnqNbDnhlnjXHZA .rough-node .label,#mermaid-svg-vsnqNbDnhlnjXHZA .node .label,#mermaid-svg-vsnqNbDnhlnjXHZA .image-shape .label,#mermaid-svg-vsnqNbDnhlnjXHZA .icon-shape .label{text-align:center;}#mermaid-svg-vsnqNbDnhlnjXHZA .node.clickable{cursor:pointer;}#mermaid-svg-vsnqNbDnhlnjXHZA .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-vsnqNbDnhlnjXHZA .arrowheadPath{fill:#333333;}#mermaid-svg-vsnqNbDnhlnjXHZA .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-vsnqNbDnhlnjXHZA .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-vsnqNbDnhlnjXHZA .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-vsnqNbDnhlnjXHZA .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-vsnqNbDnhlnjXHZA .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-vsnqNbDnhlnjXHZA .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-vsnqNbDnhlnjXHZA .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-vsnqNbDnhlnjXHZA .cluster text{fill:#333;}#mermaid-svg-vsnqNbDnhlnjXHZA .cluster span{color:#333;}#mermaid-svg-vsnqNbDnhlnjXHZA 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-vsnqNbDnhlnjXHZA .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-vsnqNbDnhlnjXHZA rect.text{fill:none;stroke-width:0;}#mermaid-svg-vsnqNbDnhlnjXHZA .icon-shape,#mermaid-svg-vsnqNbDnhlnjXHZA .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-vsnqNbDnhlnjXHZA .icon-shape p,#mermaid-svg-vsnqNbDnhlnjXHZA .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-vsnqNbDnhlnjXHZA .icon-shape .label rect,#mermaid-svg-vsnqNbDnhlnjXHZA .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-vsnqNbDnhlnjXHZA .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-vsnqNbDnhlnjXHZA .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-vsnqNbDnhlnjXHZA :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 是
否
是
否
是
是
否
否
是
是
否
是
否
否
ReplicatedDeviceRef 字符串
是纯数字?
GPU Index
格式 N:N?
MIG Index
前缀 GPU-?
UUID 合法?
GPU UUID
无效
前缀 MIG-?
直接 UUID 合法?
MIG UUID 短格式
格式 MIG-GPU-uuid/N/N?
MIG UUID 长格式
3.7.5 ReplicatedDevices 自定义序列化
go
type ReplicatedDevices struct {
All bool
Count int
List []ReplicatedDeviceRef
}
这是一个联合体(union)类型------同一时间只应设置一个字段。通过自定义 JSON 序列化实现三种配置方式:
go
func (s *ReplicatedDevices) UnmarshalJSON(b []byte) error {
// 尝试1: 字符串 "all"
var str string
err := json.Unmarshal(b, &str)
if err == nil {
if str != "all" {
return fmt.Errorf("...")
}
s.All = true
return nil
}
// 尝试2: 整数(count)
var count int
err = json.Unmarshal(b, &count)
if err == nil {
if count <= 0 {
return fmt.Errorf("...")
}
s.Count = count
return nil
}
// 尝试3: 列表
var slice []json.RawMessage
err = json.Unmarshal(b, &slice)
if err == nil {
// ...逐项解析并校验
s.List = result
return nil
}
return fmt.Errorf("unrecognized type for devices spec: %v", string(b))
}
配置文件中可写:
yaml
devices: "all" # 所有设备
devices: 2 # 前N个设备
devices: ["0", "1:0"] # 指定设备列表
列表解析时,每项先尝试解析为 uint64(GPU 索引),再尝试解析为字符串并校验是否为合法的 GPU 索引/MIG 索引/UUID。
go
func (s *ReplicatedDevices) MarshalJSON() ([]byte, error) {
if s.All {
return json.Marshal("all")
}
if s.Count > 0 {
return json.Marshal(s.Count)
}
if s.List != nil {
return json.Marshal(s.List)
}
return nil, fmt.Errorf("unmarshallable ReplicatedDevices struct: %v", s)
}
MarshalJSON 对称地序列化回原始格式。
3.7.6 ReplicatedResource.UnmarshalJSON
go
func (s *ReplicatedResource) UnmarshalJSON(b []byte) error {
rr := make(map[string]json.RawMessage)
// ...
// name: 必须存在
// devices: 可选,默认 "all"
// replicas: 必须存在,且 >= 2
// rename: 可选
// ...
}
校验规则:
name:必须存在,通过ResourceName.UnmarshalJSON校验格式。devices:可选,默认"all"。replicas:必须存在,必须>= 2(replicas=1 无意义,等同独占)。rename:可选,通过ResourceName.UnmarshalJSON校验格式。
3.7.7 ReplicatedResources.UnmarshalJSON
go
func (s *ReplicatedResources) UnmarshalJSON(b []byte) error {
// ...
for i, r := range s.Resources {
if s.RenameByDefault && r.Rename == "" {
s.Resources[i].Rename = r.Name.DefaultSharedRename()
}
}
return nil
}
后处理逻辑: 如果 RenameByDefault=true 且某个资源未显式设置 Rename,则自动填充默认共享名(name.shared)。
3.8 imex.go --- IMEX 通道配置
go
const (
ImexChannelEnvVar = "NVIDIA_IMEX_CHANNELS"
)
type Imex struct {
ChannelIDs []int `json:"channelIDs,omitempty" yaml:"channelIDs,omitempty"`
Required bool `json:"required,omitempty" yaml:"required,omitempty"`
}
IMEX(InfiniBand Messaging Exchange) 是 NVIDIA 的 fabric-attached 设备通信机制:
ChannelIDs:要注入容器的 IMEX 通道 ID 列表。Required:是否强制要求通道存在。true→ 启动时通道必须存在;false→ 通道不存在时静默跳过。
go
func AssertChannelIDsValid(ids []int) error {
switch {
case len(ids) == 0:
return nil
case len(ids) == 1 && ids[0] == 0:
return nil
}
return fmt.Errorf("%w: channelIDs must be [] or [0]; found %v", errInvalidImexConfig, ids)
}
当前限制: 只允许 [](空列表,禁用)或 [0](仅通道0)。其他配置报错。这表明 IMEX 功能处于早期阶段,仅支持最基础的配置。
3.9 duration.go --- 自定义时长类型
go
type Duration time.Duration
包装标准库 time.Duration,增加对 "infinite" 特殊值的支持。
go
func (d *Duration) IsInfinite() bool {
return d != nil && time.Duration(*d) == math.MaxInt64
}
用 math.MaxInt64 表示"无限时长"。主要用于 GFD 的 sleepInterval------设置为无限时 GFD 只运行一次(配合 oneshot 模式)。
go
func (d Duration) String() string {
if d.IsInfinite() {
return "infinite"
}
return time.Duration(d).String()
}
人类可读输出:无限时长显示为 "infinite",否则调用标准库的 String()(如 "30s", "5m")。
go
func (d Duration) MarshalJSON() ([]byte, error) {
return json.Marshal(d.String())
}
func (d *Duration) UnmarshalJSON(b []byte) error {
var v interface{}
if err := json.Unmarshal(b, &v); err != nil {
return err
}
switch value := v.(type) {
case float64:
*d = Duration(time.Duration(value))
return nil
case string:
return d.parse(value)
default:
return fmt.Errorf("invalid duration")
}
}
JSON 支持两种格式:
- 数字:直接转为
time.Duration(纳秒),如30000000000= 30s。 - 字符串:通过
parse解析,支持"infinite"和标准 Go duration 格式("30s","5m")。
go
func (d *Duration) parse(value string) error {
if value == "infinite" {
*d = Duration(math.MaxInt64)
return nil
}
parsed, err := time.ParseDuration(value)
if err != nil {
return err
}
*d = Duration(parsed)
return nil
}
go
type DurationValue struct {
Value *Duration
}
func NewDurationValue(d time.Duration) *DurationValue {
duration := Duration(d)
return &DurationValue{Value: &duration}
}
func (d *DurationValue) Set(value string) error {
return d.Value.parse(value)
}
func (d *DurationValue) String() string {
if d.Value == nil {
return ""
}
if d.Value.IsInfinite() {
return "infinite"
}
return time.Duration(*d.Value).String()
}
DurationValue 实现 cli.Generic 接口,使 Duration 类型可以作为 urfave/cli 的命令行标志使用。Set 方法在 CLI 解析时被调用。
四、配置加载完整流程
Config对象 UpdateFromCLIFlags() parseConfig() NewConfig() 命令行 Config对象 UpdateFromCLIFlags() parseConfig() NewConfig() 命令行 #mermaid-svg-p0H2QjGiAH90cL1A{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-p0H2QjGiAH90cL1A .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-p0H2QjGiAH90cL1A .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-p0H2QjGiAH90cL1A .error-icon{fill:#552222;}#mermaid-svg-p0H2QjGiAH90cL1A .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-p0H2QjGiAH90cL1A .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-p0H2QjGiAH90cL1A .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-p0H2QjGiAH90cL1A .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-p0H2QjGiAH90cL1A .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-p0H2QjGiAH90cL1A .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-p0H2QjGiAH90cL1A .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-p0H2QjGiAH90cL1A .marker{fill:#333333;stroke:#333333;}#mermaid-svg-p0H2QjGiAH90cL1A .marker.cross{stroke:#333333;}#mermaid-svg-p0H2QjGiAH90cL1A svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-p0H2QjGiAH90cL1A p{margin:0;}#mermaid-svg-p0H2QjGiAH90cL1A .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-p0H2QjGiAH90cL1A text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-p0H2QjGiAH90cL1A .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-p0H2QjGiAH90cL1A .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-p0H2QjGiAH90cL1A .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-p0H2QjGiAH90cL1A .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-p0H2QjGiAH90cL1A #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-p0H2QjGiAH90cL1A .sequenceNumber{fill:white;}#mermaid-svg-p0H2QjGiAH90cL1A #sequencenumber{fill:#333;}#mermaid-svg-p0H2QjGiAH90cL1A #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-p0H2QjGiAH90cL1A .messageText{fill:#333;stroke:none;}#mermaid-svg-p0H2QjGiAH90cL1A .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-p0H2QjGiAH90cL1A .labelText,#mermaid-svg-p0H2QjGiAH90cL1A .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-p0H2QjGiAH90cL1A .loopText,#mermaid-svg-p0H2QjGiAH90cL1A .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-p0H2QjGiAH90cL1A .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-p0H2QjGiAH90cL1A .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-p0H2QjGiAH90cL1A .noteText,#mermaid-svg-p0H2QjGiAH90cL1A .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-p0H2QjGiAH90cL1A .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-p0H2QjGiAH90cL1A .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-p0H2QjGiAH90cL1A .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-p0H2QjGiAH90cL1A .actorPopupMenu{position:absolute;}#mermaid-svg-p0H2QjGiAH90cL1A .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-p0H2QjGiAH90cL1A .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-p0H2QjGiAH90cL1A .actor-man circle,#mermaid-svg-p0H2QjGiAH90cL1A line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-p0H2QjGiAH90cL1A :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} alt配置文件存在 cli.Context + flags初始化默认 Config{Version: "v1"}configFile路径os.Open(configFile)io.ReadAll(reader)yaml.Unmarshal(configYaml)版本校验 (v1)返回 *Config合并CLI标志遍历flags × names通用标志: mig-strategy, gds-enabled 等Plugin标志: pass-device-specs 等GFD标志: oneshot, output-file 等更新后的 FlagsIMEX CLI覆盖 (imex-channel-ids, imex-required)NvidiaDevRoot 默认值填充MPS FailRequestsGreaterThanOne 强制设为 true返回最终 *Config
五、配置结构 YAML 示例与解析流程
5.1 完整配置示例
yaml
version: v1
flags:
migStrategy: "none"
failOnInitError: true
gdsEnabled: false
mofedEnabled: false
plugin:
passDeviceSpecs: true
deviceListStrategy: ["cdi-annotations"]
deviceIDStrategy: "uuid"
cdiAnnotationPrefix: "nvidia.com"
nvidiaCTKPath: "/usr/bin/nvidia-ctk"
containerDriverRoot: "/driver-root"
gfd:
oneshot: false
noTimestamp: false
sleepInterval: "5m"
outputFile: "/etc/kubernetes/node_features.yaml"
sharing:
timeSlicing:
renameByDefault: false
resources:
- name: "nvidia.com/gpu"
replicas: 2
devices: "all"
imex:
channelIDs: [0]
required: false
5.2 解析流程
#mermaid-svg-clicPvlY8kKHL4Os{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-clicPvlY8kKHL4Os .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-clicPvlY8kKHL4Os .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-clicPvlY8kKHL4Os .error-icon{fill:#552222;}#mermaid-svg-clicPvlY8kKHL4Os .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-clicPvlY8kKHL4Os .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-clicPvlY8kKHL4Os .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-clicPvlY8kKHL4Os .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-clicPvlY8kKHL4Os .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-clicPvlY8kKHL4Os .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-clicPvlY8kKHL4Os .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-clicPvlY8kKHL4Os .marker{fill:#333333;stroke:#333333;}#mermaid-svg-clicPvlY8kKHL4Os .marker.cross{stroke:#333333;}#mermaid-svg-clicPvlY8kKHL4Os svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-clicPvlY8kKHL4Os p{margin:0;}#mermaid-svg-clicPvlY8kKHL4Os .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-clicPvlY8kKHL4Os .cluster-label text{fill:#333;}#mermaid-svg-clicPvlY8kKHL4Os .cluster-label span{color:#333;}#mermaid-svg-clicPvlY8kKHL4Os .cluster-label span p{background-color:transparent;}#mermaid-svg-clicPvlY8kKHL4Os .label text,#mermaid-svg-clicPvlY8kKHL4Os span{fill:#333;color:#333;}#mermaid-svg-clicPvlY8kKHL4Os .node rect,#mermaid-svg-clicPvlY8kKHL4Os .node circle,#mermaid-svg-clicPvlY8kKHL4Os .node ellipse,#mermaid-svg-clicPvlY8kKHL4Os .node polygon,#mermaid-svg-clicPvlY8kKHL4Os .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-clicPvlY8kKHL4Os .rough-node .label text,#mermaid-svg-clicPvlY8kKHL4Os .node .label text,#mermaid-svg-clicPvlY8kKHL4Os .image-shape .label,#mermaid-svg-clicPvlY8kKHL4Os .icon-shape .label{text-anchor:middle;}#mermaid-svg-clicPvlY8kKHL4Os .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-clicPvlY8kKHL4Os .rough-node .label,#mermaid-svg-clicPvlY8kKHL4Os .node .label,#mermaid-svg-clicPvlY8kKHL4Os .image-shape .label,#mermaid-svg-clicPvlY8kKHL4Os .icon-shape .label{text-align:center;}#mermaid-svg-clicPvlY8kKHL4Os .node.clickable{cursor:pointer;}#mermaid-svg-clicPvlY8kKHL4Os .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-clicPvlY8kKHL4Os .arrowheadPath{fill:#333333;}#mermaid-svg-clicPvlY8kKHL4Os .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-clicPvlY8kKHL4Os .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-clicPvlY8kKHL4Os .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-clicPvlY8kKHL4Os .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-clicPvlY8kKHL4Os .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-clicPvlY8kKHL4Os .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-clicPvlY8kKHL4Os .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-clicPvlY8kKHL4Os .cluster text{fill:#333;}#mermaid-svg-clicPvlY8kKHL4Os .cluster span{color:#333;}#mermaid-svg-clicPvlY8kKHL4Os 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-clicPvlY8kKHL4Os .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-clicPvlY8kKHL4Os rect.text{fill:none;stroke-width:0;}#mermaid-svg-clicPvlY8kKHL4Os .icon-shape,#mermaid-svg-clicPvlY8kKHL4Os .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-clicPvlY8kKHL4Os .icon-shape p,#mermaid-svg-clicPvlY8kKHL4Os .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-clicPvlY8kKHL4Os .icon-shape .label rect,#mermaid-svg-clicPvlY8kKHL4Os .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-clicPvlY8kKHL4Os .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-clicPvlY8kKHL4Os .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-clicPvlY8kKHL4Os :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 否
是
是
否
是
否
是
否
是
否
读取 YAML 文件
yaml.Unmarshal → Config
version == v1?
报错: unknown version
Config.Flags 已从YAML填充
UpdateFromCLIFlags: CLI覆盖
CLI --imex-channel-ids 设置?
覆盖 Config.Imex.ChannelIDs
CLI --imex-required 设置?
覆盖 Config.Imex.Required
NvidiaDevRoot 为空?
NvidiaDevRoot = NvidiaDriverRoot
Sharing.MPS != nil?
MPS.FailRequestsGreaterThanOne = true
返回最终 Config
六、资源共享策略详解
6.1 两种共享模式对比
#mermaid-svg-vZFob6mjaAwNwd1o{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-vZFob6mjaAwNwd1o .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-vZFob6mjaAwNwd1o .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-vZFob6mjaAwNwd1o .error-icon{fill:#552222;}#mermaid-svg-vZFob6mjaAwNwd1o .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-vZFob6mjaAwNwd1o .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-vZFob6mjaAwNwd1o .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-vZFob6mjaAwNwd1o .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-vZFob6mjaAwNwd1o .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-vZFob6mjaAwNwd1o .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-vZFob6mjaAwNwd1o .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-vZFob6mjaAwNwd1o .marker{fill:#333333;stroke:#333333;}#mermaid-svg-vZFob6mjaAwNwd1o .marker.cross{stroke:#333333;}#mermaid-svg-vZFob6mjaAwNwd1o svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-vZFob6mjaAwNwd1o p{margin:0;}#mermaid-svg-vZFob6mjaAwNwd1o .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-vZFob6mjaAwNwd1o .cluster-label text{fill:#333;}#mermaid-svg-vZFob6mjaAwNwd1o .cluster-label span{color:#333;}#mermaid-svg-vZFob6mjaAwNwd1o .cluster-label span p{background-color:transparent;}#mermaid-svg-vZFob6mjaAwNwd1o .label text,#mermaid-svg-vZFob6mjaAwNwd1o span{fill:#333;color:#333;}#mermaid-svg-vZFob6mjaAwNwd1o .node rect,#mermaid-svg-vZFob6mjaAwNwd1o .node circle,#mermaid-svg-vZFob6mjaAwNwd1o .node ellipse,#mermaid-svg-vZFob6mjaAwNwd1o .node polygon,#mermaid-svg-vZFob6mjaAwNwd1o .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-vZFob6mjaAwNwd1o .rough-node .label text,#mermaid-svg-vZFob6mjaAwNwd1o .node .label text,#mermaid-svg-vZFob6mjaAwNwd1o .image-shape .label,#mermaid-svg-vZFob6mjaAwNwd1o .icon-shape .label{text-anchor:middle;}#mermaid-svg-vZFob6mjaAwNwd1o .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-vZFob6mjaAwNwd1o .rough-node .label,#mermaid-svg-vZFob6mjaAwNwd1o .node .label,#mermaid-svg-vZFob6mjaAwNwd1o .image-shape .label,#mermaid-svg-vZFob6mjaAwNwd1o .icon-shape .label{text-align:center;}#mermaid-svg-vZFob6mjaAwNwd1o .node.clickable{cursor:pointer;}#mermaid-svg-vZFob6mjaAwNwd1o .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-vZFob6mjaAwNwd1o .arrowheadPath{fill:#333333;}#mermaid-svg-vZFob6mjaAwNwd1o .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-vZFob6mjaAwNwd1o .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-vZFob6mjaAwNwd1o .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-vZFob6mjaAwNwd1o .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-vZFob6mjaAwNwd1o .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-vZFob6mjaAwNwd1o .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-vZFob6mjaAwNwd1o .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-vZFob6mjaAwNwd1o .cluster text{fill:#333;}#mermaid-svg-vZFob6mjaAwNwd1o .cluster span{color:#333;}#mermaid-svg-vZFob6mjaAwNwd1o 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-vZFob6mjaAwNwd1o .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-vZFob6mjaAwNwd1o rect.text{fill:none;stroke-width:0;}#mermaid-svg-vZFob6mjaAwNwd1o .icon-shape,#mermaid-svg-vZFob6mjaAwNwd1o .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-vZFob6mjaAwNwd1o .icon-shape p,#mermaid-svg-vZFob6mjaAwNwd1o .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-vZFob6mjaAwNwd1o .icon-shape .label rect,#mermaid-svg-vZFob6mjaAwNwd1o .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-vZFob6mjaAwNwd1o .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-vZFob6mjaAwNwd1o .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-vZFob6mjaAwNwd1o :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} MPS Multi-Process Service
共享上下文
共享上下文
GPU 0
MPS Client 1
MPS Client 2
Pod A
Pod B
同一GPU被多个Pod共享
通过MPS共享GPU上下文
强制FailRequestsGreaterThanOne
TimeSlicing 时间分片
时间片轮转
时间片轮转
GPU 0
Replica 1
Replica 2
Pod A
Pod B
同一GPU被多个Pod共享
通过时间片轮转实现
6.2 共享资源配置解析流程
#mermaid-svg-e30EHHhneFjVcDWo{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-e30EHHhneFjVcDWo .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-e30EHHhneFjVcDWo .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-e30EHHhneFjVcDWo .error-icon{fill:#552222;}#mermaid-svg-e30EHHhneFjVcDWo .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-e30EHHhneFjVcDWo .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-e30EHHhneFjVcDWo .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-e30EHHhneFjVcDWo .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-e30EHHhneFjVcDWo .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-e30EHHhneFjVcDWo .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-e30EHHhneFjVcDWo .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-e30EHHhneFjVcDWo .marker{fill:#333333;stroke:#333333;}#mermaid-svg-e30EHHhneFjVcDWo .marker.cross{stroke:#333333;}#mermaid-svg-e30EHHhneFjVcDWo svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-e30EHHhneFjVcDWo p{margin:0;}#mermaid-svg-e30EHHhneFjVcDWo .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-e30EHHhneFjVcDWo .cluster-label text{fill:#333;}#mermaid-svg-e30EHHhneFjVcDWo .cluster-label span{color:#333;}#mermaid-svg-e30EHHhneFjVcDWo .cluster-label span p{background-color:transparent;}#mermaid-svg-e30EHHhneFjVcDWo .label text,#mermaid-svg-e30EHHhneFjVcDWo span{fill:#333;color:#333;}#mermaid-svg-e30EHHhneFjVcDWo .node rect,#mermaid-svg-e30EHHhneFjVcDWo .node circle,#mermaid-svg-e30EHHhneFjVcDWo .node ellipse,#mermaid-svg-e30EHHhneFjVcDWo .node polygon,#mermaid-svg-e30EHHhneFjVcDWo .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-e30EHHhneFjVcDWo .rough-node .label text,#mermaid-svg-e30EHHhneFjVcDWo .node .label text,#mermaid-svg-e30EHHhneFjVcDWo .image-shape .label,#mermaid-svg-e30EHHhneFjVcDWo .icon-shape .label{text-anchor:middle;}#mermaid-svg-e30EHHhneFjVcDWo .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-e30EHHhneFjVcDWo .rough-node .label,#mermaid-svg-e30EHHhneFjVcDWo .node .label,#mermaid-svg-e30EHHhneFjVcDWo .image-shape .label,#mermaid-svg-e30EHHhneFjVcDWo .icon-shape .label{text-align:center;}#mermaid-svg-e30EHHhneFjVcDWo .node.clickable{cursor:pointer;}#mermaid-svg-e30EHHhneFjVcDWo .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-e30EHHhneFjVcDWo .arrowheadPath{fill:#333333;}#mermaid-svg-e30EHHhneFjVcDWo .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-e30EHHhneFjVcDWo .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-e30EHHhneFjVcDWo .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-e30EHHhneFjVcDWo .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-e30EHHhneFjVcDWo .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-e30EHHhneFjVcDWo .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-e30EHHhneFjVcDWo .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-e30EHHhneFjVcDWo .cluster text{fill:#333;}#mermaid-svg-e30EHHhneFjVcDWo .cluster span{color:#333;}#mermaid-svg-e30EHHhneFjVcDWo 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-e30EHHhneFjVcDWo .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-e30EHHhneFjVcDWo rect.text{fill:none;stroke-width:0;}#mermaid-svg-e30EHHhneFjVcDWo .icon-shape,#mermaid-svg-e30EHHhneFjVcDWo .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-e30EHHhneFjVcDWo .icon-shape p,#mermaid-svg-e30EHHhneFjVcDWo .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-e30EHHhneFjVcDWo .icon-shape .label rect,#mermaid-svg-e30EHHhneFjVcDWo .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-e30EHHhneFjVcDWo .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-e30EHHhneFjVcDWo .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-e30EHHhneFjVcDWo :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 否
是
否
是
否
是
否
是
否
是
是
否
是
否
YAML sharing.timeSlicing
ReplicatedResources.UnmarshalJSON
renameByDefault 存在?
默认 false
解析 renameByDefault
resources 存在?
报错: no resources specified
解析 resources 列表
遍历每个 ReplicatedResource
ReplicatedResource.UnmarshalJSON
name 存在?
报错
ResourceName.UnmarshalJSON 校验
devices 存在?
默认 'all'
ReplicatedDevices.UnmarshalJSON
replicas 存在且 >= 2?
报错
rename 存在?
ResourceName.UnmarshalJSON 校验
renameByDefault?
Rename = Name.DefaultSharedRename
Rename = 空
完成
七、配置优先级与合并机制
#mermaid-svg-UOi4AjkwQq3WiUS4{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-UOi4AjkwQq3WiUS4 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-UOi4AjkwQq3WiUS4 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-UOi4AjkwQq3WiUS4 .error-icon{fill:#552222;}#mermaid-svg-UOi4AjkwQq3WiUS4 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-UOi4AjkwQq3WiUS4 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-UOi4AjkwQq3WiUS4 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-UOi4AjkwQq3WiUS4 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-UOi4AjkwQq3WiUS4 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-UOi4AjkwQq3WiUS4 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-UOi4AjkwQq3WiUS4 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-UOi4AjkwQq3WiUS4 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-UOi4AjkwQq3WiUS4 .marker.cross{stroke:#333333;}#mermaid-svg-UOi4AjkwQq3WiUS4 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-UOi4AjkwQq3WiUS4 p{margin:0;}#mermaid-svg-UOi4AjkwQq3WiUS4 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-UOi4AjkwQq3WiUS4 .cluster-label text{fill:#333;}#mermaid-svg-UOi4AjkwQq3WiUS4 .cluster-label span{color:#333;}#mermaid-svg-UOi4AjkwQq3WiUS4 .cluster-label span p{background-color:transparent;}#mermaid-svg-UOi4AjkwQq3WiUS4 .label text,#mermaid-svg-UOi4AjkwQq3WiUS4 span{fill:#333;color:#333;}#mermaid-svg-UOi4AjkwQq3WiUS4 .node rect,#mermaid-svg-UOi4AjkwQq3WiUS4 .node circle,#mermaid-svg-UOi4AjkwQq3WiUS4 .node ellipse,#mermaid-svg-UOi4AjkwQq3WiUS4 .node polygon,#mermaid-svg-UOi4AjkwQq3WiUS4 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-UOi4AjkwQq3WiUS4 .rough-node .label text,#mermaid-svg-UOi4AjkwQq3WiUS4 .node .label text,#mermaid-svg-UOi4AjkwQq3WiUS4 .image-shape .label,#mermaid-svg-UOi4AjkwQq3WiUS4 .icon-shape .label{text-anchor:middle;}#mermaid-svg-UOi4AjkwQq3WiUS4 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-UOi4AjkwQq3WiUS4 .rough-node .label,#mermaid-svg-UOi4AjkwQq3WiUS4 .node .label,#mermaid-svg-UOi4AjkwQq3WiUS4 .image-shape .label,#mermaid-svg-UOi4AjkwQq3WiUS4 .icon-shape .label{text-align:center;}#mermaid-svg-UOi4AjkwQq3WiUS4 .node.clickable{cursor:pointer;}#mermaid-svg-UOi4AjkwQq3WiUS4 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-UOi4AjkwQq3WiUS4 .arrowheadPath{fill:#333333;}#mermaid-svg-UOi4AjkwQq3WiUS4 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-UOi4AjkwQq3WiUS4 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-UOi4AjkwQq3WiUS4 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-UOi4AjkwQq3WiUS4 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-UOi4AjkwQq3WiUS4 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-UOi4AjkwQq3WiUS4 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-UOi4AjkwQq3WiUS4 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-UOi4AjkwQq3WiUS4 .cluster text{fill:#333;}#mermaid-svg-UOi4AjkwQq3WiUS4 .cluster span{color:#333;}#mermaid-svg-UOi4AjkwQq3WiUS4 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-UOi4AjkwQq3WiUS4 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-UOi4AjkwQq3WiUS4 rect.text{fill:none;stroke-width:0;}#mermaid-svg-UOi4AjkwQq3WiUS4 .icon-shape,#mermaid-svg-UOi4AjkwQq3WiUS4 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-UOi4AjkwQq3WiUS4 .icon-shape p,#mermaid-svg-UOi4AjkwQq3WiUS4 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-UOi4AjkwQq3WiUS4 .icon-shape .label rect,#mermaid-svg-UOi4AjkwQq3WiUS4 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-UOi4AjkwQq3WiUS4 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-UOi4AjkwQq3WiUS4 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-UOi4AjkwQq3WiUS4 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 优先级(高→低)
- CLI 命令行显式设置
c.IsSet(flagName) == true
2. CLI 默认值
配置文件字段为 nil 时填充
3. 配置文件 YAML/JSON 值
parseConfig 解析得到
updateFromCLIFlag
条件: c.IsSet OR pflag==nil
parseConfig
YAML反序列化
最终 Config
合并规则详解:
| 场景 | CLI 是否设置 | 配置文件是否设置 | 最终值来源 |
|---|---|---|---|
| CLI 显式覆盖 | ✅ | ✅ | CLI |
| CLI 显式覆盖 | ✅ | ❌ | CLI |
| CLI 默认填充 | ❌ | ❌ (nil) | CLI 默认值 |
| 配置文件保留 | ❌ | ✅ | 配置文件 |
关键函数 updateFromCLIFlag 的条件 c.IsSet(flagName) || *pflag == nil 精确实现了此规则。
八、自定义 JSON 序列化体系
本模块有大量自定义 JSON 序列化/反序列化实现,汇总如下:
#mermaid-svg-s7AfgSEWKNzYgs8M{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-s7AfgSEWKNzYgs8M .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-s7AfgSEWKNzYgs8M .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-s7AfgSEWKNzYgs8M .error-icon{fill:#552222;}#mermaid-svg-s7AfgSEWKNzYgs8M .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-s7AfgSEWKNzYgs8M .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-s7AfgSEWKNzYgs8M .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-s7AfgSEWKNzYgs8M .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-s7AfgSEWKNzYgs8M .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-s7AfgSEWKNzYgs8M .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-s7AfgSEWKNzYgs8M .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-s7AfgSEWKNzYgs8M .marker{fill:#333333;stroke:#333333;}#mermaid-svg-s7AfgSEWKNzYgs8M .marker.cross{stroke:#333333;}#mermaid-svg-s7AfgSEWKNzYgs8M svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-s7AfgSEWKNzYgs8M p{margin:0;}#mermaid-svg-s7AfgSEWKNzYgs8M g.classGroup text{fill:#9370DB;stroke:none;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:10px;}#mermaid-svg-s7AfgSEWKNzYgs8M g.classGroup text .title{font-weight:bolder;}#mermaid-svg-s7AfgSEWKNzYgs8M .cluster-label text{fill:#333;}#mermaid-svg-s7AfgSEWKNzYgs8M .cluster-label span{color:#333;}#mermaid-svg-s7AfgSEWKNzYgs8M .cluster-label span p{background-color:transparent;}#mermaid-svg-s7AfgSEWKNzYgs8M .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-s7AfgSEWKNzYgs8M .cluster text{fill:#333;}#mermaid-svg-s7AfgSEWKNzYgs8M .cluster span{color:#333;}#mermaid-svg-s7AfgSEWKNzYgs8M .nodeLabel,#mermaid-svg-s7AfgSEWKNzYgs8M .edgeLabel{color:#131300;}#mermaid-svg-s7AfgSEWKNzYgs8M .edgeLabel .label rect{fill:#ECECFF;}#mermaid-svg-s7AfgSEWKNzYgs8M .label text{fill:#131300;}#mermaid-svg-s7AfgSEWKNzYgs8M .labelBkg{background:#ECECFF;}#mermaid-svg-s7AfgSEWKNzYgs8M .edgeLabel .label span{background:#ECECFF;}#mermaid-svg-s7AfgSEWKNzYgs8M .classTitle{font-weight:bolder;}#mermaid-svg-s7AfgSEWKNzYgs8M .node rect,#mermaid-svg-s7AfgSEWKNzYgs8M .node circle,#mermaid-svg-s7AfgSEWKNzYgs8M .node ellipse,#mermaid-svg-s7AfgSEWKNzYgs8M .node polygon,#mermaid-svg-s7AfgSEWKNzYgs8M .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-s7AfgSEWKNzYgs8M .divider{stroke:#9370DB;stroke-width:1;}#mermaid-svg-s7AfgSEWKNzYgs8M g.clickable{cursor:pointer;}#mermaid-svg-s7AfgSEWKNzYgs8M g.classGroup rect{fill:#ECECFF;stroke:#9370DB;}#mermaid-svg-s7AfgSEWKNzYgs8M g.classGroup line{stroke:#9370DB;stroke-width:1;}#mermaid-svg-s7AfgSEWKNzYgs8M .classLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.5;}#mermaid-svg-s7AfgSEWKNzYgs8M .classLabel .label{fill:#9370DB;font-size:10px;}#mermaid-svg-s7AfgSEWKNzYgs8M .relation{stroke:#333333;stroke-width:1;fill:none;}#mermaid-svg-s7AfgSEWKNzYgs8M .dashed-line{stroke-dasharray:3;}#mermaid-svg-s7AfgSEWKNzYgs8M .dotted-line{stroke-dasharray:1 2;}#mermaid-svg-s7AfgSEWKNzYgs8M #compositionStart,#mermaid-svg-s7AfgSEWKNzYgs8M .composition{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-s7AfgSEWKNzYgs8M #compositionEnd,#mermaid-svg-s7AfgSEWKNzYgs8M .composition{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-s7AfgSEWKNzYgs8M #dependencyStart,#mermaid-svg-s7AfgSEWKNzYgs8M .dependency{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-s7AfgSEWKNzYgs8M #dependencyStart,#mermaid-svg-s7AfgSEWKNzYgs8M .dependency{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-s7AfgSEWKNzYgs8M #extensionStart,#mermaid-svg-s7AfgSEWKNzYgs8M .extension{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-s7AfgSEWKNzYgs8M #extensionEnd,#mermaid-svg-s7AfgSEWKNzYgs8M .extension{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-s7AfgSEWKNzYgs8M #aggregationStart,#mermaid-svg-s7AfgSEWKNzYgs8M .aggregation{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-s7AfgSEWKNzYgs8M #aggregationEnd,#mermaid-svg-s7AfgSEWKNzYgs8M .aggregation{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-s7AfgSEWKNzYgs8M #lollipopStart,#mermaid-svg-s7AfgSEWKNzYgs8M .lollipop{fill:#ECECFF!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-s7AfgSEWKNzYgs8M #lollipopEnd,#mermaid-svg-s7AfgSEWKNzYgs8M .lollipop{fill:#ECECFF!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-s7AfgSEWKNzYgs8M .edgeTerminals{font-size:11px;line-height:initial;}#mermaid-svg-s7AfgSEWKNzYgs8M .classTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-s7AfgSEWKNzYgs8M .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-s7AfgSEWKNzYgs8M .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-s7AfgSEWKNzYgs8M :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Resource
+UnmarshalJSON(b \[\]byte) : error
ResourceName
+UnmarshalJSON(b \[\]byte) : error
+Split()(string, string)
+DefaultSharedRename() : ResourceName
deviceListStrategyFlag
+UnmarshalJSON(b \[\]byte) : error
ReplicatedResources
+UnmarshalJSON(b \[\]byte) : error
ReplicatedResource
+UnmarshalJSON(b \[\]byte) : error
ReplicatedDevices
+UnmarshalJSON(b \[\]byte) : error
+MarshalJSON()(\[\]byte, error)
Duration
+MarshalJSON()(\[\]byte, error)
+UnmarshalJSON(b \[\]byte) : error
自定义序列化的设计原因:
| 类型 | 原因 |
|---|---|
Resource |
确保 pattern 和 name 字段都存在 |
ResourceName |
自动补全前缀 + 格式校验 |
deviceListStrategyFlag |
支持单字符串和字符串列表两种格式 |
ReplicatedResources |
renameByDefault 后处理逻辑 |
ReplicatedResource |
devices 默认值 + replicas 校验 |
ReplicatedDevices |
联合体类型,支持 string/int/array 三种格式 |
Duration |
支持 "infinite" 特殊值 + 数字/字符串双格式 |
九、设计模式与架构评价
9.1 设计模式识别
| 模式 | 体现 |
|---|---|
| Builder | NewConfig 逐步构建 Config 对象(文件解析 → CLI 覆盖 → 后处理) |
| Strategy | SharingStrategy 枚举 + SharingStrategy() 方法实现策略模式 |
| Union Type | ReplicatedDevices 使用联合体模式(All/Count/List 三选一) |
| Type Wrapper | Duration 包装 time.Duration 增加特殊语义 |
| Generic Update | updateFromCLIFlag 使用 Go 泛型实现通用的配置更新逻辑 |
| Functional Option | 指针类型字段 + omitempty 实现可选配置 |
9.2 架构优点
- 优先级合并机制清晰 :通过指针类型(nil = 未设置)+ 泛型
updateFromCLIFlag精确实现三级优先级。 - 版本化配置 :
Version字段 + 版本校验为未来配置迁移预留了扩展点。 - 安全保守策略 :MPS 强制
FailRequestsGreaterThanOne,避免不安全的请求合并。 - 灵活的序列化 :
ReplicatedDevices支持三种配置写法,Duration支持 "infinite",提升了配置文件的易用性。 - 功能降级优雅 :
DisableResourceNamingInConfig保留数据结构但禁用功能,打印警告而非静默忽略。
9.3 潜在改进点
- IMEX 配置位置 :
NewConfig中 IMEX 的 CLI 覆盖逻辑游离于UpdateFromCLIFlags之外(TODO 注释也指出了这一点),应归入 Flags 层级。 - SharingStrategy 逻辑差异 :
SharingStrategy()检查isReplicated(),但ReplicatedResources()不检查,可能导致 MPS 非 nil 但未复制时返回 MPS 资源列表。 - panic in updateFromCLIFlag:遇到不支持的类型直接 panic,生产环境中应返回 error。
- AllCDIEnabled 空集行为:无策略启用时返回 true,语义上可能有争议(是否应该返回 false?)。
- IMEX 通道限制 :当前仅允许
[]或[0],处于早期阶段,扩展性受限。
十、模块依赖关系图
#mermaid-svg-IYJn1roxknM0kqYO{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-IYJn1roxknM0kqYO .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-IYJn1roxknM0kqYO .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-IYJn1roxknM0kqYO .error-icon{fill:#552222;}#mermaid-svg-IYJn1roxknM0kqYO .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-IYJn1roxknM0kqYO .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-IYJn1roxknM0kqYO .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-IYJn1roxknM0kqYO .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-IYJn1roxknM0kqYO .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-IYJn1roxknM0kqYO .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-IYJn1roxknM0kqYO .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-IYJn1roxknM0kqYO .marker{fill:#333333;stroke:#333333;}#mermaid-svg-IYJn1roxknM0kqYO .marker.cross{stroke:#333333;}#mermaid-svg-IYJn1roxknM0kqYO svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-IYJn1roxknM0kqYO p{margin:0;}#mermaid-svg-IYJn1roxknM0kqYO .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-IYJn1roxknM0kqYO .cluster-label text{fill:#333;}#mermaid-svg-IYJn1roxknM0kqYO .cluster-label span{color:#333;}#mermaid-svg-IYJn1roxknM0kqYO .cluster-label span p{background-color:transparent;}#mermaid-svg-IYJn1roxknM0kqYO .label text,#mermaid-svg-IYJn1roxknM0kqYO span{fill:#333;color:#333;}#mermaid-svg-IYJn1roxknM0kqYO .node rect,#mermaid-svg-IYJn1roxknM0kqYO .node circle,#mermaid-svg-IYJn1roxknM0kqYO .node ellipse,#mermaid-svg-IYJn1roxknM0kqYO .node polygon,#mermaid-svg-IYJn1roxknM0kqYO .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-IYJn1roxknM0kqYO .rough-node .label text,#mermaid-svg-IYJn1roxknM0kqYO .node .label text,#mermaid-svg-IYJn1roxknM0kqYO .image-shape .label,#mermaid-svg-IYJn1roxknM0kqYO .icon-shape .label{text-anchor:middle;}#mermaid-svg-IYJn1roxknM0kqYO .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-IYJn1roxknM0kqYO .rough-node .label,#mermaid-svg-IYJn1roxknM0kqYO .node .label,#mermaid-svg-IYJn1roxknM0kqYO .image-shape .label,#mermaid-svg-IYJn1roxknM0kqYO .icon-shape .label{text-align:center;}#mermaid-svg-IYJn1roxknM0kqYO .node.clickable{cursor:pointer;}#mermaid-svg-IYJn1roxknM0kqYO .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-IYJn1roxknM0kqYO .arrowheadPath{fill:#333333;}#mermaid-svg-IYJn1roxknM0kqYO .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-IYJn1roxknM0kqYO .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-IYJn1roxknM0kqYO .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-IYJn1roxknM0kqYO .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-IYJn1roxknM0kqYO .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-IYJn1roxknM0kqYO .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-IYJn1roxknM0kqYO .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-IYJn1roxknM0kqYO .cluster text{fill:#333;}#mermaid-svg-IYJn1roxknM0kqYO .cluster span{color:#333;}#mermaid-svg-IYJn1roxknM0kqYO 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-IYJn1roxknM0kqYO .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-IYJn1roxknM0kqYO rect.text{fill:none;stroke-width:0;}#mermaid-svg-IYJn1roxknM0kqYO .icon-shape,#mermaid-svg-IYJn1roxknM0kqYO .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-IYJn1roxknM0kqYO .icon-shape p,#mermaid-svg-IYJn1roxknM0kqYO .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-IYJn1roxknM0kqYO .icon-shape .label rect,#mermaid-svg-IYJn1roxknM0kqYO .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-IYJn1roxknM0kqYO .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-IYJn1roxknM0kqYO .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-IYJn1roxknM0kqYO :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 内部文件
外部依赖
urfave/cli/v2
sigs.k8s.io/yaml
k8s.io/klog/v2
k8s.io/apimachinery
google/uuid
tags.cncf.io/container-device-interface
config.go
consts.go
flags.go
resources.go
sharing.go
strategy.go
replicas.go
imex.go
duration.go
十一、总结
NVIDIA k8s-device-plugin v0.19.3 的配置API模块是一个版本化、多源合并、类型安全的配置系统,其核心设计可概括为:
- 统一数据模型 :
Config结构体作为所有配置的单一数据源,包含 Flags、Resources、Sharing、Imex 四大子域。 - 三级优先级合并 :CLI > 环境变量 > 配置文件,通过指针类型的 nil 语义和泛型
updateFromCLIFlag精确实现。 - 丰富的自定义序列化:7 个自定义 JSON 序列化/反序列化实现,处理联合体、默认值、格式校验等复杂场景。
- 安全保守的默认策略:MPS 强制拒绝多 GPU 请求、资源重命名临时禁用、IMEX 通道限制为最小集。
- 面向未来的扩展设计:版本号字段、功能降级保留数据结构、TODO 标记改进方向。
该模块是整个 k8s-device-plugin 的配置基石,其设计质量直接决定了插件的行为灵活性和安全性。