NVIDIA k8s-device-plugin v0.19.3 辅助命令模块深度分析
分析范围:
cmd/gpu-feature-discovery、cmd/config-manager、cmd/mps-control-daemon(含 mps 子包及 mount 子包)版本:v0.19.3
日期:2026-07-26
目录
- 模块总览
- [GPU Feature Discovery (GFD)](#GPU Feature Discovery (GFD))
- [Config Manager](#Config Manager)
- [MPS Control Daemon](#MPS Control Daemon)
- [MPS Daemon 核心逻辑](#MPS Daemon 核心逻辑)
- [MPS Device 设备校验](#MPS Device 设备校验)
- [MPS Log Tailer 日志跟踪](#MPS Log Tailer 日志跟踪)
- [MPS Manager 守护进程管理器](#MPS Manager 守护进程管理器)
- [MPS Options 配置选项](#MPS Options 配置选项)
- [MPS Root 路径管理](#MPS Root 路径管理)
- [Mount SHM 共享内存挂载](#Mount SHM 共享内存挂载)
- 全局架构与交互关系
1. 模块总览
1.1 辅助命令在系统中的位置
k8s-device-plugin 除了核心的 nvidia-device-plugin 守护进程外,还提供了三个辅助命令,各自承担独立的运营职责:
| 命令 | 路径 | 核心职责 |
|---|---|---|
| gpu-feature-discovery (GFD) | cmd/gpu-feature-discovery/ |
发现 GPU 硬件特征,生成节点标签 |
| config-manager | cmd/config-manager/ |
监听 Node 标签变化,动态切换设备插件配置 |
| mps-control-daemon | cmd/mps-control-daemon/ |
管理 MPS (Multi-Process Service) 守护进程,实现 GPU 共享 |
1.2 模块关系总览
#mermaid-svg-FgaN3GtCJSWj1Zz8{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-FgaN3GtCJSWj1Zz8 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-FgaN3GtCJSWj1Zz8 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-FgaN3GtCJSWj1Zz8 .error-icon{fill:#552222;}#mermaid-svg-FgaN3GtCJSWj1Zz8 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-FgaN3GtCJSWj1Zz8 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-FgaN3GtCJSWj1Zz8 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-FgaN3GtCJSWj1Zz8 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-FgaN3GtCJSWj1Zz8 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-FgaN3GtCJSWj1Zz8 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-FgaN3GtCJSWj1Zz8 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-FgaN3GtCJSWj1Zz8 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-FgaN3GtCJSWj1Zz8 .marker.cross{stroke:#333333;}#mermaid-svg-FgaN3GtCJSWj1Zz8 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-FgaN3GtCJSWj1Zz8 p{margin:0;}#mermaid-svg-FgaN3GtCJSWj1Zz8 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-FgaN3GtCJSWj1Zz8 .cluster-label text{fill:#333;}#mermaid-svg-FgaN3GtCJSWj1Zz8 .cluster-label span{color:#333;}#mermaid-svg-FgaN3GtCJSWj1Zz8 .cluster-label span p{background-color:transparent;}#mermaid-svg-FgaN3GtCJSWj1Zz8 .label text,#mermaid-svg-FgaN3GtCJSWj1Zz8 span{fill:#333;color:#333;}#mermaid-svg-FgaN3GtCJSWj1Zz8 .node rect,#mermaid-svg-FgaN3GtCJSWj1Zz8 .node circle,#mermaid-svg-FgaN3GtCJSWj1Zz8 .node ellipse,#mermaid-svg-FgaN3GtCJSWj1Zz8 .node polygon,#mermaid-svg-FgaN3GtCJSWj1Zz8 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-FgaN3GtCJSWj1Zz8 .rough-node .label text,#mermaid-svg-FgaN3GtCJSWj1Zz8 .node .label text,#mermaid-svg-FgaN3GtCJSWj1Zz8 .image-shape .label,#mermaid-svg-FgaN3GtCJSWj1Zz8 .icon-shape .label{text-anchor:middle;}#mermaid-svg-FgaN3GtCJSWj1Zz8 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-FgaN3GtCJSWj1Zz8 .rough-node .label,#mermaid-svg-FgaN3GtCJSWj1Zz8 .node .label,#mermaid-svg-FgaN3GtCJSWj1Zz8 .image-shape .label,#mermaid-svg-FgaN3GtCJSWj1Zz8 .icon-shape .label{text-align:center;}#mermaid-svg-FgaN3GtCJSWj1Zz8 .node.clickable{cursor:pointer;}#mermaid-svg-FgaN3GtCJSWj1Zz8 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-FgaN3GtCJSWj1Zz8 .arrowheadPath{fill:#333333;}#mermaid-svg-FgaN3GtCJSWj1Zz8 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-FgaN3GtCJSWj1Zz8 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-FgaN3GtCJSWj1Zz8 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-FgaN3GtCJSWj1Zz8 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-FgaN3GtCJSWj1Zz8 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-FgaN3GtCJSWj1Zz8 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-FgaN3GtCJSWj1Zz8 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-FgaN3GtCJSWj1Zz8 .cluster text{fill:#333;}#mermaid-svg-FgaN3GtCJSWj1Zz8 .cluster span{color:#333;}#mermaid-svg-FgaN3GtCJSWj1Zz8 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-FgaN3GtCJSWj1Zz8 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-FgaN3GtCJSWj1Zz8 rect.text{fill:none;stroke-width:0;}#mermaid-svg-FgaN3GtCJSWj1Zz8 .icon-shape,#mermaid-svg-FgaN3GtCJSWj1Zz8 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-FgaN3GtCJSWj1Zz8 .icon-shape p,#mermaid-svg-FgaN3GtCJSWj1Zz8 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-FgaN3GtCJSWj1Zz8 .icon-shape .label rect,#mermaid-svg-FgaN3GtCJSWj1Zz8 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-FgaN3GtCJSWj1Zz8 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-FgaN3GtCJSWj1Zz8 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-FgaN3GtCJSWj1Zz8 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 外部依赖
Kubernetes Node
符号链接配置文件
SIGHUP 信号
节点标签
MPS 共享
config-manager
nvidia-device-plugin
核心插件
gpu-feature-discovery
mps-control-daemon
Kubernetes API
NVML 库
nvidia-smi
nvidia-cuda-mps-control
2. GPU Feature Discovery (GFD)
2.1 模块定位
GFD 是一个 Node Feature Discovery (NFD) 兼容的 GPU 特征发现工具。它定期扫描节点上的 GPU 硬件,将 GPU 的型号、驱动版本、CUDA 版本、MIG 能力等信息转化为 Kubernetes 节点标签,供调度器使用。
2.2 文件结构
cmd/gpu-feature-discovery/
└── main.go # 单文件实现,约 270 行
2.3 CLI 配置项
| Flag | 默认值 | 环境变量 | 用途 |
|---|---|---|---|
--mig-strategy |
none |
GFD_MIG_STRATEGY |
MIG 设备暴露策略 |
--fail-on-init-error |
true |
GFD_FAIL_ON_INIT_ERROR |
初始化失败时是否退出 |
--oneshot |
false |
GFD_ONESHOT |
标注一次后退出 |
--no-timestamp |
false |
GFD_NO_TIMESTAMP |
不添加时间戳标签 |
--sleep-interval |
60s |
GFD_SLEEP_INTERVAL |
标注间隔 |
--output-file |
/etc/kubernetes/.../gfd |
GFD_OUTPUT_FILE |
输出文件路径 |
--machine-type-file |
/sys/class/dmi/id/product_name |
GFD_MACHINE_TYPE_FILE |
DMI 信息文件 |
--config-file |
- | GFD_CONFIG_FILE |
配置文件路径 |
--use-node-feature-api |
true |
GFD_USE_NODE_FEATURE_API |
使用 NFD NodeFeature API |
--device-discovery-strategy |
auto |
DEVICE_DISCOVERY_STRATEGY |
设备发现策略 |
2.4 核心数据结构
go
// Config 收集 GFD 的 CLI 配置
type Config struct {
configFile string
kubeClientConfig flags.KubeClientConfig // K8s 客户端配置
nodeConfig flags.NodeConfig // 节点配置
flags []cli.Flag // CLI flag 列表
}
// gfd 运行时实例
type gfd struct {
manager resource.Manager // 资源管理器
vgpu vgpu.Interface // vGPU 接口
config *spec.Config // 规格配置
labelOutputer lm.Outputer // 标签输出器
}
2.5 执行流程深度解析
2.5.1 main() 函数
#mermaid-svg-TXEVRsBSOlvEjNMe{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-TXEVRsBSOlvEjNMe .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-TXEVRsBSOlvEjNMe .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-TXEVRsBSOlvEjNMe .error-icon{fill:#552222;}#mermaid-svg-TXEVRsBSOlvEjNMe .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-TXEVRsBSOlvEjNMe .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-TXEVRsBSOlvEjNMe .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-TXEVRsBSOlvEjNMe .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-TXEVRsBSOlvEjNMe .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-TXEVRsBSOlvEjNMe .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-TXEVRsBSOlvEjNMe .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-TXEVRsBSOlvEjNMe .marker{fill:#333333;stroke:#333333;}#mermaid-svg-TXEVRsBSOlvEjNMe .marker.cross{stroke:#333333;}#mermaid-svg-TXEVRsBSOlvEjNMe svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-TXEVRsBSOlvEjNMe p{margin:0;}#mermaid-svg-TXEVRsBSOlvEjNMe .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-TXEVRsBSOlvEjNMe .cluster-label text{fill:#333;}#mermaid-svg-TXEVRsBSOlvEjNMe .cluster-label span{color:#333;}#mermaid-svg-TXEVRsBSOlvEjNMe .cluster-label span p{background-color:transparent;}#mermaid-svg-TXEVRsBSOlvEjNMe .label text,#mermaid-svg-TXEVRsBSOlvEjNMe span{fill:#333;color:#333;}#mermaid-svg-TXEVRsBSOlvEjNMe .node rect,#mermaid-svg-TXEVRsBSOlvEjNMe .node circle,#mermaid-svg-TXEVRsBSOlvEjNMe .node ellipse,#mermaid-svg-TXEVRsBSOlvEjNMe .node polygon,#mermaid-svg-TXEVRsBSOlvEjNMe .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-TXEVRsBSOlvEjNMe .rough-node .label text,#mermaid-svg-TXEVRsBSOlvEjNMe .node .label text,#mermaid-svg-TXEVRsBSOlvEjNMe .image-shape .label,#mermaid-svg-TXEVRsBSOlvEjNMe .icon-shape .label{text-anchor:middle;}#mermaid-svg-TXEVRsBSOlvEjNMe .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-TXEVRsBSOlvEjNMe .rough-node .label,#mermaid-svg-TXEVRsBSOlvEjNMe .node .label,#mermaid-svg-TXEVRsBSOlvEjNMe .image-shape .label,#mermaid-svg-TXEVRsBSOlvEjNMe .icon-shape .label{text-align:center;}#mermaid-svg-TXEVRsBSOlvEjNMe .node.clickable{cursor:pointer;}#mermaid-svg-TXEVRsBSOlvEjNMe .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-TXEVRsBSOlvEjNMe .arrowheadPath{fill:#333333;}#mermaid-svg-TXEVRsBSOlvEjNMe .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-TXEVRsBSOlvEjNMe .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-TXEVRsBSOlvEjNMe .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-TXEVRsBSOlvEjNMe .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-TXEVRsBSOlvEjNMe .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-TXEVRsBSOlvEjNMe .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-TXEVRsBSOlvEjNMe .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-TXEVRsBSOlvEjNMe .cluster text{fill:#333;}#mermaid-svg-TXEVRsBSOlvEjNMe .cluster span{color:#333;}#mermaid-svg-TXEVRsBSOlvEjNMe 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-TXEVRsBSOlvEjNMe .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-TXEVRsBSOlvEjNMe rect.text{fill:none;stroke-width:0;}#mermaid-svg-TXEVRsBSOlvEjNMe .icon-shape,#mermaid-svg-TXEVRsBSOlvEjNMe .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-TXEVRsBSOlvEjNMe .icon-shape p,#mermaid-svg-TXEVRsBSOlvEjNMe .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-TXEVRsBSOlvEjNMe .icon-shape .label rect,#mermaid-svg-TXEVRsBSOlvEjNMe .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-TXEVRsBSOlvEjNMe .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-TXEVRsBSOlvEjNMe .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-TXEVRsBSOlvEjNMe :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 否
是
main 开始
创建 Config 结构
创建 cli.App
设置 App 属性
Name/Usage/Version/Action
定义 CLI flags
追加 kubeClient + node flags
c.Run 执行
运行成功?
klog.Error + os.Exit 1
正常退出
逐行解析 main():
go
func main() {
config := &Config{} // 初始化空配置结构
c := cli.NewApp() // 创建 urfave/cli 应用实例
c.Name = "GPU Feature Discovery"
c.Usage = "generate labels for NVIDIA devices"
c.Version = info.GetVersionString() // 从内部 info 包获取版本
c.Action = func(ctx *cli.Context) error {
return start(ctx, config) // 入口 action 调用 start()
}
// 定义所有 CLI flags(见 2.3 表格)
config.flags = []cli.Flag{
&cli.StringFlag{ Name: "mig-strategy", Value: spec.MigStrategyNone, ... },
&cli.BoolFlag{ Name: "fail-on-init-error", Value: true, ... },
&cli.BoolFlag{ Name: "oneshot", Value: false, ... },
&cli.BoolFlag{ Name: "no-timestamp", Value: false, ... },
&cli.GenericFlag{ Name: "sleep-interval", Value: spec.NewDurationValue(60*time.Second), ... },
&cli.StringFlag{ Name: "output-file", Value: "/etc/kubernetes/node-feature-discovery/features.d/gfd", ... },
&cli.StringFlag{ Name: "machine-type-file", Value: "/sys/class/dmi/id/product_name", ... },
&cli.StringFlag{ Name: "config-file", Destination: &config.configFile, ... },
&cli.BoolFlag{ Name: "use-node-feature-api", Value: true, ... },
&cli.StringFlag{ Name: "device-discovery-strategy", Value: "auto", ... },
&cli.StringFlag{ Name: "driver-root-ctr-path", Value: spec.DefaultContainerDriverRoot, ... },
}
// 追加 Kubernetes 客户端和节点相关 flags
config.flags = append(config.flags, config.kubeClientConfig.Flags()...)
config.flags = append(config.flags, config.nodeConfig.Flags()...)
c.Flags = config.flags
if err := c.Run(os.Args); err != nil { // 解析参数并执行
klog.Error(err)
os.Exit(1)
}
}
2.5.2 start() 函数 --- 主循环
#mermaid-svg-4uU0Y8jIZvF5eXrs{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-4uU0Y8jIZvF5eXrs .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-4uU0Y8jIZvF5eXrs .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-4uU0Y8jIZvF5eXrs .error-icon{fill:#552222;}#mermaid-svg-4uU0Y8jIZvF5eXrs .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-4uU0Y8jIZvF5eXrs .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-4uU0Y8jIZvF5eXrs .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-4uU0Y8jIZvF5eXrs .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-4uU0Y8jIZvF5eXrs .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-4uU0Y8jIZvF5eXrs .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-4uU0Y8jIZvF5eXrs .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-4uU0Y8jIZvF5eXrs .marker{fill:#333333;stroke:#333333;}#mermaid-svg-4uU0Y8jIZvF5eXrs .marker.cross{stroke:#333333;}#mermaid-svg-4uU0Y8jIZvF5eXrs svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-4uU0Y8jIZvF5eXrs p{margin:0;}#mermaid-svg-4uU0Y8jIZvF5eXrs .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-4uU0Y8jIZvF5eXrs .cluster-label text{fill:#333;}#mermaid-svg-4uU0Y8jIZvF5eXrs .cluster-label span{color:#333;}#mermaid-svg-4uU0Y8jIZvF5eXrs .cluster-label span p{background-color:transparent;}#mermaid-svg-4uU0Y8jIZvF5eXrs .label text,#mermaid-svg-4uU0Y8jIZvF5eXrs span{fill:#333;color:#333;}#mermaid-svg-4uU0Y8jIZvF5eXrs .node rect,#mermaid-svg-4uU0Y8jIZvF5eXrs .node circle,#mermaid-svg-4uU0Y8jIZvF5eXrs .node ellipse,#mermaid-svg-4uU0Y8jIZvF5eXrs .node polygon,#mermaid-svg-4uU0Y8jIZvF5eXrs .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-4uU0Y8jIZvF5eXrs .rough-node .label text,#mermaid-svg-4uU0Y8jIZvF5eXrs .node .label text,#mermaid-svg-4uU0Y8jIZvF5eXrs .image-shape .label,#mermaid-svg-4uU0Y8jIZvF5eXrs .icon-shape .label{text-anchor:middle;}#mermaid-svg-4uU0Y8jIZvF5eXrs .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-4uU0Y8jIZvF5eXrs .rough-node .label,#mermaid-svg-4uU0Y8jIZvF5eXrs .node .label,#mermaid-svg-4uU0Y8jIZvF5eXrs .image-shape .label,#mermaid-svg-4uU0Y8jIZvF5eXrs .icon-shape .label{text-align:center;}#mermaid-svg-4uU0Y8jIZvF5eXrs .node.clickable{cursor:pointer;}#mermaid-svg-4uU0Y8jIZvF5eXrs .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-4uU0Y8jIZvF5eXrs .arrowheadPath{fill:#333333;}#mermaid-svg-4uU0Y8jIZvF5eXrs .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-4uU0Y8jIZvF5eXrs .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-4uU0Y8jIZvF5eXrs .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-4uU0Y8jIZvF5eXrs .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-4uU0Y8jIZvF5eXrs .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-4uU0Y8jIZvF5eXrs .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-4uU0Y8jIZvF5eXrs .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-4uU0Y8jIZvF5eXrs .cluster text{fill:#333;}#mermaid-svg-4uU0Y8jIZvF5eXrs .cluster span{color:#333;}#mermaid-svg-4uU0Y8jIZvF5eXrs 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-4uU0Y8jIZvF5eXrs .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-4uU0Y8jIZvF5eXrs rect.text{fill:none;stroke-width:0;}#mermaid-svg-4uU0Y8jIZvF5eXrs .icon-shape,#mermaid-svg-4uU0Y8jIZvF5eXrs .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-4uU0Y8jIZvF5eXrs .icon-shape p,#mermaid-svg-4uU0Y8jIZvF5eXrs .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-4uU0Y8jIZvF5eXrs .icon-shape .label rect,#mermaid-svg-4uU0Y8jIZvF5eXrs .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-4uU0Y8jIZvF5eXrs .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-4uU0Y8jIZvF5eXrs .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-4uU0Y8jIZvF5eXrs :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 是
否
是
否
start 开始
启动 OS 信号监听
SIGHUP/SIGINT/SIGTERM/SIGQUIT
加载配置 loadConfig
禁用 Resource Naming
打印配置 JSON
初始化 NVML/device/info 库
创建 ResourceManager
创建 vGPU Lib
UseNodeFeatureAPI?
创建 K8s ClientSets
跳过
创建 LabelOutputer
创建 gfd 实例
d.run 执行标注循环
restart?
返回 nil 退出
关键代码段解析:
go
func start(c *cli.Context, cfg *Config) error {
defer func() { klog.Info("Exiting") }()
// 启动 OS 信号 watcher,监听 4 种信号
sigs := watch.Signals(syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT)
for { // 外层循环:SIGHUP 后重新加载配置重启
klog.Info("Loading configuration.")
config, err := cfg.loadConfig(c) // 从 CLI flags + config file 加载
if err != nil {
return fmt.Errorf("unable to load config: %v", err)
}
spec.DisableResourceNamingInConfig(config) // 禁用资源命名(GFD 不需要)
// 打印最终配置 JSON,便于调试
configJSON, _ := json.MarshalIndent(config, "", " ")
klog.Infof("\nRunning with config:\n%v", string(configJSON))
// 初始化三层库:nvml(底层 NVML 绑定)→ device(设备抽象)→ info(信息聚合)
nvmllib := nvml.New()
devicelib := device.New(nvmllib)
infolib := nvinfo.New(
nvinfo.WithNvmlLib(nvmllib),
nvinfo.WithDeviceLib(devicelib),
)
// 创建资源管理器:根据配置和设备信息管理 GPU 资源
manager, err := resource.NewManager(infolib, nvmllib, devicelib, config)
if err != nil {
return fmt.Errorf("failed to create resource manager: %w", err)
}
vgpul := vgpu.NewVGPULib(vgpu.NewNvidiaPCILib()) // vGPU PCI 库
// 如果启用 NodeFeature API,创建 K8s 客户端
var clientSets flags.ClientSets
if config.Flags.UseNodeFeatureAPI != nil && *config.Flags.UseNodeFeatureAPI {
cs, err := cfg.kubeClientConfig.NewClientSets()
if err != nil {
return fmt.Errorf("failed to create clientsets: %w", err)
}
clientSets = cs
}
// 创建标签输出器:决定标签写到文件还是 NodeFeature API
labelOutputer, err := lm.NewOutputer(config, cfg.nodeConfig, clientSets)
if err != nil {
return fmt.Errorf("failed to create label outputer: %w", err)
}
// 组装运行时实例
d := &gfd{
manager: manager,
vgpu: vgpul,
config: config,
labelOutputer: labelOutputer,
}
restart, err := d.run(sigs)
if err != nil {
return err
}
if !restart {
return nil // 正常退出
}
// restart=true 时回到 for 循环顶部,重新加载配置
}
}
2.5.3 gfd.run() --- 标注执行循环
#mermaid-svg-9NVejWnLTa6Cd9YT{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-9NVejWnLTa6Cd9YT .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-9NVejWnLTa6Cd9YT .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-9NVejWnLTa6Cd9YT .error-icon{fill:#552222;}#mermaid-svg-9NVejWnLTa6Cd9YT .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-9NVejWnLTa6Cd9YT .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-9NVejWnLTa6Cd9YT .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-9NVejWnLTa6Cd9YT .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-9NVejWnLTa6Cd9YT .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-9NVejWnLTa6Cd9YT .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-9NVejWnLTa6Cd9YT .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-9NVejWnLTa6Cd9YT .marker{fill:#333333;stroke:#333333;}#mermaid-svg-9NVejWnLTa6Cd9YT .marker.cross{stroke:#333333;}#mermaid-svg-9NVejWnLTa6Cd9YT svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-9NVejWnLTa6Cd9YT p{margin:0;}#mermaid-svg-9NVejWnLTa6Cd9YT .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-9NVejWnLTa6Cd9YT .cluster-label text{fill:#333;}#mermaid-svg-9NVejWnLTa6Cd9YT .cluster-label span{color:#333;}#mermaid-svg-9NVejWnLTa6Cd9YT .cluster-label span p{background-color:transparent;}#mermaid-svg-9NVejWnLTa6Cd9YT .label text,#mermaid-svg-9NVejWnLTa6Cd9YT span{fill:#333;color:#333;}#mermaid-svg-9NVejWnLTa6Cd9YT .node rect,#mermaid-svg-9NVejWnLTa6Cd9YT .node circle,#mermaid-svg-9NVejWnLTa6Cd9YT .node ellipse,#mermaid-svg-9NVejWnLTa6Cd9YT .node polygon,#mermaid-svg-9NVejWnLTa6Cd9YT .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-9NVejWnLTa6Cd9YT .rough-node .label text,#mermaid-svg-9NVejWnLTa6Cd9YT .node .label text,#mermaid-svg-9NVejWnLTa6Cd9YT .image-shape .label,#mermaid-svg-9NVejWnLTa6Cd9YT .icon-shape .label{text-anchor:middle;}#mermaid-svg-9NVejWnLTa6Cd9YT .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-9NVejWnLTa6Cd9YT .rough-node .label,#mermaid-svg-9NVejWnLTa6Cd9YT .node .label,#mermaid-svg-9NVejWnLTa6Cd9YT .image-shape .label,#mermaid-svg-9NVejWnLTa6Cd9YT .icon-shape .label{text-align:center;}#mermaid-svg-9NVejWnLTa6Cd9YT .node.clickable{cursor:pointer;}#mermaid-svg-9NVejWnLTa6Cd9YT .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-9NVejWnLTa6Cd9YT .arrowheadPath{fill:#333333;}#mermaid-svg-9NVejWnLTa6Cd9YT .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-9NVejWnLTa6Cd9YT .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-9NVejWnLTa6Cd9YT .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-9NVejWnLTa6Cd9YT .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-9NVejWnLTa6Cd9YT .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-9NVejWnLTa6Cd9YT .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-9NVejWnLTa6Cd9YT .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-9NVejWnLTa6Cd9YT .cluster text{fill:#333;}#mermaid-svg-9NVejWnLTa6Cd9YT .cluster span{color:#333;}#mermaid-svg-9NVejWnLTa6Cd9YT 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-9NVejWnLTa6Cd9YT .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-9NVejWnLTa6Cd9YT rect.text{fill:none;stroke-width:0;}#mermaid-svg-9NVejWnLTa6Cd9YT .icon-shape,#mermaid-svg-9NVejWnLTa6Cd9YT .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-9NVejWnLTa6Cd9YT .icon-shape p,#mermaid-svg-9NVejWnLTa6Cd9YT .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-9NVejWnLTa6Cd9YT .icon-shape .label rect,#mermaid-svg-9NVejWnLTa6Cd9YT .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-9NVejWnLTa6Cd9YT .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-9NVejWnLTa6Cd9YT .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-9NVejWnLTa6Cd9YT :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 是
否
是
否
infinite
有限
rerunTimeout
SIGHUP
其他信号
run 开始
设置 defer: 清理输出文件
创建 TimestampLabeler
创建 LoopLabelers
从 manager+vgpu+config
合并 Labelers
生成 Labels
labels 数量 <= 1?
警告: 无标签生成
输出标签
oneshot 模式?
返回 false 退出
sleep interval?
永久休眠
设置 rerunTimeout
等待信号
信号类型
返回 true 触发重启
返回 false 退出
逐行解析 run():
go
func (d *gfd) run(sigs chan os.Signal) (bool, error) {
// defer 逻辑:在退出时清理输出文件(仅在非 oneshot、非 infinite、有输出文件路径时)
defer func() {
if d.config.Flags.UseNodeFeatureAPI != nil && *d.config.Flags.UseNodeFeatureAPI {
return // 使用 NFD API 时不清理文件
}
if d.config.Flags.GFD.Oneshot != nil && *d.config.Flags.GFD.Oneshot {
return // oneshot 模式不清理
}
if d.config.Flags.GFD.SleepInterval.IsInfinite() {
return // 无限休眠不清理
}
if d.config.Flags.GFD.OutputFile != nil && *d.config.Flags.GFD.OutputFile == "" {
return // 无输出文件不清理
}
err := removeOutputFile(*d.config.Flags.GFD.OutputFile)
if err != nil {
klog.Warningf("Error removing output file: %v", err)
}
}()
timestampLabeler := lm.NewTimestampLabeler(d.config) // 时间戳标签生成器
rerun: // 标签重新生成入口(goto 目标)
loopLabelers, err := lm.NewLabelers(d.manager, d.vgpu, d.config) // 核心标签生成器集合
if err != nil {
return false, err
}
// 合并时间戳标签和业务标签
labelers := lm.Merge(timestampLabeler, loopLabelers)
labels, err := labelers.Labels() // 执行标签生成
if err != nil {
return false, fmt.Errorf("error generating labels: %v", err)
}
if len(labels) <= 1 { // 只有时间戳标签 → 无业务标签
klog.Warning("No labels generated from any source")
}
klog.Info("Creating Labels")
if err := d.labelOutputer.Output(labels); err != nil { // 输出到文件或 API
return false, err
}
if *d.config.Flags.GFD.Oneshot {
return false, nil // oneshot 模式:标注一次后退出
}
// 设置下次重跑的定时器
var rerunTimeout <-chan time.Time
if d.config.Flags.GFD.SleepInterval.IsInfinite() {
klog.Info("Sleep interval is infinite, sleeping indefinitely")
} else {
klog.Info("Sleeping for ", *d.config.Flags.GFD.SleepInterval)
rerunTimeout = time.After(time.Duration(*d.config.Flags.GFD.SleepInterval))
}
for {
select {
case <-rerunTimeout:
goto rerun // 定时器到期,重新标注
case s := <-sigs:
switch s {
case syscall.SIGHUP:
klog.Info("Received SIGHUP, restarting.")
return true, nil // 通知外层循环重新加载配置
default:
klog.Infof("Received signal %v, shutting down.", s)
return false, nil // 优雅退出
}
}
}
}
2.5.4 removeOutputFile() --- 清理函数
清理临时目录和输出文件,转为绝对路径后执行 RemoveAll 和 Remove。
2.6 GFD 架构图
#mermaid-svg-DVWl0s3AV9Jczlps{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-DVWl0s3AV9Jczlps .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-DVWl0s3AV9Jczlps .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-DVWl0s3AV9Jczlps .error-icon{fill:#552222;}#mermaid-svg-DVWl0s3AV9Jczlps .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-DVWl0s3AV9Jczlps .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-DVWl0s3AV9Jczlps .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-DVWl0s3AV9Jczlps .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-DVWl0s3AV9Jczlps .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-DVWl0s3AV9Jczlps .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-DVWl0s3AV9Jczlps .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-DVWl0s3AV9Jczlps .marker{fill:#333333;stroke:#333333;}#mermaid-svg-DVWl0s3AV9Jczlps .marker.cross{stroke:#333333;}#mermaid-svg-DVWl0s3AV9Jczlps svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-DVWl0s3AV9Jczlps p{margin:0;}#mermaid-svg-DVWl0s3AV9Jczlps .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-DVWl0s3AV9Jczlps .cluster-label text{fill:#333;}#mermaid-svg-DVWl0s3AV9Jczlps .cluster-label span{color:#333;}#mermaid-svg-DVWl0s3AV9Jczlps .cluster-label span p{background-color:transparent;}#mermaid-svg-DVWl0s3AV9Jczlps .label text,#mermaid-svg-DVWl0s3AV9Jczlps span{fill:#333;color:#333;}#mermaid-svg-DVWl0s3AV9Jczlps .node rect,#mermaid-svg-DVWl0s3AV9Jczlps .node circle,#mermaid-svg-DVWl0s3AV9Jczlps .node ellipse,#mermaid-svg-DVWl0s3AV9Jczlps .node polygon,#mermaid-svg-DVWl0s3AV9Jczlps .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-DVWl0s3AV9Jczlps .rough-node .label text,#mermaid-svg-DVWl0s3AV9Jczlps .node .label text,#mermaid-svg-DVWl0s3AV9Jczlps .image-shape .label,#mermaid-svg-DVWl0s3AV9Jczlps .icon-shape .label{text-anchor:middle;}#mermaid-svg-DVWl0s3AV9Jczlps .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-DVWl0s3AV9Jczlps .rough-node .label,#mermaid-svg-DVWl0s3AV9Jczlps .node .label,#mermaid-svg-DVWl0s3AV9Jczlps .image-shape .label,#mermaid-svg-DVWl0s3AV9Jczlps .icon-shape .label{text-align:center;}#mermaid-svg-DVWl0s3AV9Jczlps .node.clickable{cursor:pointer;}#mermaid-svg-DVWl0s3AV9Jczlps .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-DVWl0s3AV9Jczlps .arrowheadPath{fill:#333333;}#mermaid-svg-DVWl0s3AV9Jczlps .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-DVWl0s3AV9Jczlps .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-DVWl0s3AV9Jczlps .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-DVWl0s3AV9Jczlps .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-DVWl0s3AV9Jczlps .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-DVWl0s3AV9Jczlps .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-DVWl0s3AV9Jczlps .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-DVWl0s3AV9Jczlps .cluster text{fill:#333;}#mermaid-svg-DVWl0s3AV9Jczlps .cluster span{color:#333;}#mermaid-svg-DVWl0s3AV9Jczlps 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-DVWl0s3AV9Jczlps .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-DVWl0s3AV9Jczlps rect.text{fill:none;stroke-width:0;}#mermaid-svg-DVWl0s3AV9Jczlps .icon-shape,#mermaid-svg-DVWl0s3AV9Jczlps .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-DVWl0s3AV9Jczlps .icon-shape p,#mermaid-svg-DVWl0s3AV9Jczlps .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-DVWl0s3AV9Jczlps .icon-shape .label rect,#mermaid-svg-DVWl0s3AV9Jczlps .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-DVWl0s3AV9Jczlps .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-DVWl0s3AV9Jczlps .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-DVWl0s3AV9Jczlps :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 外部
GFD 进程
run 循环
CLI Flags 解析
Config 加载
NVML/Device/Info 库初始化
ResourceManager
vGPU Lib
LabelOutputer
TimestampLabeler
LoopLabelers
GPU/MIG/vGPU/Driver 标签
Merged Labelers
Output
→ 文件 或 NFD API
/etc/kubernetes/.../gfd
NodeFeature CRD
Kubernetes API
3. Config Manager
3.1 模块定位
Config Manager 是一个 动态配置管理工具。它监听 Kubernetes Node 对象上特定标签的变化,根据标签值选择对应的配置文件,通过符号链接将其设为设备插件的实际配置文件,并发送 SIGHUP 信号通知设备插件重新加载配置。
3.2 文件结构
cmd/config-manager/
└── main.go # 单文件实现,约 310 行
3.3 核心常量
go
const (
ResourceNodes = "nodes" // K8s 资源名
DefaultOneshot = false // 默认持续运行
DefaultSendSignal = true // 默认发送信号
DefaultSignal = int(syscall.SIGHUP) // 默认 SIGHUP
DefaultProcessToSignal = "nvidia-device-plugin" // 信号目标进程
DefaultConfigLabel = "nvidia.com/device-plugin.config" // 监听的标签
)
// 三种回退策略
const (
FallbackStrategyNamedConfig = "named" // 查找名为 "default" 的配置
FallbackStrategySingleConfig = "single" // 只有一个配置时自动选用
FallbackStrategyEmptyConfig = "empty" // 使用空配置
)
const NamedConfigFallback = "default" // named 策略查找的配置名
3.4 核心数据结构
Flags
go
type Flags struct {
Oneshot bool // 单次运行
Kubeconfig string // kubeconfig 路径
NodeName string // 目标节点名
NodeLabel string // 监听的标签 key
ConfigFileSrcdir string // 配置文件源目录(ConfigMap 挂载点)
ConfigFileDst string // 配置文件目标路径(符号链接)
DefaultConfig string // 默认配置名
FallbackStrategies cli.StringSlice // 回退策略列表
SendSignal bool // 是否发信号
Signal int // 信号值
ProcessToSignal string // 信号目标进程名
}
SyncableConfig --- 条件变量同步配置
go
// SyncableConfig 使用 sync.Cond 实现"等待配置变更"的阻塞语义
// Get() 阻塞直到 Set() 被调用,实现生产者-消费者模式
type SyncableConfig struct {
cond *sync.Cond // 条件变量
mutex sync.Mutex // 互斥锁
current string // 当前配置值
lastRead *string // 上次读取的值(用于去重)
}
SyncableConfig 语义分析:
Main Loop SyncableConfig Watcher Goroutine Main Loop SyncableConfig Watcher Goroutine #mermaid-svg-f7Wid71RHsDOHV54{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-f7Wid71RHsDOHV54 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-f7Wid71RHsDOHV54 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-f7Wid71RHsDOHV54 .error-icon{fill:#552222;}#mermaid-svg-f7Wid71RHsDOHV54 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-f7Wid71RHsDOHV54 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-f7Wid71RHsDOHV54 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-f7Wid71RHsDOHV54 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-f7Wid71RHsDOHV54 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-f7Wid71RHsDOHV54 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-f7Wid71RHsDOHV54 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-f7Wid71RHsDOHV54 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-f7Wid71RHsDOHV54 .marker.cross{stroke:#333333;}#mermaid-svg-f7Wid71RHsDOHV54 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-f7Wid71RHsDOHV54 p{margin:0;}#mermaid-svg-f7Wid71RHsDOHV54 .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-f7Wid71RHsDOHV54 text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-f7Wid71RHsDOHV54 .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-f7Wid71RHsDOHV54 .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-f7Wid71RHsDOHV54 .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-f7Wid71RHsDOHV54 .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-f7Wid71RHsDOHV54 #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-f7Wid71RHsDOHV54 .sequenceNumber{fill:white;}#mermaid-svg-f7Wid71RHsDOHV54 #sequencenumber{fill:#333;}#mermaid-svg-f7Wid71RHsDOHV54 #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-f7Wid71RHsDOHV54 .messageText{fill:#333;stroke:none;}#mermaid-svg-f7Wid71RHsDOHV54 .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-f7Wid71RHsDOHV54 .labelText,#mermaid-svg-f7Wid71RHsDOHV54 .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-f7Wid71RHsDOHV54 .loopText,#mermaid-svg-f7Wid71RHsDOHV54 .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-f7Wid71RHsDOHV54 .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-f7Wid71RHsDOHV54 .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-f7Wid71RHsDOHV54 .noteText,#mermaid-svg-f7Wid71RHsDOHV54 .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-f7Wid71RHsDOHV54 .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-f7Wid71RHsDOHV54 .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-f7Wid71RHsDOHV54 .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-f7Wid71RHsDOHV54 .actorPopupMenu{position:absolute;}#mermaid-svg-f7Wid71RHsDOHV54 .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-f7Wid71RHsDOHV54 .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-f7Wid71RHsDOHV54 .actor-man circle,#mermaid-svg-f7Wid71RHsDOHV54 line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-f7Wid71RHsDOHV54 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} current = "config-a", broadcast 阻塞(lastRead == current) current = "config-b", broadcast Set("config-a") Get() "config-a" (lastRead = "config-a") Get() Set("config-b") "config-b" (lastRead = "config-b")
关键方法解析:
go
// Set 设置新值并唤醒所有等待者
func (m *SyncableConfig) Set(value string) {
m.mutex.Lock()
defer m.mutex.Unlock()
m.current = value
m.cond.Broadcast() // 唤醒所有 Get() 等待者
}
// Get 阻塞直到值发生变化
func (m *SyncableConfig) Get() string {
m.mutex.Lock()
defer m.mutex.Unlock()
if m.lastRead != nil && *m.lastRead == m.current {
m.cond.Wait() // 值未变,阻塞等待
}
val := m.current
m.lastRead = &val // 记录已读值
return *m.lastRead
}
注意 :
lastRead是*string指针类型。每次Get()返回时lastRead指向新分配的val的地址。这种设计避免了lastRead与current引用同一底层字符串的隐患。
3.5 执行流程深度解析
3.5.1 main() 与 validateFlags()
go
func main() {
flags := Flags{}
c := cli.NewApp()
c.Before = func(c *cli.Context) error {
return validateFlags(c, &flags) // 在 Action 前校验
}
c.Action = func(c *cli.Context) error {
return start(c, &flags)
}
// 定义 11 个 CLI flags(见 3.4)
c.Flags = []cli.Flag{ ... }
err := c.Run(os.Args)
if err != nil { klog.Error(err); os.Exit(1) }
}
// 校验四个必填字段
func validateFlags(c *cli.Context, f *Flags) error {
if f.NodeName == "" { return error("invalid <node-name>") }
if f.NodeLabel == "" { return error("invalid <node-label>") }
if f.ConfigFileSrcdir == "" { return error("invalid <config-file-srcdir>") }
if f.ConfigFileDst == "" { return error("invalid <config-file-dst>") }
return nil
}
3.5.2 start() --- 主流程
#mermaid-svg-jQUUWGsT3soEfyCj{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-jQUUWGsT3soEfyCj .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-jQUUWGsT3soEfyCj .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-jQUUWGsT3soEfyCj .error-icon{fill:#552222;}#mermaid-svg-jQUUWGsT3soEfyCj .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-jQUUWGsT3soEfyCj .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-jQUUWGsT3soEfyCj .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-jQUUWGsT3soEfyCj .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-jQUUWGsT3soEfyCj .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-jQUUWGsT3soEfyCj .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-jQUUWGsT3soEfyCj .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-jQUUWGsT3soEfyCj .marker{fill:#333333;stroke:#333333;}#mermaid-svg-jQUUWGsT3soEfyCj .marker.cross{stroke:#333333;}#mermaid-svg-jQUUWGsT3soEfyCj svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-jQUUWGsT3soEfyCj p{margin:0;}#mermaid-svg-jQUUWGsT3soEfyCj .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-jQUUWGsT3soEfyCj .cluster-label text{fill:#333;}#mermaid-svg-jQUUWGsT3soEfyCj .cluster-label span{color:#333;}#mermaid-svg-jQUUWGsT3soEfyCj .cluster-label span p{background-color:transparent;}#mermaid-svg-jQUUWGsT3soEfyCj .label text,#mermaid-svg-jQUUWGsT3soEfyCj span{fill:#333;color:#333;}#mermaid-svg-jQUUWGsT3soEfyCj .node rect,#mermaid-svg-jQUUWGsT3soEfyCj .node circle,#mermaid-svg-jQUUWGsT3soEfyCj .node ellipse,#mermaid-svg-jQUUWGsT3soEfyCj .node polygon,#mermaid-svg-jQUUWGsT3soEfyCj .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-jQUUWGsT3soEfyCj .rough-node .label text,#mermaid-svg-jQUUWGsT3soEfyCj .node .label text,#mermaid-svg-jQUUWGsT3soEfyCj .image-shape .label,#mermaid-svg-jQUUWGsT3soEfyCj .icon-shape .label{text-anchor:middle;}#mermaid-svg-jQUUWGsT3soEfyCj .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-jQUUWGsT3soEfyCj .rough-node .label,#mermaid-svg-jQUUWGsT3soEfyCj .node .label,#mermaid-svg-jQUUWGsT3soEfyCj .image-shape .label,#mermaid-svg-jQUUWGsT3soEfyCj .icon-shape .label{text-align:center;}#mermaid-svg-jQUUWGsT3soEfyCj .node.clickable{cursor:pointer;}#mermaid-svg-jQUUWGsT3soEfyCj .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-jQUUWGsT3soEfyCj .arrowheadPath{fill:#333333;}#mermaid-svg-jQUUWGsT3soEfyCj .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-jQUUWGsT3soEfyCj .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-jQUUWGsT3soEfyCj .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-jQUUWGsT3soEfyCj .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-jQUUWGsT3soEfyCj .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-jQUUWGsT3soEfyCj .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-jQUUWGsT3soEfyCj .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-jQUUWGsT3soEfyCj .cluster text{fill:#333;}#mermaid-svg-jQUUWGsT3soEfyCj .cluster span{color:#333;}#mermaid-svg-jQUUWGsT3soEfyCj 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-jQUUWGsT3soEfyCj .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-jQUUWGsT3soEfyCj rect.text{fill:none;stroke-width:0;}#mermaid-svg-jQUUWGsT3soEfyCj .icon-shape,#mermaid-svg-jQUUWGsT3soEfyCj .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-jQUUWGsT3soEfyCj .icon-shape p,#mermaid-svg-jQUUWGsT3soEfyCj .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-jQUUWGsT3soEfyCj .icon-shape .label rect,#mermaid-svg-jQUUWGsT3soEfyCj .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-jQUUWGsT3soEfyCj .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-jQUUWGsT3soEfyCj .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-jQUUWGsT3soEfyCj :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 是
否
start
构建 K8s 客户端配置
创建 Clientset
创建 SyncableConfig
启动 Informer 协程
持续监听 Node 标签
阻塞等待配置变更
config.Get
收到新配置值
updateConfig
oneshot 或 error?
返回
go
func start(c *cli.Context, f *Flags) error {
// 1. 构建 K8s 客户端
kubeconfig, err := clientcmd.BuildConfigFromFlags("", f.Kubeconfig)
if err != nil { return ... }
clientset, err := kubernetes.NewForConfig(kubeconfig)
if err != nil { return ... }
// 2. 创建同步配置对象
config := NewSyncableConfig(f)
// 3. 启动 Informer 后台协程,持续监听 Node 标签变化
stop := continuouslySyncConfigChanges(clientset, config, f)
defer close(stop) // 退出时停止 Informer
// 4. 主循环:阻塞等待配置变更
for {
klog.Infof("Waiting for change to '%s' label", f.NodeLabel)
config := config.Get() // 阻塞直到标签变化
klog.Infof("Label change detected: %s=%s", f.NodeLabel, config)
err := updateConfig(config, f) // 应用配置变更
if f.Oneshot || err != nil {
return err // oneshot 模式或有错误时退出
}
}
}
3.5.3 continuouslySyncConfigChanges() --- Node Informer
#mermaid-svg-yGFNudjNNB4C2t9w{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-yGFNudjNNB4C2t9w .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-yGFNudjNNB4C2t9w .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-yGFNudjNNB4C2t9w .error-icon{fill:#552222;}#mermaid-svg-yGFNudjNNB4C2t9w .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-yGFNudjNNB4C2t9w .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-yGFNudjNNB4C2t9w .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-yGFNudjNNB4C2t9w .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-yGFNudjNNB4C2t9w .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-yGFNudjNNB4C2t9w .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-yGFNudjNNB4C2t9w .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-yGFNudjNNB4C2t9w .marker{fill:#333333;stroke:#333333;}#mermaid-svg-yGFNudjNNB4C2t9w .marker.cross{stroke:#333333;}#mermaid-svg-yGFNudjNNB4C2t9w svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-yGFNudjNNB4C2t9w p{margin:0;}#mermaid-svg-yGFNudjNNB4C2t9w .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-yGFNudjNNB4C2t9w .cluster-label text{fill:#333;}#mermaid-svg-yGFNudjNNB4C2t9w .cluster-label span{color:#333;}#mermaid-svg-yGFNudjNNB4C2t9w .cluster-label span p{background-color:transparent;}#mermaid-svg-yGFNudjNNB4C2t9w .label text,#mermaid-svg-yGFNudjNNB4C2t9w span{fill:#333;color:#333;}#mermaid-svg-yGFNudjNNB4C2t9w .node rect,#mermaid-svg-yGFNudjNNB4C2t9w .node circle,#mermaid-svg-yGFNudjNNB4C2t9w .node ellipse,#mermaid-svg-yGFNudjNNB4C2t9w .node polygon,#mermaid-svg-yGFNudjNNB4C2t9w .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-yGFNudjNNB4C2t9w .rough-node .label text,#mermaid-svg-yGFNudjNNB4C2t9w .node .label text,#mermaid-svg-yGFNudjNNB4C2t9w .image-shape .label,#mermaid-svg-yGFNudjNNB4C2t9w .icon-shape .label{text-anchor:middle;}#mermaid-svg-yGFNudjNNB4C2t9w .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-yGFNudjNNB4C2t9w .rough-node .label,#mermaid-svg-yGFNudjNNB4C2t9w .node .label,#mermaid-svg-yGFNudjNNB4C2t9w .image-shape .label,#mermaid-svg-yGFNudjNNB4C2t9w .icon-shape .label{text-align:center;}#mermaid-svg-yGFNudjNNB4C2t9w .node.clickable{cursor:pointer;}#mermaid-svg-yGFNudjNNB4C2t9w .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-yGFNudjNNB4C2t9w .arrowheadPath{fill:#333333;}#mermaid-svg-yGFNudjNNB4C2t9w .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-yGFNudjNNB4C2t9w .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-yGFNudjNNB4C2t9w .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-yGFNudjNNB4C2t9w .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-yGFNudjNNB4C2t9w .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-yGFNudjNNB4C2t9w .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-yGFNudjNNB4C2t9w .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-yGFNudjNNB4C2t9w .cluster text{fill:#333;}#mermaid-svg-yGFNudjNNB4C2t9w .cluster span{color:#333;}#mermaid-svg-yGFNudjNNB4C2t9w 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-yGFNudjNNB4C2t9w .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-yGFNudjNNB4C2t9w rect.text{fill:none;stroke-width:0;}#mermaid-svg-yGFNudjNNB4C2t9w .icon-shape,#mermaid-svg-yGFNudjNNB4C2t9w .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-yGFNudjNNB4C2t9w .icon-shape p,#mermaid-svg-yGFNudjNNB4C2t9w .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-yGFNudjNNB4C2t9w .icon-shape .label rect,#mermaid-svg-yGFNudjNNB4C2t9w .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-yGFNudjNNB4C2t9w .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-yGFNudjNNB4C2t9w .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-yGFNudjNNB4C2t9w :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Add
Update
是
否
Delete
是
否
创建 ListWatch
过滤当前 Node
创建 Informer
启动 Informer 协程
事件类型
config.Set labelsnodeLabel
标签值变化?
config.Set newLabel
旧标签非空?
config.Set ''
go
func continuouslySyncConfigChanges(clientset *kubernetes.Clientset, config *SyncableConfig, f *Flags) chan struct{} {
// 创建 ListWatch:仅关注当前节点
listWatch := cache.NewListWatchFromClient(
clientset.CoreV1().RESTClient(),
ResourceNodes, // "nodes"
v1.NamespaceAll,
fields.OneTermEqualSelector("metadata.name", f.NodeName), // 按节点名过滤
)
// 创建 Informer,注册三种事件处理器
_, controller := cache.NewInformerWithOptions(
cache.InformerOptions{
ListerWatcher: listWatch,
ObjectType: &v1.Node{},
Handler: cache.ResourceEventHandlerFuncs{
AddFunc: func(obj interface{}) {
// 节点新增:直接设置当前标签值
config.Set(obj.(*v1.Node).Labels[f.NodeLabel])
},
UpdateFunc: func(oldObj, newObj interface{}) {
// 节点更新:仅在标签值变化时通知
oldLabel := oldObj.(*v1.Node).Labels[f.NodeLabel]
newLabel := newObj.(*v1.Node).Labels[f.NodeLabel]
if oldLabel != newLabel {
config.Set(newLabel)
}
},
DeleteFunc: func(obj interface{}) {
// 节点删除:清空配置
oldLabel := obj.(*v1.Node).Labels[f.NodeLabel]
if oldLabel != "" {
config.Set("")
}
},
},
ResyncPeriod: 0, // 不做定期重同步
},
)
stop := make(chan struct{})
go controller.Run(stop) // 后台运行
return stop
}
3.5.4 updateConfig() --- 配置应用
#mermaid-svg-vFZxyivnjlsTfJWH{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-vFZxyivnjlsTfJWH .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-vFZxyivnjlsTfJWH .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-vFZxyivnjlsTfJWH .error-icon{fill:#552222;}#mermaid-svg-vFZxyivnjlsTfJWH .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-vFZxyivnjlsTfJWH .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-vFZxyivnjlsTfJWH .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-vFZxyivnjlsTfJWH .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-vFZxyivnjlsTfJWH .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-vFZxyivnjlsTfJWH .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-vFZxyivnjlsTfJWH .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-vFZxyivnjlsTfJWH .marker{fill:#333333;stroke:#333333;}#mermaid-svg-vFZxyivnjlsTfJWH .marker.cross{stroke:#333333;}#mermaid-svg-vFZxyivnjlsTfJWH svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-vFZxyivnjlsTfJWH p{margin:0;}#mermaid-svg-vFZxyivnjlsTfJWH .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-vFZxyivnjlsTfJWH .cluster-label text{fill:#333;}#mermaid-svg-vFZxyivnjlsTfJWH .cluster-label span{color:#333;}#mermaid-svg-vFZxyivnjlsTfJWH .cluster-label span p{background-color:transparent;}#mermaid-svg-vFZxyivnjlsTfJWH .label text,#mermaid-svg-vFZxyivnjlsTfJWH span{fill:#333;color:#333;}#mermaid-svg-vFZxyivnjlsTfJWH .node rect,#mermaid-svg-vFZxyivnjlsTfJWH .node circle,#mermaid-svg-vFZxyivnjlsTfJWH .node ellipse,#mermaid-svg-vFZxyivnjlsTfJWH .node polygon,#mermaid-svg-vFZxyivnjlsTfJWH .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-vFZxyivnjlsTfJWH .rough-node .label text,#mermaid-svg-vFZxyivnjlsTfJWH .node .label text,#mermaid-svg-vFZxyivnjlsTfJWH .image-shape .label,#mermaid-svg-vFZxyivnjlsTfJWH .icon-shape .label{text-anchor:middle;}#mermaid-svg-vFZxyivnjlsTfJWH .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-vFZxyivnjlsTfJWH .rough-node .label,#mermaid-svg-vFZxyivnjlsTfJWH .node .label,#mermaid-svg-vFZxyivnjlsTfJWH .image-shape .label,#mermaid-svg-vFZxyivnjlsTfJWH .icon-shape .label{text-align:center;}#mermaid-svg-vFZxyivnjlsTfJWH .node.clickable{cursor:pointer;}#mermaid-svg-vFZxyivnjlsTfJWH .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-vFZxyivnjlsTfJWH .arrowheadPath{fill:#333333;}#mermaid-svg-vFZxyivnjlsTfJWH .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-vFZxyivnjlsTfJWH .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-vFZxyivnjlsTfJWH .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-vFZxyivnjlsTfJWH .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-vFZxyivnjlsTfJWH .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-vFZxyivnjlsTfJWH .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-vFZxyivnjlsTfJWH .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-vFZxyivnjlsTfJWH .cluster text{fill:#333;}#mermaid-svg-vFZxyivnjlsTfJWH .cluster span{color:#333;}#mermaid-svg-vFZxyivnjlsTfJWH 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-vFZxyivnjlsTfJWH .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-vFZxyivnjlsTfJWH rect.text{fill:none;stroke-width:0;}#mermaid-svg-vFZxyivnjlsTfJWH .icon-shape,#mermaid-svg-vFZxyivnjlsTfJWH .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-vFZxyivnjlsTfJWH .icon-shape p,#mermaid-svg-vFZxyivnjlsTfJWH .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-vFZxyivnjlsTfJWH .icon-shape .label rect,#mermaid-svg-vFZxyivnjlsTfJWH .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-vFZxyivnjlsTfJWH .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-vFZxyivnjlsTfJWH .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-vFZxyivnjlsTfJWH :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 是
否
否
是
否
是
updateConfig
updateConfigName
解析配置名
配置名为空?
日志: 空配置
日志: 配置名
updateSymlink
更新符号链接
符号链接已更新?
跳过:已是目标配置
send-signal?
完成
signalProcess
发送 SIGHUP
3.5.5 updateConfigName() --- 配置名解析与回退
go
func updateConfigName(config string, f *Flags) (string, error) {
// 1. 获取可用配置文件列表
files, err := getConfigFileNameMap(f)
if err != nil { return "", err }
if len(files) == 0 { return "", error("no configuration files") }
// 2. 如果有显式配置名,检查是否存在
if config != "" {
if !files[config] { return "", error("specified config does not exist") }
return config, nil
}
// 3. 如果有默认配置,检查是否存在
if f.DefaultConfig != "" {
if !files[f.DefaultConfig] { return "", error("default config does not exist") }
return f.DefaultConfig, nil
}
// 4. 按顺序尝试回退策略
for _, fallback := range f.FallbackStrategies.Value() {
switch fallback {
case "named": // 查找名为 "default" 的配置
if files[NamedConfigFallback] { return NamedConfigFallback, nil }
case "single": // 只有一个配置时自动选用
if len(files) == 1 { return filenames[0], nil }
case "empty": // 使用空配置
return "", nil
}
}
return "", error("no config set, no default, all fallbacks failed")
}
回退策略流程图:
#mermaid-svg-HbzGU1CyAni3oXgj{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-HbzGU1CyAni3oXgj .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-HbzGU1CyAni3oXgj .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-HbzGU1CyAni3oXgj .error-icon{fill:#552222;}#mermaid-svg-HbzGU1CyAni3oXgj .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-HbzGU1CyAni3oXgj .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-HbzGU1CyAni3oXgj .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-HbzGU1CyAni3oXgj .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-HbzGU1CyAni3oXgj .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-HbzGU1CyAni3oXgj .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-HbzGU1CyAni3oXgj .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-HbzGU1CyAni3oXgj .marker{fill:#333333;stroke:#333333;}#mermaid-svg-HbzGU1CyAni3oXgj .marker.cross{stroke:#333333;}#mermaid-svg-HbzGU1CyAni3oXgj svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-HbzGU1CyAni3oXgj p{margin:0;}#mermaid-svg-HbzGU1CyAni3oXgj .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-HbzGU1CyAni3oXgj .cluster-label text{fill:#333;}#mermaid-svg-HbzGU1CyAni3oXgj .cluster-label span{color:#333;}#mermaid-svg-HbzGU1CyAni3oXgj .cluster-label span p{background-color:transparent;}#mermaid-svg-HbzGU1CyAni3oXgj .label text,#mermaid-svg-HbzGU1CyAni3oXgj span{fill:#333;color:#333;}#mermaid-svg-HbzGU1CyAni3oXgj .node rect,#mermaid-svg-HbzGU1CyAni3oXgj .node circle,#mermaid-svg-HbzGU1CyAni3oXgj .node ellipse,#mermaid-svg-HbzGU1CyAni3oXgj .node polygon,#mermaid-svg-HbzGU1CyAni3oXgj .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-HbzGU1CyAni3oXgj .rough-node .label text,#mermaid-svg-HbzGU1CyAni3oXgj .node .label text,#mermaid-svg-HbzGU1CyAni3oXgj .image-shape .label,#mermaid-svg-HbzGU1CyAni3oXgj .icon-shape .label{text-anchor:middle;}#mermaid-svg-HbzGU1CyAni3oXgj .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-HbzGU1CyAni3oXgj .rough-node .label,#mermaid-svg-HbzGU1CyAni3oXgj .node .label,#mermaid-svg-HbzGU1CyAni3oXgj .image-shape .label,#mermaid-svg-HbzGU1CyAni3oXgj .icon-shape .label{text-align:center;}#mermaid-svg-HbzGU1CyAni3oXgj .node.clickable{cursor:pointer;}#mermaid-svg-HbzGU1CyAni3oXgj .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-HbzGU1CyAni3oXgj .arrowheadPath{fill:#333333;}#mermaid-svg-HbzGU1CyAni3oXgj .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-HbzGU1CyAni3oXgj .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-HbzGU1CyAni3oXgj .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-HbzGU1CyAni3oXgj .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-HbzGU1CyAni3oXgj .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-HbzGU1CyAni3oXgj .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-HbzGU1CyAni3oXgj .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-HbzGU1CyAni3oXgj .cluster text{fill:#333;}#mermaid-svg-HbzGU1CyAni3oXgj .cluster span{color:#333;}#mermaid-svg-HbzGU1CyAni3oXgj 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-HbzGU1CyAni3oXgj .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-HbzGU1CyAni3oXgj rect.text{fill:none;stroke-width:0;}#mermaid-svg-HbzGU1CyAni3oXgj .icon-shape,#mermaid-svg-HbzGU1CyAni3oXgj .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-HbzGU1CyAni3oXgj .icon-shape p,#mermaid-svg-HbzGU1CyAni3oXgj .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-HbzGU1CyAni3oXgj .icon-shape .label rect,#mermaid-svg-HbzGU1CyAni3oXgj .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-HbzGU1CyAni3oXgj .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-HbzGU1CyAni3oXgj .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-HbzGU1CyAni3oXgj :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 是
是
否
否
是
是
否
否
是
是
否
否
是
否
config 值为空
有 DefaultConfig?
DefaultConfig 存在?
返回 DefaultConfig
报错
遍历 FallbackStrategies
策略: named?
存在 'default' 配置?
返回 'default'
继续下一策略
策略: single?
只有一个配置?
返回该配置
策略: empty?
返回空字符串
报错: 未知策略
3.5.6 updateSymlink() --- 符号链接管理
go
func updateSymlink(config string, f *Flags) (bool, error) {
src := "/dev/null" // 空配置指向 /dev/null
if config != "" {
src = filepath.Join(f.ConfigFileSrcdir, config) // 非空指向源目录下的配置文件
}
// 检查目标是否已存在
exists, err := fileExists(f.ConfigFileDst)
if exists {
// 比较现有符号链接目标与新目标
srcRealpath, _ := filepath.EvalSymlinks(src)
dstRealpath, _ := filepath.EvalSymlinks(f.ConfigFileDst)
if srcRealpath == dstRealpath {
return false, nil // 已指向正确目标,无需更新
}
// 删除旧符号链接
os.Remove(f.ConfigFileDst)
}
// 创建新符号链接
os.Symlink(src, f.ConfigFileDst)
return true, nil
}
3.5.7 signalProcess() --- 信号发送
通过 procfs.AllProcs() 遍历 /proc 下所有进程,匹配 cmdline[0] == f.ProcessToSignal,找到 PID 后 syscall.Kill(pid, signal) 发送信号。
3.5.8 getConfigFileNameMap() --- 配置文件发现
读取源目录,排除子目录和 ConfigMap 特殊文件(以 .. 开头的元数据文件如 ..data、..2025_07_26_..),返回可用配置名 map。
3.6 Config Manager 完整流程图
nvidia-device-plugin Filesystem Main Loop SyncableConfig Informer Kubernetes API nvidia-device-plugin Filesystem Main Loop SyncableConfig Informer Kubernetes API #mermaid-svg-inNQdjIVNmqi8pgS{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-inNQdjIVNmqi8pgS .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-inNQdjIVNmqi8pgS .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-inNQdjIVNmqi8pgS .error-icon{fill:#552222;}#mermaid-svg-inNQdjIVNmqi8pgS .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-inNQdjIVNmqi8pgS .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-inNQdjIVNmqi8pgS .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-inNQdjIVNmqi8pgS .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-inNQdjIVNmqi8pgS .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-inNQdjIVNmqi8pgS .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-inNQdjIVNmqi8pgS .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-inNQdjIVNmqi8pgS .marker{fill:#333333;stroke:#333333;}#mermaid-svg-inNQdjIVNmqi8pgS .marker.cross{stroke:#333333;}#mermaid-svg-inNQdjIVNmqi8pgS svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-inNQdjIVNmqi8pgS p{margin:0;}#mermaid-svg-inNQdjIVNmqi8pgS .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-inNQdjIVNmqi8pgS text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-inNQdjIVNmqi8pgS .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-inNQdjIVNmqi8pgS .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-inNQdjIVNmqi8pgS .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-inNQdjIVNmqi8pgS .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-inNQdjIVNmqi8pgS #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-inNQdjIVNmqi8pgS .sequenceNumber{fill:white;}#mermaid-svg-inNQdjIVNmqi8pgS #sequencenumber{fill:#333;}#mermaid-svg-inNQdjIVNmqi8pgS #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-inNQdjIVNmqi8pgS .messageText{fill:#333;stroke:none;}#mermaid-svg-inNQdjIVNmqi8pgS .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-inNQdjIVNmqi8pgS .labelText,#mermaid-svg-inNQdjIVNmqi8pgS .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-inNQdjIVNmqi8pgS .loopText,#mermaid-svg-inNQdjIVNmqi8pgS .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-inNQdjIVNmqi8pgS .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-inNQdjIVNmqi8pgS .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-inNQdjIVNmqi8pgS .noteText,#mermaid-svg-inNQdjIVNmqi8pgS .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-inNQdjIVNmqi8pgS .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-inNQdjIVNmqi8pgS .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-inNQdjIVNmqi8pgS .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-inNQdjIVNmqi8pgS .actorPopupMenu{position:absolute;}#mermaid-svg-inNQdjIVNmqi8pgS .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-inNQdjIVNmqi8pgS .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-inNQdjIVNmqi8pgS .actor-man circle,#mermaid-svg-inNQdjIVNmqi8pgS line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-inNQdjIVNmqi8pgS :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Node label 变更事件 Set("new-config") Get() 返回 "new-config" updateConfigName("new-config") 读取源目录,验证配置存在 EvalSymlinks 比较当前链接 Remove 旧链接 + Symlink 新链接 findPidToSignal("nvidia-device-plugin") syscall.Kill(pid, SIGHUP) 重新加载配置 Get() 阻塞等待下一次变更
4. MPS Control Daemon
4.1 模块定位
MPS Control Daemon 是 NVIDIA Multi-Process Service 的 Kubernetes 侧管理器。MPS 允许多个进程共享同一块 GPU,通过软硬件协同提供 GPU 隔离。此守护进程负责:
- 为每个共享资源启动
nvidia-cuda-mps-control后台进程 - 设置 GPU 的计算模式为
EXCLUSIVE_PROCESS - 配置每设备的显存限制和线程百分比
- 管理 MPS 管道目录、日志目录和共享内存
4.2 文件结构
cmd/mps-control-daemon/
├── main.go # 入口,守护进程生命周期管理
├── mps/
│ ├── daemon.go # MPS Daemon 实现(启停、控制)
│ ├── device.go # MPS 设备校验(replica 限制)
│ ├── log-tailer.go # 日志跟踪器
│ ├── manager.go # Daemon 管理器(创建多个 Daemon)
│ ├── options.go # 函数式选项模式
│ └── root.go # 路径管理
└── mount/
└── mount-shm.go # tmpfs 共享内存挂载
4.3 入口 main.go 深度解析
4.3.1 main() 函数
创建 cli.App,注册 --config-file 和 --mig-strategy 两个顶层 flag,注册 mount-shm 子命令,c.Action 调用 start()。
4.3.2 loadConfig() --- 配置加载
通过 spec.NewConfig() 从 CLI flags + 配置文件加载配置,调用 validateFlags()(当前空实现),清空 config.Flags.GFD = nil(MPS 不需要 GFD 配置)。
4.3.3 start() --- 生命周期管理
#mermaid-svg-3z0YrcsyCmWIE3Eb{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-3z0YrcsyCmWIE3Eb .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-3z0YrcsyCmWIE3Eb .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-3z0YrcsyCmWIE3Eb .error-icon{fill:#552222;}#mermaid-svg-3z0YrcsyCmWIE3Eb .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-3z0YrcsyCmWIE3Eb .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-3z0YrcsyCmWIE3Eb .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-3z0YrcsyCmWIE3Eb .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-3z0YrcsyCmWIE3Eb .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-3z0YrcsyCmWIE3Eb .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-3z0YrcsyCmWIE3Eb .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-3z0YrcsyCmWIE3Eb .marker{fill:#333333;stroke:#333333;}#mermaid-svg-3z0YrcsyCmWIE3Eb .marker.cross{stroke:#333333;}#mermaid-svg-3z0YrcsyCmWIE3Eb svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-3z0YrcsyCmWIE3Eb p{margin:0;}#mermaid-svg-3z0YrcsyCmWIE3Eb .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-3z0YrcsyCmWIE3Eb .cluster-label text{fill:#333;}#mermaid-svg-3z0YrcsyCmWIE3Eb .cluster-label span{color:#333;}#mermaid-svg-3z0YrcsyCmWIE3Eb .cluster-label span p{background-color:transparent;}#mermaid-svg-3z0YrcsyCmWIE3Eb .label text,#mermaid-svg-3z0YrcsyCmWIE3Eb span{fill:#333;color:#333;}#mermaid-svg-3z0YrcsyCmWIE3Eb .node rect,#mermaid-svg-3z0YrcsyCmWIE3Eb .node circle,#mermaid-svg-3z0YrcsyCmWIE3Eb .node ellipse,#mermaid-svg-3z0YrcsyCmWIE3Eb .node polygon,#mermaid-svg-3z0YrcsyCmWIE3Eb .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-3z0YrcsyCmWIE3Eb .rough-node .label text,#mermaid-svg-3z0YrcsyCmWIE3Eb .node .label text,#mermaid-svg-3z0YrcsyCmWIE3Eb .image-shape .label,#mermaid-svg-3z0YrcsyCmWIE3Eb .icon-shape .label{text-anchor:middle;}#mermaid-svg-3z0YrcsyCmWIE3Eb .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-3z0YrcsyCmWIE3Eb .rough-node .label,#mermaid-svg-3z0YrcsyCmWIE3Eb .node .label,#mermaid-svg-3z0YrcsyCmWIE3Eb .image-shape .label,#mermaid-svg-3z0YrcsyCmWIE3Eb .icon-shape .label{text-align:center;}#mermaid-svg-3z0YrcsyCmWIE3Eb .node.clickable{cursor:pointer;}#mermaid-svg-3z0YrcsyCmWIE3Eb .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-3z0YrcsyCmWIE3Eb .arrowheadPath{fill:#333333;}#mermaid-svg-3z0YrcsyCmWIE3Eb .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-3z0YrcsyCmWIE3Eb .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-3z0YrcsyCmWIE3Eb .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-3z0YrcsyCmWIE3Eb .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-3z0YrcsyCmWIE3Eb .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-3z0YrcsyCmWIE3Eb .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-3z0YrcsyCmWIE3Eb .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-3z0YrcsyCmWIE3Eb .cluster text{fill:#333;}#mermaid-svg-3z0YrcsyCmWIE3Eb .cluster span{color:#333;}#mermaid-svg-3z0YrcsyCmWIE3Eb 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-3z0YrcsyCmWIE3Eb .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-3z0YrcsyCmWIE3Eb rect.text{fill:none;stroke-width:0;}#mermaid-svg-3z0YrcsyCmWIE3Eb .icon-shape,#mermaid-svg-3z0YrcsyCmWIE3Eb .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-3z0YrcsyCmWIE3Eb .icon-shape p,#mermaid-svg-3z0YrcsyCmWIE3Eb .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-3z0YrcsyCmWIE3Eb .icon-shape .label rect,#mermaid-svg-3z0YrcsyCmWIE3Eb .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-3z0YrcsyCmWIE3Eb .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-3z0YrcsyCmWIE3Eb .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-3z0YrcsyCmWIE3Eb :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 失败
成功
重试超时
SIGHUP
其他信号
start
启动信号监听
startDaemons
启动成功?
设置 30s 重试定时器
创建 /mps/.ready 文件
等待事件
事件类型
stopDaemons 旧实例
stopDaemons
退出
逐行解析:
go
func start(c *cli.Context, cfg *Config) error {
klog.Info("Starting OS watcher.")
sigs := watch.Signals(syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT)
var started bool // 是否已启动过(用于重启时停止旧实例)
var restartTimeout <-chan time.Time // 重试定时器
var daemons []*mps.Daemon // 当前运行的 Daemon 列表
restart:
if started { // 如果是重启,先停止旧的所有 daemon
err := stopDaemons(daemons...)
if err != nil { return ... }
}
klog.Info("Starting Daemons.")
daemons, restartDaemons, err := startDaemons(c, cfg)
if err != nil { return ... }
started = true
if restartDaemons { // 启动失败,30 秒后重试
klog.Infof("Failed to start one or more MPS deamons. Retrying in 30s...")
restartTimeout = time.After(30 * time.Second)
}
for {
select {
case <-restartTimeout:
goto restart // 重试
case s := <-sigs:
switch s {
case syscall.SIGHUP:
klog.Info("Received SIGHUP, restarting.")
goto restart // 重新启动所有 daemon
default:
klog.Infof("Received signal \"%v\", shutting down.", s)
goto exit
}
}
}
exit:
if err := stopDaemons(daemons...); err != nil { return ... }
return nil
}
4.3.4 startDaemons() --- 创建并启动所有 Daemon
go
func startDaemons(c *cli.Context, cfg *Config) ([]*mps.Daemon, bool, error) {
// 1. 加载配置
config, err := cfg.loadConfig(c)
spec.DisableResourceNamingInConfig(config)
// 2. 初始化 NVML 库三层
nvmllib := nvml.New()
devicelib := device.New(nvmllib)
infolib := nvinfo.New(...)
// 3. 添加默认资源配置
rm.AddDefaultResourcesToConfig(infolib, nvmllib, devicelib, config)
// 4. 打印配置 JSON
configJSON, _ := json.MarshalIndent(config, "", " ")
klog.Infof("\nRunning with config:\n%v", string(configJSON))
// 5. 创建 MPS Daemon 集合
mpsDaemons, err := mps.NewDaemons(infolib, nvmllib, devicelib,
mps.WithConfig(config),
)
if err != nil { return nil, false, ... }
if len(mpsDaemons) == 0 {
klog.Info("No devices are configured for MPS sharing; Waiting indefinitely.")
}
// 6. 逐个启动 Daemon
for _, mpsDaemon := range mpsDaemons {
if err := mpsDaemon.Start(); err != nil {
klog.Errorf("Failed to start MPS daemon: %v", err)
return mpsDaemons, true, nil // 返回 restartDaemons=true 触发重试
}
}
// 7. 创建 ready 文件,通知外部 daemon 已就绪
readyFile, err := os.Create("/mps/.ready")
if err != nil { return mpsDaemons, true, ... }
defer readyFile.Close()
return mpsDaemons, false, nil
}
4.3.5 stopDaemons() --- 停止所有 Daemon
go
func stopDaemons(mpsDaemons ...*mps.Daemon) error {
// 1. 删除 ready 文件,通知外部 daemon 已停止
os.Remove("/mps/.ready")
// 2. 逐个停止每个 daemon
klog.Info("Stopping MPS daemons.")
var errs error
for _, p := range mpsDaemons {
errs = errors.Join(errs, p.Stop()) // 累积错误
}
return errs
}
4.4 MPS Control Daemon 架构图
#mermaid-svg-gNTeE7STRgo6rpxX{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-gNTeE7STRgo6rpxX .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-gNTeE7STRgo6rpxX .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-gNTeE7STRgo6rpxX .error-icon{fill:#552222;}#mermaid-svg-gNTeE7STRgo6rpxX .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-gNTeE7STRgo6rpxX .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-gNTeE7STRgo6rpxX .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-gNTeE7STRgo6rpxX .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-gNTeE7STRgo6rpxX .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-gNTeE7STRgo6rpxX .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-gNTeE7STRgo6rpxX .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-gNTeE7STRgo6rpxX .marker{fill:#333333;stroke:#333333;}#mermaid-svg-gNTeE7STRgo6rpxX .marker.cross{stroke:#333333;}#mermaid-svg-gNTeE7STRgo6rpxX svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-gNTeE7STRgo6rpxX p{margin:0;}#mermaid-svg-gNTeE7STRgo6rpxX .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-gNTeE7STRgo6rpxX .cluster-label text{fill:#333;}#mermaid-svg-gNTeE7STRgo6rpxX .cluster-label span{color:#333;}#mermaid-svg-gNTeE7STRgo6rpxX .cluster-label span p{background-color:transparent;}#mermaid-svg-gNTeE7STRgo6rpxX .label text,#mermaid-svg-gNTeE7STRgo6rpxX span{fill:#333;color:#333;}#mermaid-svg-gNTeE7STRgo6rpxX .node rect,#mermaid-svg-gNTeE7STRgo6rpxX .node circle,#mermaid-svg-gNTeE7STRgo6rpxX .node ellipse,#mermaid-svg-gNTeE7STRgo6rpxX .node polygon,#mermaid-svg-gNTeE7STRgo6rpxX .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-gNTeE7STRgo6rpxX .rough-node .label text,#mermaid-svg-gNTeE7STRgo6rpxX .node .label text,#mermaid-svg-gNTeE7STRgo6rpxX .image-shape .label,#mermaid-svg-gNTeE7STRgo6rpxX .icon-shape .label{text-anchor:middle;}#mermaid-svg-gNTeE7STRgo6rpxX .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-gNTeE7STRgo6rpxX .rough-node .label,#mermaid-svg-gNTeE7STRgo6rpxX .node .label,#mermaid-svg-gNTeE7STRgo6rpxX .image-shape .label,#mermaid-svg-gNTeE7STRgo6rpxX .icon-shape .label{text-align:center;}#mermaid-svg-gNTeE7STRgo6rpxX .node.clickable{cursor:pointer;}#mermaid-svg-gNTeE7STRgo6rpxX .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-gNTeE7STRgo6rpxX .arrowheadPath{fill:#333333;}#mermaid-svg-gNTeE7STRgo6rpxX .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-gNTeE7STRgo6rpxX .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-gNTeE7STRgo6rpxX .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-gNTeE7STRgo6rpxX .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-gNTeE7STRgo6rpxX .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-gNTeE7STRgo6rpxX .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-gNTeE7STRgo6rpxX .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-gNTeE7STRgo6rpxX .cluster text{fill:#333;}#mermaid-svg-gNTeE7STRgo6rpxX .cluster span{color:#333;}#mermaid-svg-gNTeE7STRgo6rpxX 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-gNTeE7STRgo6rpxX .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-gNTeE7STRgo6rpxX rect.text{fill:none;stroke-width:0;}#mermaid-svg-gNTeE7STRgo6rpxX .icon-shape,#mermaid-svg-gNTeE7STRgo6rpxX .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-gNTeE7STRgo6rpxX .icon-shape p,#mermaid-svg-gNTeE7STRgo6rpxX .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-gNTeE7STRgo6rpxX .icon-shape .label rect,#mermaid-svg-gNTeE7STRgo6rpxX .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-gNTeE7STRgo6rpxX .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-gNTeE7STRgo6rpxX .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-gNTeE7STRgo6rpxX :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 每个 Daemon 管理
MPS Control Daemon 进程
Per-Resource Daemon
main.go start
startDaemons
mps.Manager
Daemon-1
resource=nvidia.com/gpu
Daemon-2
resource=nvidia.com/gpu.shared
Daemon-N
/mps/.ready 文件
nvidia-cuda-mps-control -d
nvidia-smi -c EXCLUSIVE_PROCESS
/mps/ResourceName/pipe
/mps/ResourceName/log
tail -f control.log
5. MPS Daemon 核心逻辑
5.1 Daemon 结构体
go
type Daemon struct {
rm rm.ResourceManager // 关联的资源管理器
root Root // MPS 根路径(默认 /mps)
logTailer *tailer // 日志跟踪器
}
5.2 核心常量与类型
go
type computeMode string
const (
mpsControlBin = "nvidia-cuda-mps-control" // MPS 控制二进制
computeModeExclusiveProcess = computeMode("EXCLUSIVE_PROCESS") // 独占进程模式
computeModeDefault = computeMode("DEFAULT") // 默认模式
unprivilegedContainerSELinuxLabel = "system_u:object_r:container_file_t:s0" // SELinux 标签
)
5.3 Start() --- 启动 MPS Daemon
#mermaid-svg-KrvwkeTpld1WFppf{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-KrvwkeTpld1WFppf .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-KrvwkeTpld1WFppf .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-KrvwkeTpld1WFppf .error-icon{fill:#552222;}#mermaid-svg-KrvwkeTpld1WFppf .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-KrvwkeTpld1WFppf .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-KrvwkeTpld1WFppf .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-KrvwkeTpld1WFppf .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-KrvwkeTpld1WFppf .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-KrvwkeTpld1WFppf .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-KrvwkeTpld1WFppf .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-KrvwkeTpld1WFppf .marker{fill:#333333;stroke:#333333;}#mermaid-svg-KrvwkeTpld1WFppf .marker.cross{stroke:#333333;}#mermaid-svg-KrvwkeTpld1WFppf svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-KrvwkeTpld1WFppf p{margin:0;}#mermaid-svg-KrvwkeTpld1WFppf .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-KrvwkeTpld1WFppf .cluster-label text{fill:#333;}#mermaid-svg-KrvwkeTpld1WFppf .cluster-label span{color:#333;}#mermaid-svg-KrvwkeTpld1WFppf .cluster-label span p{background-color:transparent;}#mermaid-svg-KrvwkeTpld1WFppf .label text,#mermaid-svg-KrvwkeTpld1WFppf span{fill:#333;color:#333;}#mermaid-svg-KrvwkeTpld1WFppf .node rect,#mermaid-svg-KrvwkeTpld1WFppf .node circle,#mermaid-svg-KrvwkeTpld1WFppf .node ellipse,#mermaid-svg-KrvwkeTpld1WFppf .node polygon,#mermaid-svg-KrvwkeTpld1WFppf .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-KrvwkeTpld1WFppf .rough-node .label text,#mermaid-svg-KrvwkeTpld1WFppf .node .label text,#mermaid-svg-KrvwkeTpld1WFppf .image-shape .label,#mermaid-svg-KrvwkeTpld1WFppf .icon-shape .label{text-anchor:middle;}#mermaid-svg-KrvwkeTpld1WFppf .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-KrvwkeTpld1WFppf .rough-node .label,#mermaid-svg-KrvwkeTpld1WFppf .node .label,#mermaid-svg-KrvwkeTpld1WFppf .image-shape .label,#mermaid-svg-KrvwkeTpld1WFppf .icon-shape .label{text-align:center;}#mermaid-svg-KrvwkeTpld1WFppf .node.clickable{cursor:pointer;}#mermaid-svg-KrvwkeTpld1WFppf .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-KrvwkeTpld1WFppf .arrowheadPath{fill:#333333;}#mermaid-svg-KrvwkeTpld1WFppf .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-KrvwkeTpld1WFppf .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-KrvwkeTpld1WFppf .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-KrvwkeTpld1WFppf .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-KrvwkeTpld1WFppf .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-KrvwkeTpld1WFppf .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-KrvwkeTpld1WFppf .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-KrvwkeTpld1WFppf .cluster text{fill:#333;}#mermaid-svg-KrvwkeTpld1WFppf .cluster span{color:#333;}#mermaid-svg-KrvwkeTpld1WFppf 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-KrvwkeTpld1WFppf .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-KrvwkeTpld1WFppf rect.text{fill:none;stroke-width:0;}#mermaid-svg-KrvwkeTpld1WFppf .icon-shape,#mermaid-svg-KrvwkeTpld1WFppf .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-KrvwkeTpld1WFppf .icon-shape p,#mermaid-svg-KrvwkeTpld1WFppf .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-KrvwkeTpld1WFppf .icon-shape .label rect,#mermaid-svg-KrvwkeTpld1WFppf .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-KrvwkeTpld1WFppf .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-KrvwkeTpld1WFppf .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-KrvwkeTpld1WFppf :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 是
否
Start
setComputeMode
EXCLUSIVE_PROCESS
创建 PipeDir
/mps/Resource/pipe
设置 SELinux 上下文
创建 LogDir
/mps/Resource/log
启动 nvidia-cuda-mps-control -d
设置每设备显存限制
set_default_device_pinned_mem_limit
有 thread percentage?
设置线程百分比
set_default_active_thread_percentage
创建 .started 文件
启动 log tailer
tail -f control.log
完成
逐行解析 Start():
go
func (d *Daemon) Start() error {
// 1. 将所有关联 GPU 设置为 EXCLUSIVE_PROCESS 模式
// MPS 要求 GPU 处于独占进程模式才能正常工作
if err := d.setComputeMode(computeModeExclusiveProcess); err != nil {
return fmt.Errorf("error setting compute mode %v: %w", computeModeExclusiveProcess, err)
}
klog.InfoS("Staring MPS daemon", "resource", d.rm.Resource())
// 2. 创建管道目录(MPS 客户端通过此目录与服务端通信)
pipeDir := d.PipeDir()
if err := os.MkdirAll(pipeDir, 0755); err != nil {
return fmt.Errorf("error creating directory %v: %w", pipeDir, err)
}
// 3. 设置 SELinux 上下文(确保非特权容器可访问)
if err := setSELinuxContext(pipeDir, unprivilegedContainerSELinuxLabel); err != nil {
return fmt.Errorf("error setting SELinux context: %w", err)
}
// 4. 创建日志目录
logDir := d.LogDir()
if err := os.MkdirAll(logDir, 0755); err != nil {
return fmt.Errorf("error creating directory %v: %w", logDir, err)
}
// 5. 启动 nvidia-cuda-mps-control 守护进程(-d 表示 daemon 模式)
mpsDaemon := exec.Command(mpsControlBin, "-d")
mpsDaemon.Env = append(mpsDaemon.Env, d.EnvVars().toSlice()...) // 设置管道和日志目录环境变量
if err := mpsDaemon.Run(); err != nil {
return err
}
// 6. 为每个设备设置显存限制
// 公式:per_device_limit = total_memory / replicas
for index, limit := range d.perDevicePinnedDeviceMemoryLimits() {
_, err := d.EchoPipeToControl(
fmt.Sprintf("set_default_device_pinned_mem_limit %s %s", index, limit),
)
if err != nil { return ... }
}
// 7. 设置线程百分比(如果有共享副本)
if threadPercentage := d.activeThreadPercentage(); threadPercentage != "" {
_, err := d.EchoPipeToControl(
fmt.Sprintf("set_default_active_thread_percentage %s", threadPercentage),
)
if err != nil { return ... }
}
// 8. 创建 .started 标记文件
statusFile, err := os.Create(d.startedFile())
if err != nil { return err }
defer statusFile.Close()
// 9. 启动日志跟踪器,将 control.log 转发到 stdout
d.logTailer = newTailer(filepath.Join(logDir, "control.log"))
if err := d.logTailer.Start(); err != nil {
klog.ErrorS(err, "Could not start tail command on control.log; ignoring logs")
}
return nil
}
5.4 Stop() --- 停止 MPS Daemon
EchoPipeToControl("quit")发送退出命令- 停止 log tailer
setComputeMode(DEFAULT)恢复 GPU 计算模式- 删除
.started文件 os.RemoveAll(LogDir)清理日志目录
5.5 EchoPipeToControl() --- MPS 控制命令通道
MPS Server nvidia-cuda-mps-control io.Pipe Daemon MPS Server nvidia-cuda-mps-control io.Pipe Daemon #mermaid-svg-eBZE6f5LhZOT6V5F{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-eBZE6f5LhZOT6V5F .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-eBZE6f5LhZOT6V5F .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-eBZE6f5LhZOT6V5F .error-icon{fill:#552222;}#mermaid-svg-eBZE6f5LhZOT6V5F .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-eBZE6f5LhZOT6V5F .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-eBZE6f5LhZOT6V5F .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-eBZE6f5LhZOT6V5F .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-eBZE6f5LhZOT6V5F .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-eBZE6f5LhZOT6V5F .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-eBZE6f5LhZOT6V5F .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-eBZE6f5LhZOT6V5F .marker{fill:#333333;stroke:#333333;}#mermaid-svg-eBZE6f5LhZOT6V5F .marker.cross{stroke:#333333;}#mermaid-svg-eBZE6f5LhZOT6V5F svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-eBZE6f5LhZOT6V5F p{margin:0;}#mermaid-svg-eBZE6f5LhZOT6V5F .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-eBZE6f5LhZOT6V5F text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-eBZE6f5LhZOT6V5F .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-eBZE6f5LhZOT6V5F .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-eBZE6f5LhZOT6V5F .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-eBZE6f5LhZOT6V5F .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-eBZE6f5LhZOT6V5F #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-eBZE6f5LhZOT6V5F .sequenceNumber{fill:white;}#mermaid-svg-eBZE6f5LhZOT6V5F #sequencenumber{fill:#333;}#mermaid-svg-eBZE6f5LhZOT6V5F #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-eBZE6f5LhZOT6V5F .messageText{fill:#333;stroke:none;}#mermaid-svg-eBZE6f5LhZOT6V5F .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-eBZE6f5LhZOT6V5F .labelText,#mermaid-svg-eBZE6f5LhZOT6V5F .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-eBZE6f5LhZOT6V5F .loopText,#mermaid-svg-eBZE6f5LhZOT6V5F .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-eBZE6f5LhZOT6V5F .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-eBZE6f5LhZOT6V5F .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-eBZE6f5LhZOT6V5F .noteText,#mermaid-svg-eBZE6f5LhZOT6V5F .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-eBZE6f5LhZOT6V5F .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-eBZE6f5LhZOT6V5F .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-eBZE6f5LhZOT6V5F .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-eBZE6f5LhZOT6V5F .actorPopupMenu{position:absolute;}#mermaid-svg-eBZE6f5LhZOT6V5F .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-eBZE6f5LhZOT6V5F .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-eBZE6f5LhZOT6V5F .actor-man circle,#mermaid-svg-eBZE6f5LhZOT6V5F line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-eBZE6f5LhZOT6V5F :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 创建 reader/writer pipe Start (stdin=reader, env=CUDA_MPS_*) Write("set_default_device_pinned_mem_limit 0 8192M") Close writer 通过管道发送命令 响应输出 Wait() 完成,out 包含响应 Close reader
go
func (d *Daemon) EchoPipeToControl(command string) (string, error) {
var out bytes.Buffer
reader, writer := io.Pipe() // 创建内存管道
defer writer.Close()
defer reader.Close()
mpsDaemon := exec.Command(mpsControlBin) // 不带 -d,交互模式
mpsDaemon.Env = append(mpsDaemon.Env, d.EnvVars().toSlice()...)
mpsDaemon.Stdin = reader // 从 pipe 读取命令
mpsDaemon.Stdout = &out // 输出捕获到 buffer
if err := mpsDaemon.Start(); err != nil { return "", ... }
// 通过管道写入命令
if _, err := writer.Write([]byte(command)); err != nil { return "", ... }
writer.Close() // 关闭写端,使 mps-control 的 stdin EOF
if err := mpsDaemon.Wait(); err != nil { return "", ... }
return out.String(), nil // 返回命令输出
}
5.6 setComputeMode() --- GPU 计算模式切换
对所有关联 GPU 的 UUID,执行 nvidia-smi -i {uuid} -c {mode} 切换计算模式。MPS 要求 EXCLUSIVE_PROCESS 模式,停止时恢复为 DEFAULT。
5.7 资源限制计算
perDevicePinnedDeviceMemoryLimits()
go
func (m *Daemon) perDevicePinnedDeviceMemoryLimits() map[string]string {
totalMemoryInBytesPerDevice := make(map[string]uint64)
replicasPerDevice := make(map[string]uint64)
// 遍历所有设备实例(一个 GPU 可能有多个副本)
for _, device := range m.Devices() {
index := device.Index
totalMemoryInBytesPerDevice[index] = device.TotalMemory
replicasPerDevice[index] += 1 // 同一 GPU 上的副本数累加
}
limits := make(map[string]string)
for index, totalMemory := range totalMemoryInBytesPerDevice {
if totalMemory == 0 { continue } // 跳过无内存信息的设备
replicas := replicasPerDevice[index]
// 每副本可用内存 = 总内存 / 副本数,转换为 MB
limits[index] = fmt.Sprintf("%vM", totalMemory/replicas/1024/1024)
}
return limits
}
示例:GPU 0 总显存 24GB(=25769803776 bytes),配置 4 副本:
- limit = 25769803776 / 4 / 1024 / 1024 = 6144M
activeThreadPercentage()
go
func (m *Daemon) activeThreadPercentage() string {
if len(m.Devices()) == 0 { return "" }
// 设备实例总数 / GPU 数 = 每 GPU 副本数
replicasPerDevice := len(m.Devices()) / len(m.Devices().GetUUIDs())
return fmt.Sprintf("%d", 100/replicasPerDevice)
}
示例:2 块 GPU,每块 4 副本 → 总设备实例 8 / 2 = 4 → 100/4 = 25%
5.8 EnvVars() --- 环境变量
返回 CUDA_MPS_PIPE_DIRECTORY 和 CUDA_MPS_LOG_DIRECTORY 两个环境变量,需传递给使用 MPS 共享 GPU 的容器。MPS 客户端通过管道目录找到服务端通信。
5.9 setSELinuxContext()
检查 /sys/fs/selinux 是否存在判断 SELinux 是否启用。若启用,调用 selinux.Chcon(path, context, true) 递归设置 SELinux 上下文为 system_u:object_r:container_file_t:s0,确保非特权容器可访问管道目录。
6. MPS Device 设备校验
6.1 文件:mps/device.go
此文件定义了 mpsDevice 类型别名和设备校验逻辑。
go
// mpsDevice 是 rm.Device 的类型别名
type mpsDevice rm.Device
6.2 assertReplicas() --- 副本数校验
go
func (d *mpsDevice) assertReplicas() error {
maxClients := d.maxClients()
if d.Replicas > maxClients {
return fmt.Errorf("%w maximum allowed replicas exceeded: %d > %d",
errInvalidDevice, d.Replicas, maxClients)
}
return nil
}
6.3 maxClients() --- MPS 最大客户端数
go
func (d *mpsDevice) maxClients() int {
if d.isAtLeastVolta() {
return 48 // Volta 及以上架构支持 48 个 MPS 客户端
}
return 16 // 较老架构仅支持 16 个
}
6.4 isAtLeastVolta() --- 架构检测
go
func (d *mpsDevice) isAtLeastVolta() bool {
// 将计算能力格式化为语义版本号
vCc := "v" + strings.TrimPrefix(d.ComputeCapability, "v")
// semver.Compare 比较版本号:>= 7.5 即为 Volta 及以上
return semver.Compare(semver.Canonical(vCc), semver.Canonical("v7.5")) >= 0
}
架构与 MPS 客户端限制:
- Volta (7.5+) 及以上(Turing, Ampere, Hopper)支持 48 个并发 MPS 客户端
- Pascal 及更早架构仅支持 16 个
- 此校验确保配置的副本数不超过硬件限制
#mermaid-svg-0LmoJGCtHnyO05go{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-0LmoJGCtHnyO05go .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-0LmoJGCtHnyO05go .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-0LmoJGCtHnyO05go .error-icon{fill:#552222;}#mermaid-svg-0LmoJGCtHnyO05go .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-0LmoJGCtHnyO05go .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-0LmoJGCtHnyO05go .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-0LmoJGCtHnyO05go .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-0LmoJGCtHnyO05go .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-0LmoJGCtHnyO05go .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-0LmoJGCtHnyO05go .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-0LmoJGCtHnyO05go .marker{fill:#333333;stroke:#333333;}#mermaid-svg-0LmoJGCtHnyO05go .marker.cross{stroke:#333333;}#mermaid-svg-0LmoJGCtHnyO05go svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-0LmoJGCtHnyO05go p{margin:0;}#mermaid-svg-0LmoJGCtHnyO05go .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-0LmoJGCtHnyO05go .cluster-label text{fill:#333;}#mermaid-svg-0LmoJGCtHnyO05go .cluster-label span{color:#333;}#mermaid-svg-0LmoJGCtHnyO05go .cluster-label span p{background-color:transparent;}#mermaid-svg-0LmoJGCtHnyO05go .label text,#mermaid-svg-0LmoJGCtHnyO05go span{fill:#333;color:#333;}#mermaid-svg-0LmoJGCtHnyO05go .node rect,#mermaid-svg-0LmoJGCtHnyO05go .node circle,#mermaid-svg-0LmoJGCtHnyO05go .node ellipse,#mermaid-svg-0LmoJGCtHnyO05go .node polygon,#mermaid-svg-0LmoJGCtHnyO05go .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-0LmoJGCtHnyO05go .rough-node .label text,#mermaid-svg-0LmoJGCtHnyO05go .node .label text,#mermaid-svg-0LmoJGCtHnyO05go .image-shape .label,#mermaid-svg-0LmoJGCtHnyO05go .icon-shape .label{text-anchor:middle;}#mermaid-svg-0LmoJGCtHnyO05go .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-0LmoJGCtHnyO05go .rough-node .label,#mermaid-svg-0LmoJGCtHnyO05go .node .label,#mermaid-svg-0LmoJGCtHnyO05go .image-shape .label,#mermaid-svg-0LmoJGCtHnyO05go .icon-shape .label{text-align:center;}#mermaid-svg-0LmoJGCtHnyO05go .node.clickable{cursor:pointer;}#mermaid-svg-0LmoJGCtHnyO05go .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-0LmoJGCtHnyO05go .arrowheadPath{fill:#333333;}#mermaid-svg-0LmoJGCtHnyO05go .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-0LmoJGCtHnyO05go .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-0LmoJGCtHnyO05go .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-0LmoJGCtHnyO05go .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-0LmoJGCtHnyO05go .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-0LmoJGCtHnyO05go .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-0LmoJGCtHnyO05go .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-0LmoJGCtHnyO05go .cluster text{fill:#333;}#mermaid-svg-0LmoJGCtHnyO05go .cluster span{color:#333;}#mermaid-svg-0LmoJGCtHnyO05go 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-0LmoJGCtHnyO05go .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-0LmoJGCtHnyO05go rect.text{fill:none;stroke-width:0;}#mermaid-svg-0LmoJGCtHnyO05go .icon-shape,#mermaid-svg-0LmoJGCtHnyO05go .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-0LmoJGCtHnyO05go .icon-shape p,#mermaid-svg-0LmoJGCtHnyO05go .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-0LmoJGCtHnyO05go .icon-shape .label rect,#mermaid-svg-0LmoJGCtHnyO05go .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-0LmoJGCtHnyO05go .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-0LmoJGCtHnyO05go .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-0LmoJGCtHnyO05go :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 是
否
是
否
mpsDevice
ComputeCapability >= 7.5?
maxClients = 48
Volta/Turing/Ampere/Hopper
maxClients = 16
Pascal/Maxwell/Kepler
Replicas > maxClients?
返回错误
errInvalidDevice
校验通过
7. MPS Log Tailer 日志跟踪
7.1 文件:mps/log-tailer.go
日志跟踪器使用 tail -f 命令持续读取 MPS 控制进程的日志文件,将输出转发到当前进程的 stdout/stderr。
7.2 数据结构
go
type tailer struct {
filename string // 跟踪的文件路径
cmd *exec.Cmd // tail 命令实例
cancel context.CancelFunc // 用于取消 tail 命令
}
7.3 Start() --- 启动日志跟踪
创建可取消 context,执行 tail -n +1 -f {filename}(从第1行开始、持续跟踪),将 stdout/stderr 转发到当前进程的 os.Stdout/os.Stderr。
7.4 Stop() --- 停止日志跟踪
调用 cancel() 取消 context(tail 进程收到终止信号),然后 cmd.Wait() 等待 tail 进程退出。
7.5 Log Tailer 流程
stdout control.log tail -f process Tailer Daemon stdout control.log tail -f process Tailer Daemon #mermaid-svg-ka70AYXcJhdc3OPA{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-ka70AYXcJhdc3OPA .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-ka70AYXcJhdc3OPA .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-ka70AYXcJhdc3OPA .error-icon{fill:#552222;}#mermaid-svg-ka70AYXcJhdc3OPA .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-ka70AYXcJhdc3OPA .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-ka70AYXcJhdc3OPA .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-ka70AYXcJhdc3OPA .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-ka70AYXcJhdc3OPA .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-ka70AYXcJhdc3OPA .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-ka70AYXcJhdc3OPA .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-ka70AYXcJhdc3OPA .marker{fill:#333333;stroke:#333333;}#mermaid-svg-ka70AYXcJhdc3OPA .marker.cross{stroke:#333333;}#mermaid-svg-ka70AYXcJhdc3OPA svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-ka70AYXcJhdc3OPA p{margin:0;}#mermaid-svg-ka70AYXcJhdc3OPA .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-ka70AYXcJhdc3OPA text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-ka70AYXcJhdc3OPA .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-ka70AYXcJhdc3OPA .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-ka70AYXcJhdc3OPA .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-ka70AYXcJhdc3OPA .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-ka70AYXcJhdc3OPA #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-ka70AYXcJhdc3OPA .sequenceNumber{fill:white;}#mermaid-svg-ka70AYXcJhdc3OPA #sequencenumber{fill:#333;}#mermaid-svg-ka70AYXcJhdc3OPA #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-ka70AYXcJhdc3OPA .messageText{fill:#333;stroke:none;}#mermaid-svg-ka70AYXcJhdc3OPA .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-ka70AYXcJhdc3OPA .labelText,#mermaid-svg-ka70AYXcJhdc3OPA .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-ka70AYXcJhdc3OPA .loopText,#mermaid-svg-ka70AYXcJhdc3OPA .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-ka70AYXcJhdc3OPA .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-ka70AYXcJhdc3OPA .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-ka70AYXcJhdc3OPA .noteText,#mermaid-svg-ka70AYXcJhdc3OPA .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-ka70AYXcJhdc3OPA .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-ka70AYXcJhdc3OPA .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-ka70AYXcJhdc3OPA .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-ka70AYXcJhdc3OPA .actorPopupMenu{position:absolute;}#mermaid-svg-ka70AYXcJhdc3OPA .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-ka70AYXcJhdc3OPA .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-ka70AYXcJhdc3OPA .actor-man circle,#mermaid-svg-ka70AYXcJhdc3OPA line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-ka70AYXcJhdc3OPA :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} newTailer("/mps/Resource/log/control.log") Start() exec.CommandContext(tail -n +1 -f control.log) cmd.Stdout = os.Stdout 持续读取新增内容 新日志行 转发日志 Stop() cancel() → context 取消 进程退出 cmd.Wait() 返回
8. MPS Manager 守护进程管理器
8.1 文件:mps/manager.go
Manager 负责根据配置创建一组 Daemon 实例,每个 Daemon 对应一个共享资源配置。
8.2 接口定义
go
type Manager interface {
Daemons() ([]*Daemon, error)
}
8.3 实现类
go
// 正常 manager:根据配置创建 Daemon
type manager struct {
infolib info.Interface
nvmllib nvml.Interface
devicelib device.Interface
config *spec.Config
}
// null manager:不创建任何 Daemon(非 MPS 共享模式时使用)
type nullManager struct{}
8.4 New() --- 工厂函数
go
func New(infolib info.Interface, nvmllib nvml.Interface, devicelib device.Interface, opts ...Option) (Manager, error) {
m := &manager{
infolib: infolib,
nvmllib: nvmllib,
devicelib: devicelib,
}
for _, opt := range opts {
opt(m) // 应用函数式选项
}
// 检查共享策略:如果不是 MPS,返回 nullManager
if strategy := m.config.Sharing.SharingStrategy(); strategy != spec.SharingStrategyMPS {
klog.InfoS("Sharing strategy is not MPS; skipping MPS manager creation", "strategy", strategy)
return &nullManager{}, nil
}
return m, nil
}
8.5 Daemons() --- 创建 Daemon 集合
#mermaid-svg-7hEP7tECObk5OF2H{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-7hEP7tECObk5OF2H .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-7hEP7tECObk5OF2H .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-7hEP7tECObk5OF2H .error-icon{fill:#552222;}#mermaid-svg-7hEP7tECObk5OF2H .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-7hEP7tECObk5OF2H .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-7hEP7tECObk5OF2H .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-7hEP7tECObk5OF2H .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-7hEP7tECObk5OF2H .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-7hEP7tECObk5OF2H .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-7hEP7tECObk5OF2H .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-7hEP7tECObk5OF2H .marker{fill:#333333;stroke:#333333;}#mermaid-svg-7hEP7tECObk5OF2H .marker.cross{stroke:#333333;}#mermaid-svg-7hEP7tECObk5OF2H svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-7hEP7tECObk5OF2H p{margin:0;}#mermaid-svg-7hEP7tECObk5OF2H .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-7hEP7tECObk5OF2H .cluster-label text{fill:#333;}#mermaid-svg-7hEP7tECObk5OF2H .cluster-label span{color:#333;}#mermaid-svg-7hEP7tECObk5OF2H .cluster-label span p{background-color:transparent;}#mermaid-svg-7hEP7tECObk5OF2H .label text,#mermaid-svg-7hEP7tECObk5OF2H span{fill:#333;color:#333;}#mermaid-svg-7hEP7tECObk5OF2H .node rect,#mermaid-svg-7hEP7tECObk5OF2H .node circle,#mermaid-svg-7hEP7tECObk5OF2H .node ellipse,#mermaid-svg-7hEP7tECObk5OF2H .node polygon,#mermaid-svg-7hEP7tECObk5OF2H .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-7hEP7tECObk5OF2H .rough-node .label text,#mermaid-svg-7hEP7tECObk5OF2H .node .label text,#mermaid-svg-7hEP7tECObk5OF2H .image-shape .label,#mermaid-svg-7hEP7tECObk5OF2H .icon-shape .label{text-anchor:middle;}#mermaid-svg-7hEP7tECObk5OF2H .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-7hEP7tECObk5OF2H .rough-node .label,#mermaid-svg-7hEP7tECObk5OF2H .node .label,#mermaid-svg-7hEP7tECObk5OF2H .image-shape .label,#mermaid-svg-7hEP7tECObk5OF2H .icon-shape .label{text-align:center;}#mermaid-svg-7hEP7tECObk5OF2H .node.clickable{cursor:pointer;}#mermaid-svg-7hEP7tECObk5OF2H .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-7hEP7tECObk5OF2H .arrowheadPath{fill:#333333;}#mermaid-svg-7hEP7tECObk5OF2H .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-7hEP7tECObk5OF2H .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-7hEP7tECObk5OF2H .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-7hEP7tECObk5OF2H .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-7hEP7tECObk5OF2H .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-7hEP7tECObk5OF2H .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-7hEP7tECObk5OF2H .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-7hEP7tECObk5OF2H .cluster text{fill:#333;}#mermaid-svg-7hEP7tECObk5OF2H .cluster span{color:#333;}#mermaid-svg-7hEP7tECObk5OF2H 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-7hEP7tECObk5OF2H .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-7hEP7tECObk5OF2H rect.text{fill:none;stroke-width:0;}#mermaid-svg-7hEP7tECObk5OF2H .icon-shape,#mermaid-svg-7hEP7tECObk5OF2H .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-7hEP7tECObk5OF2H .icon-shape p,#mermaid-svg-7hEP7tECObk5OF2H .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-7hEP7tECObk5OF2H .icon-shape .label rect,#mermaid-svg-7hEP7tECObk5OF2H .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-7hEP7tECObk5OF2H .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-7hEP7tECObk5OF2H .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-7hEP7tECObk5OF2H :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 否
是
是
否
否
是
是
否
manager.Daemons
创建 NVML ResourceManagers
遍历每个 ResourceManager
设备数 > 0 且共享?
跳过
包含 MIG 设备?
警告跳过
assertReplicas 校验
校验通过?
返回错误
创建 NewDaemon
更多 ResourceManager?
返回 daemons 列表
核心逻辑:
rm.NewNVMLResourceManagers()创建资源管理器集合- 跳过无设备的资源
- 跳过非共享资源(设备 ID 无注解)
- MIG 设备不支持 MPS,跳过并警告
(*mpsDevice)(rmDevice).assertReplicas()校验副本数NewDaemon(resourceManager, ContainerRoot)创建 Daemon
8.6 Manager 类图
#mermaid-svg-dwhOJN60F2msUrzP{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-dwhOJN60F2msUrzP .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-dwhOJN60F2msUrzP .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-dwhOJN60F2msUrzP .error-icon{fill:#552222;}#mermaid-svg-dwhOJN60F2msUrzP .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-dwhOJN60F2msUrzP .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-dwhOJN60F2msUrzP .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-dwhOJN60F2msUrzP .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-dwhOJN60F2msUrzP .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-dwhOJN60F2msUrzP .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-dwhOJN60F2msUrzP .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-dwhOJN60F2msUrzP .marker{fill:#333333;stroke:#333333;}#mermaid-svg-dwhOJN60F2msUrzP .marker.cross{stroke:#333333;}#mermaid-svg-dwhOJN60F2msUrzP svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-dwhOJN60F2msUrzP p{margin:0;}#mermaid-svg-dwhOJN60F2msUrzP g.classGroup text{fill:#9370DB;stroke:none;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:10px;}#mermaid-svg-dwhOJN60F2msUrzP g.classGroup text .title{font-weight:bolder;}#mermaid-svg-dwhOJN60F2msUrzP .cluster-label text{fill:#333;}#mermaid-svg-dwhOJN60F2msUrzP .cluster-label span{color:#333;}#mermaid-svg-dwhOJN60F2msUrzP .cluster-label span p{background-color:transparent;}#mermaid-svg-dwhOJN60F2msUrzP .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-dwhOJN60F2msUrzP .cluster text{fill:#333;}#mermaid-svg-dwhOJN60F2msUrzP .cluster span{color:#333;}#mermaid-svg-dwhOJN60F2msUrzP .nodeLabel,#mermaid-svg-dwhOJN60F2msUrzP .edgeLabel{color:#131300;}#mermaid-svg-dwhOJN60F2msUrzP .edgeLabel .label rect{fill:#ECECFF;}#mermaid-svg-dwhOJN60F2msUrzP .label text{fill:#131300;}#mermaid-svg-dwhOJN60F2msUrzP .labelBkg{background:#ECECFF;}#mermaid-svg-dwhOJN60F2msUrzP .edgeLabel .label span{background:#ECECFF;}#mermaid-svg-dwhOJN60F2msUrzP .classTitle{font-weight:bolder;}#mermaid-svg-dwhOJN60F2msUrzP .node rect,#mermaid-svg-dwhOJN60F2msUrzP .node circle,#mermaid-svg-dwhOJN60F2msUrzP .node ellipse,#mermaid-svg-dwhOJN60F2msUrzP .node polygon,#mermaid-svg-dwhOJN60F2msUrzP .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-dwhOJN60F2msUrzP .divider{stroke:#9370DB;stroke-width:1;}#mermaid-svg-dwhOJN60F2msUrzP g.clickable{cursor:pointer;}#mermaid-svg-dwhOJN60F2msUrzP g.classGroup rect{fill:#ECECFF;stroke:#9370DB;}#mermaid-svg-dwhOJN60F2msUrzP g.classGroup line{stroke:#9370DB;stroke-width:1;}#mermaid-svg-dwhOJN60F2msUrzP .classLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.5;}#mermaid-svg-dwhOJN60F2msUrzP .classLabel .label{fill:#9370DB;font-size:10px;}#mermaid-svg-dwhOJN60F2msUrzP .relation{stroke:#333333;stroke-width:1;fill:none;}#mermaid-svg-dwhOJN60F2msUrzP .dashed-line{stroke-dasharray:3;}#mermaid-svg-dwhOJN60F2msUrzP .dotted-line{stroke-dasharray:1 2;}#mermaid-svg-dwhOJN60F2msUrzP #compositionStart,#mermaid-svg-dwhOJN60F2msUrzP .composition{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-dwhOJN60F2msUrzP #compositionEnd,#mermaid-svg-dwhOJN60F2msUrzP .composition{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-dwhOJN60F2msUrzP #dependencyStart,#mermaid-svg-dwhOJN60F2msUrzP .dependency{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-dwhOJN60F2msUrzP #dependencyStart,#mermaid-svg-dwhOJN60F2msUrzP .dependency{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-dwhOJN60F2msUrzP #extensionStart,#mermaid-svg-dwhOJN60F2msUrzP .extension{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-dwhOJN60F2msUrzP #extensionEnd,#mermaid-svg-dwhOJN60F2msUrzP .extension{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-dwhOJN60F2msUrzP #aggregationStart,#mermaid-svg-dwhOJN60F2msUrzP .aggregation{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-dwhOJN60F2msUrzP #aggregationEnd,#mermaid-svg-dwhOJN60F2msUrzP .aggregation{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-dwhOJN60F2msUrzP #lollipopStart,#mermaid-svg-dwhOJN60F2msUrzP .lollipop{fill:#ECECFF!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-dwhOJN60F2msUrzP #lollipopEnd,#mermaid-svg-dwhOJN60F2msUrzP .lollipop{fill:#ECECFF!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-dwhOJN60F2msUrzP .edgeTerminals{font-size:11px;line-height:initial;}#mermaid-svg-dwhOJN60F2msUrzP .classTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-dwhOJN60F2msUrzP .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-dwhOJN60F2msUrzP .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-dwhOJN60F2msUrzP :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} creates
<<interface>>
Manager
+Daemons()(\[\]*Daemon, error)
manager
-infolib info.Interface
-nvmllib nvml.Interface
-devicelib device.Interface
-config *spec.Config
+Daemons()(\[\]*Daemon, error)
nullManager
+Daemons()(\[\]*Daemon, error)
Daemon
-rm rm.ResourceManager
-root Root
-logTailer *tailer
+Start() : error
+Stop() : error
+Devices() : rm.Devices
+EnvVars() : envvars
+AssertHealthy() : error
+EchoPipeToControl(cmd string)(string, error)
9. MPS Options 配置选项
9.1 文件:mps/options.go
采用函数式选项模式 ,Option 是 func(*manager) 类型,WithConfig() 返回设置 config 的闭包。使用方式:mps.NewDaemons(..., mps.WithConfig(config))。设计意图:允许未来添加更多配置选项而不改变函数签名。
10. MPS Root 路径管理
10.1 文件:mps/root.go
Root 类型管理 MPS 相关的目录结构,确保每个资源共享资源有独立的管道和日志空间。
10.2 类型与常量
go
const ContainerRoot = Root("/mps") // 容器内默认根路径
type Root string // Root 是一个字符串类型
10.3 路径计算方法
go
// LogDir: /mps/{resourceName}/log
func (r Root) LogDir(resourceName spec.ResourceName) string {
return r.Path(string(resourceName), "log")
}
// PipeDir: /mps/{resourceName}/pipe
func (r Root) PipeDir(resourceName spec.ResourceName) string {
return r.Path(string(resourceName), "pipe")
}
// ShmDir: /mps/shm (所有资源共享)
func (r Root) ShmDir(resourceName spec.ResourceName) string {
return r.Path("shm")
}
// startedFile: /mps/{resourceName}/.started
func (r Root) startedFile(resourceName spec.ResourceName) string {
return r.Path(string(resourceName), ".started")
}
// Path: 拼接根路径与子路径
func (r Root) Path(parts ...string) string {
pathparts := append([]string{string(r)}, parts...)
return filepath.Join(pathparts...)
}
10.4 目录结构图
/mps/ ← Root (/mps)
├── .ready ← 全局就绪标记文件
├── shm/ ← 共享内存目录(所有资源共享)
│ └── (tmpfs 挂载)
├── nvidia.com_gpu/ ← 资源 nvidia.com/gpu 的目录
│ ├── .started ← 此资源 Daemon 已启动标记
│ ├── pipe/ ← MPS 管道目录
│ │ └── nvidia-cuda-mps-control
│ └── log/ ← MPS 日志目录
│ └── control.log
├── nvidia.com_gpu_shared/ ← 资源 nvidia.com/gpu.shared 的目录
│ ├── .started
│ ├── pipe/
│ └── log/
│ └── control.log
└── ...
#mermaid-svg-6TZlUIZOUnu3I8ol{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-6TZlUIZOUnu3I8ol .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-6TZlUIZOUnu3I8ol .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-6TZlUIZOUnu3I8ol .error-icon{fill:#552222;}#mermaid-svg-6TZlUIZOUnu3I8ol .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-6TZlUIZOUnu3I8ol .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-6TZlUIZOUnu3I8ol .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-6TZlUIZOUnu3I8ol .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-6TZlUIZOUnu3I8ol .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-6TZlUIZOUnu3I8ol .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-6TZlUIZOUnu3I8ol .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-6TZlUIZOUnu3I8ol .marker{fill:#333333;stroke:#333333;}#mermaid-svg-6TZlUIZOUnu3I8ol .marker.cross{stroke:#333333;}#mermaid-svg-6TZlUIZOUnu3I8ol svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-6TZlUIZOUnu3I8ol p{margin:0;}#mermaid-svg-6TZlUIZOUnu3I8ol .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-6TZlUIZOUnu3I8ol .cluster-label text{fill:#333;}#mermaid-svg-6TZlUIZOUnu3I8ol .cluster-label span{color:#333;}#mermaid-svg-6TZlUIZOUnu3I8ol .cluster-label span p{background-color:transparent;}#mermaid-svg-6TZlUIZOUnu3I8ol .label text,#mermaid-svg-6TZlUIZOUnu3I8ol span{fill:#333;color:#333;}#mermaid-svg-6TZlUIZOUnu3I8ol .node rect,#mermaid-svg-6TZlUIZOUnu3I8ol .node circle,#mermaid-svg-6TZlUIZOUnu3I8ol .node ellipse,#mermaid-svg-6TZlUIZOUnu3I8ol .node polygon,#mermaid-svg-6TZlUIZOUnu3I8ol .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-6TZlUIZOUnu3I8ol .rough-node .label text,#mermaid-svg-6TZlUIZOUnu3I8ol .node .label text,#mermaid-svg-6TZlUIZOUnu3I8ol .image-shape .label,#mermaid-svg-6TZlUIZOUnu3I8ol .icon-shape .label{text-anchor:middle;}#mermaid-svg-6TZlUIZOUnu3I8ol .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-6TZlUIZOUnu3I8ol .rough-node .label,#mermaid-svg-6TZlUIZOUnu3I8ol .node .label,#mermaid-svg-6TZlUIZOUnu3I8ol .image-shape .label,#mermaid-svg-6TZlUIZOUnu3I8ol .icon-shape .label{text-align:center;}#mermaid-svg-6TZlUIZOUnu3I8ol .node.clickable{cursor:pointer;}#mermaid-svg-6TZlUIZOUnu3I8ol .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-6TZlUIZOUnu3I8ol .arrowheadPath{fill:#333333;}#mermaid-svg-6TZlUIZOUnu3I8ol .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-6TZlUIZOUnu3I8ol .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-6TZlUIZOUnu3I8ol .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-6TZlUIZOUnu3I8ol .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-6TZlUIZOUnu3I8ol .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-6TZlUIZOUnu3I8ol .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-6TZlUIZOUnu3I8ol .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-6TZlUIZOUnu3I8ol .cluster text{fill:#333;}#mermaid-svg-6TZlUIZOUnu3I8ol .cluster span{color:#333;}#mermaid-svg-6TZlUIZOUnu3I8ol 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-6TZlUIZOUnu3I8ol .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-6TZlUIZOUnu3I8ol rect.text{fill:none;stroke-width:0;}#mermaid-svg-6TZlUIZOUnu3I8ol .icon-shape,#mermaid-svg-6TZlUIZOUnu3I8ol .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-6TZlUIZOUnu3I8ol .icon-shape p,#mermaid-svg-6TZlUIZOUnu3I8ol .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-6TZlUIZOUnu3I8ol .icon-shape .label rect,#mermaid-svg-6TZlUIZOUnu3I8ol .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-6TZlUIZOUnu3I8ol .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-6TZlUIZOUnu3I8ol .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-6TZlUIZOUnu3I8ol :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} /mps (ContainerRoot)
.ready
shm/ (tmpfs)
nvidia.com_gpu/
.started
pipe/
log/control.log
nvidia.com_gpu_shared/
.started
pipe/
log/control.log
11. Mount SHM 共享内存挂载
11.1 文件:mount/mount-shm.go
此文件实现 mount-shm 子命令,为 MPS daemon 创建专用的 tmpfs 共享内存挂载。
11.2 NewCommand() --- 子命令注册
go
func NewCommand() *cli.Command {
return &cli.Command{
Name: "mount-shm",
Usage: "Set up the /dev/shm mount required by the MPS daemon",
Action: mountShm,
}
}
此命令在
main.go中通过c.Commands = []*cli.Command{mount.NewCommand()}注册为子命令,调用方式:mps-control-daemon mount-shm
11.3 mountShm() --- 挂载逻辑
#mermaid-svg-Yf75IWcJAl0UMrXk{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-Yf75IWcJAl0UMrXk .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-Yf75IWcJAl0UMrXk .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-Yf75IWcJAl0UMrXk .error-icon{fill:#552222;}#mermaid-svg-Yf75IWcJAl0UMrXk .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-Yf75IWcJAl0UMrXk .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-Yf75IWcJAl0UMrXk .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-Yf75IWcJAl0UMrXk .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-Yf75IWcJAl0UMrXk .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-Yf75IWcJAl0UMrXk .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-Yf75IWcJAl0UMrXk .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-Yf75IWcJAl0UMrXk .marker{fill:#333333;stroke:#333333;}#mermaid-svg-Yf75IWcJAl0UMrXk .marker.cross{stroke:#333333;}#mermaid-svg-Yf75IWcJAl0UMrXk svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-Yf75IWcJAl0UMrXk p{margin:0;}#mermaid-svg-Yf75IWcJAl0UMrXk .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-Yf75IWcJAl0UMrXk .cluster-label text{fill:#333;}#mermaid-svg-Yf75IWcJAl0UMrXk .cluster-label span{color:#333;}#mermaid-svg-Yf75IWcJAl0UMrXk .cluster-label span p{background-color:transparent;}#mermaid-svg-Yf75IWcJAl0UMrXk .label text,#mermaid-svg-Yf75IWcJAl0UMrXk span{fill:#333;color:#333;}#mermaid-svg-Yf75IWcJAl0UMrXk .node rect,#mermaid-svg-Yf75IWcJAl0UMrXk .node circle,#mermaid-svg-Yf75IWcJAl0UMrXk .node ellipse,#mermaid-svg-Yf75IWcJAl0UMrXk .node polygon,#mermaid-svg-Yf75IWcJAl0UMrXk .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-Yf75IWcJAl0UMrXk .rough-node .label text,#mermaid-svg-Yf75IWcJAl0UMrXk .node .label text,#mermaid-svg-Yf75IWcJAl0UMrXk .image-shape .label,#mermaid-svg-Yf75IWcJAl0UMrXk .icon-shape .label{text-anchor:middle;}#mermaid-svg-Yf75IWcJAl0UMrXk .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-Yf75IWcJAl0UMrXk .rough-node .label,#mermaid-svg-Yf75IWcJAl0UMrXk .node .label,#mermaid-svg-Yf75IWcJAl0UMrXk .image-shape .label,#mermaid-svg-Yf75IWcJAl0UMrXk .icon-shape .label{text-align:center;}#mermaid-svg-Yf75IWcJAl0UMrXk .node.clickable{cursor:pointer;}#mermaid-svg-Yf75IWcJAl0UMrXk .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-Yf75IWcJAl0UMrXk .arrowheadPath{fill:#333333;}#mermaid-svg-Yf75IWcJAl0UMrXk .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-Yf75IWcJAl0UMrXk .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-Yf75IWcJAl0UMrXk .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Yf75IWcJAl0UMrXk .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-Yf75IWcJAl0UMrXk .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Yf75IWcJAl0UMrXk .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-Yf75IWcJAl0UMrXk .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-Yf75IWcJAl0UMrXk .cluster text{fill:#333;}#mermaid-svg-Yf75IWcJAl0UMrXk .cluster span{color:#333;}#mermaid-svg-Yf75IWcJAl0UMrXk 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-Yf75IWcJAl0UMrXk .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-Yf75IWcJAl0UMrXk rect.text{fill:none;stroke-width:0;}#mermaid-svg-Yf75IWcJAl0UMrXk .icon-shape,#mermaid-svg-Yf75IWcJAl0UMrXk .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Yf75IWcJAl0UMrXk .icon-shape p,#mermaid-svg-Yf75IWcJAl0UMrXk .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-Yf75IWcJAl0UMrXk .icon-shape .label rect,#mermaid-svg-Yf75IWcJAl0UMrXk .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Yf75IWcJAl0UMrXk .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-Yf75IWcJAl0UMrXk .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-Yf75IWcJAl0UMrXk :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} mountShm
查找 mount 可执行文件
清理旧挂载点 /mps/shm
创建目录 /mps/shm
计算 tmpfs 大小
mount -t tmpfs -o rw,nosuid,nodev,noexec,relatime,size=XXX shm /mps/shm
完成
使用 k8s.io/mount-utils 库的 mount.New() 创建挂载器,先 CleanupMountPoint 卸载旧挂载,再 MkdirAll 创建目录,最后以 rw,nosuid,nodev,noexec,relatime,size=XXX 选项挂载 tmpfs。
11.4 getDefaultShmSize() --- 智能大小计算
go
func getDefaultShmSize() string {
const fallbackSize = "65536k" // 默认 64MB
// 读取 /proc/meminfo
meminfo, err := os.Open("/proc/meminfo")
if err != nil { return fallbackSize }
defer meminfo.Close()
scanner := bufio.NewScanner(meminfo)
for scanner.Scan() {
line := scanner.Text()
if !strings.HasPrefix(line, "MemTotal:") { continue }
// 解析 "MemTotal: 32812345 kB"
parts := strings.SplitN(
strings.TrimSpace(strings.TrimPrefix(line, "MemTotal:")),
" ", 2,
)
memTotal, err := strconv.Atoi(parts[0])
if err != nil { return fallbackSize }
// 提取单位(kB, MB 等)
var unit string
if len(parts) == 2 {
unit = string(parts[1][0]) // 取第一个字符作为单位
}
// shm size = MemTotal / 2
return fmt.Sprintf("%d%s", memTotal/2, unit)
}
return fallbackSize
}
示例 :系统总内存 128GB → /proc/meminfo 中 MemTotal: 134217728 kB → shm size = 67108864 kB ≈ 64GB
11.5 SHM 挂载流程图
#mermaid-svg-KdU9qhRnj5tj8maC{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-KdU9qhRnj5tj8maC .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-KdU9qhRnj5tj8maC .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-KdU9qhRnj5tj8maC .error-icon{fill:#552222;}#mermaid-svg-KdU9qhRnj5tj8maC .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-KdU9qhRnj5tj8maC .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-KdU9qhRnj5tj8maC .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-KdU9qhRnj5tj8maC .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-KdU9qhRnj5tj8maC .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-KdU9qhRnj5tj8maC .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-KdU9qhRnj5tj8maC .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-KdU9qhRnj5tj8maC .marker{fill:#333333;stroke:#333333;}#mermaid-svg-KdU9qhRnj5tj8maC .marker.cross{stroke:#333333;}#mermaid-svg-KdU9qhRnj5tj8maC svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-KdU9qhRnj5tj8maC p{margin:0;}#mermaid-svg-KdU9qhRnj5tj8maC .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-KdU9qhRnj5tj8maC .cluster-label text{fill:#333;}#mermaid-svg-KdU9qhRnj5tj8maC .cluster-label span{color:#333;}#mermaid-svg-KdU9qhRnj5tj8maC .cluster-label span p{background-color:transparent;}#mermaid-svg-KdU9qhRnj5tj8maC .label text,#mermaid-svg-KdU9qhRnj5tj8maC span{fill:#333;color:#333;}#mermaid-svg-KdU9qhRnj5tj8maC .node rect,#mermaid-svg-KdU9qhRnj5tj8maC .node circle,#mermaid-svg-KdU9qhRnj5tj8maC .node ellipse,#mermaid-svg-KdU9qhRnj5tj8maC .node polygon,#mermaid-svg-KdU9qhRnj5tj8maC .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-KdU9qhRnj5tj8maC .rough-node .label text,#mermaid-svg-KdU9qhRnj5tj8maC .node .label text,#mermaid-svg-KdU9qhRnj5tj8maC .image-shape .label,#mermaid-svg-KdU9qhRnj5tj8maC .icon-shape .label{text-anchor:middle;}#mermaid-svg-KdU9qhRnj5tj8maC .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-KdU9qhRnj5tj8maC .rough-node .label,#mermaid-svg-KdU9qhRnj5tj8maC .node .label,#mermaid-svg-KdU9qhRnj5tj8maC .image-shape .label,#mermaid-svg-KdU9qhRnj5tj8maC .icon-shape .label{text-align:center;}#mermaid-svg-KdU9qhRnj5tj8maC .node.clickable{cursor:pointer;}#mermaid-svg-KdU9qhRnj5tj8maC .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-KdU9qhRnj5tj8maC .arrowheadPath{fill:#333333;}#mermaid-svg-KdU9qhRnj5tj8maC .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-KdU9qhRnj5tj8maC .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-KdU9qhRnj5tj8maC .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-KdU9qhRnj5tj8maC .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-KdU9qhRnj5tj8maC .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-KdU9qhRnj5tj8maC .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-KdU9qhRnj5tj8maC .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-KdU9qhRnj5tj8maC .cluster text{fill:#333;}#mermaid-svg-KdU9qhRnj5tj8maC .cluster span{color:#333;}#mermaid-svg-KdU9qhRnj5tj8maC 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-KdU9qhRnj5tj8maC .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-KdU9qhRnj5tj8maC rect.text{fill:none;stroke-width:0;}#mermaid-svg-KdU9qhRnj5tj8maC .icon-shape,#mermaid-svg-KdU9qhRnj5tj8maC .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-KdU9qhRnj5tj8maC .icon-shape p,#mermaid-svg-KdU9qhRnj5tj8maC .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-KdU9qhRnj5tj8maC .icon-shape .label rect,#mermaid-svg-KdU9qhRnj5tj8maC .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-KdU9qhRnj5tj8maC .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-KdU9qhRnj5tj8maC .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-KdU9qhRnj5tj8maC :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 找到
未找到
mps-control-daemon mount-shm
LookPath mount
CleanupMountPoint(/mps/shm)
MkdirAll(/mps/shm, 0755)
Open /proc/meminfo
读取 MemTotal 行
计算 size = MemTotal/2
使用 fallback: 65536k
mount -t tmpfs -o rw,nosuid,nodev,noexec,relatime,size=XXX shm /mps/shm
完成
12. 全局架构与交互关系
12.1 三个辅助命令协作关系
#mermaid-svg-hbMRRhEAx6z6WeQr{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-hbMRRhEAx6z6WeQr .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-hbMRRhEAx6z6WeQr .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-hbMRRhEAx6z6WeQr .error-icon{fill:#552222;}#mermaid-svg-hbMRRhEAx6z6WeQr .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-hbMRRhEAx6z6WeQr .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-hbMRRhEAx6z6WeQr .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-hbMRRhEAx6z6WeQr .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-hbMRRhEAx6z6WeQr .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-hbMRRhEAx6z6WeQr .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-hbMRRhEAx6z6WeQr .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-hbMRRhEAx6z6WeQr .marker{fill:#333333;stroke:#333333;}#mermaid-svg-hbMRRhEAx6z6WeQr .marker.cross{stroke:#333333;}#mermaid-svg-hbMRRhEAx6z6WeQr svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-hbMRRhEAx6z6WeQr p{margin:0;}#mermaid-svg-hbMRRhEAx6z6WeQr .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-hbMRRhEAx6z6WeQr .cluster-label text{fill:#333;}#mermaid-svg-hbMRRhEAx6z6WeQr .cluster-label span{color:#333;}#mermaid-svg-hbMRRhEAx6z6WeQr .cluster-label span p{background-color:transparent;}#mermaid-svg-hbMRRhEAx6z6WeQr .label text,#mermaid-svg-hbMRRhEAx6z6WeQr span{fill:#333;color:#333;}#mermaid-svg-hbMRRhEAx6z6WeQr .node rect,#mermaid-svg-hbMRRhEAx6z6WeQr .node circle,#mermaid-svg-hbMRRhEAx6z6WeQr .node ellipse,#mermaid-svg-hbMRRhEAx6z6WeQr .node polygon,#mermaid-svg-hbMRRhEAx6z6WeQr .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-hbMRRhEAx6z6WeQr .rough-node .label text,#mermaid-svg-hbMRRhEAx6z6WeQr .node .label text,#mermaid-svg-hbMRRhEAx6z6WeQr .image-shape .label,#mermaid-svg-hbMRRhEAx6z6WeQr .icon-shape .label{text-anchor:middle;}#mermaid-svg-hbMRRhEAx6z6WeQr .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-hbMRRhEAx6z6WeQr .rough-node .label,#mermaid-svg-hbMRRhEAx6z6WeQr .node .label,#mermaid-svg-hbMRRhEAx6z6WeQr .image-shape .label,#mermaid-svg-hbMRRhEAx6z6WeQr .icon-shape .label{text-align:center;}#mermaid-svg-hbMRRhEAx6z6WeQr .node.clickable{cursor:pointer;}#mermaid-svg-hbMRRhEAx6z6WeQr .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-hbMRRhEAx6z6WeQr .arrowheadPath{fill:#333333;}#mermaid-svg-hbMRRhEAx6z6WeQr .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-hbMRRhEAx6z6WeQr .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-hbMRRhEAx6z6WeQr .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-hbMRRhEAx6z6WeQr .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-hbMRRhEAx6z6WeQr .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-hbMRRhEAx6z6WeQr .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-hbMRRhEAx6z6WeQr .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-hbMRRhEAx6z6WeQr .cluster text{fill:#333;}#mermaid-svg-hbMRRhEAx6z6WeQr .cluster span{color:#333;}#mermaid-svg-hbMRRhEAx6z6WeQr 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-hbMRRhEAx6z6WeQr .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-hbMRRhEAx6z6WeQr rect.text{fill:none;stroke-width:0;}#mermaid-svg-hbMRRhEAx6z6WeQr .icon-shape,#mermaid-svg-hbMRRhEAx6z6WeQr .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-hbMRRhEAx6z6WeQr .icon-shape p,#mermaid-svg-hbMRRhEAx6z6WeQr .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-hbMRRhEAx6z6WeQr .icon-shape .label rect,#mermaid-svg-hbMRRhEAx6z6WeQr .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-hbMRRhEAx6z6WeQr .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-hbMRRhEAx6z6WeQr .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-hbMRRhEAx6z6WeQr :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Kubernetes 集群
工作节点
控制面
GPU 硬件
文件系统
Sidecar / DaemonSet
Init Container
SIGHUP
GPU 0
EXCLUSIVE_PROCESS
gpu-feature-discovery
生成 GPU 标签
/etc/kubernetes/.../features.d/gfd
GFD 输出文件
Kubernetes API Server
config-manager
监听标签 → 切换配置
/etc/nvidia-config-manager/
ConfigMap 挂载源目录
/etc/nvidia-device-plugin/config.json
符号链接目标
nvidia-device-plugin
核心设备插件
mps-control-daemon mount-shm
创建 /mps/shm tmpfs
/mps/
MPS 根目录
mps-control-daemon
管理 MPS 守护进程
GPU 1
EXCLUSIVE_PROCESS
ConfigMap
12.2 MPS 共享模式完整时序
GPU 应用 Pod nvidia-device-plugin nvidia-smi nvidia-cuda-mps-control mps-control-daemon Init Container Kubernetes GPU 应用 Pod nvidia-device-plugin nvidia-smi nvidia-cuda-mps-control mps-control-daemon Init Container Kubernetes #mermaid-svg-o3KkPM4KN0p7Iilu{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-o3KkPM4KN0p7Iilu .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-o3KkPM4KN0p7Iilu .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-o3KkPM4KN0p7Iilu .error-icon{fill:#552222;}#mermaid-svg-o3KkPM4KN0p7Iilu .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-o3KkPM4KN0p7Iilu .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-o3KkPM4KN0p7Iilu .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-o3KkPM4KN0p7Iilu .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-o3KkPM4KN0p7Iilu .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-o3KkPM4KN0p7Iilu .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-o3KkPM4KN0p7Iilu .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-o3KkPM4KN0p7Iilu .marker{fill:#333333;stroke:#333333;}#mermaid-svg-o3KkPM4KN0p7Iilu .marker.cross{stroke:#333333;}#mermaid-svg-o3KkPM4KN0p7Iilu svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-o3KkPM4KN0p7Iilu p{margin:0;}#mermaid-svg-o3KkPM4KN0p7Iilu .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-o3KkPM4KN0p7Iilu text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-o3KkPM4KN0p7Iilu .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-o3KkPM4KN0p7Iilu .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-o3KkPM4KN0p7Iilu .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-o3KkPM4KN0p7Iilu .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-o3KkPM4KN0p7Iilu #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-o3KkPM4KN0p7Iilu .sequenceNumber{fill:white;}#mermaid-svg-o3KkPM4KN0p7Iilu #sequencenumber{fill:#333;}#mermaid-svg-o3KkPM4KN0p7Iilu #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-o3KkPM4KN0p7Iilu .messageText{fill:#333;stroke:none;}#mermaid-svg-o3KkPM4KN0p7Iilu .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-o3KkPM4KN0p7Iilu .labelText,#mermaid-svg-o3KkPM4KN0p7Iilu .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-o3KkPM4KN0p7Iilu .loopText,#mermaid-svg-o3KkPM4KN0p7Iilu .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-o3KkPM4KN0p7Iilu .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-o3KkPM4KN0p7Iilu .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-o3KkPM4KN0p7Iilu .noteText,#mermaid-svg-o3KkPM4KN0p7Iilu .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-o3KkPM4KN0p7Iilu .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-o3KkPM4KN0p7Iilu .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-o3KkPM4KN0p7Iilu .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-o3KkPM4KN0p7Iilu .actorPopupMenu{position:absolute;}#mermaid-svg-o3KkPM4KN0p7Iilu .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-o3KkPM4KN0p7Iilu .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-o3KkPM4KN0p7Iilu .actor-man circle,#mermaid-svg-o3KkPM4KN0p7Iilu line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-o3KkPM4KN0p7Iilu :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 启动 Init Container mount-shm (创建 /mps/shm tmpfs) 启动 mps-control-daemon 加载配置,初始化 NVML 创建 ResourceManager nvidia-smi -c EXCLUSIVE_PROCESS GPU 计算模式设置成功 创建 /mps/Resource/pipe 目录 创建 /mps/Resource/log 目录 nvidia-cuda-mps-control -d (启动 MPS 服务) MPS 服务已启动 set_default_device_pinned_mem_limit 0 6144M OK set_default_active_thread_percentage 25 OK 创建 /mps/.ready 启动 log tailer 启动 nvidia-device-plugin 注册设备资源(含 MPS 共享资源) 调度 GPU 应用 Pod 设置 CUDA_MPS_PIPE_DIRECTORY 通过管道连接 MPS 服务 多进程共享 GPU 资源
12.3 Config Manager 配置切换时序
nvidia-device-plugin 进程 Filesystem Main Loop SyncableConfig Informer Kubernetes API 运维人员 nvidia-device-plugin 进程 Filesystem Main Loop SyncableConfig Informer Kubernetes API 运维人员 #mermaid-svg-6QC7MyzxQoffwAzK{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-6QC7MyzxQoffwAzK .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-6QC7MyzxQoffwAzK .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-6QC7MyzxQoffwAzK .error-icon{fill:#552222;}#mermaid-svg-6QC7MyzxQoffwAzK .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-6QC7MyzxQoffwAzK .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-6QC7MyzxQoffwAzK .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-6QC7MyzxQoffwAzK .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-6QC7MyzxQoffwAzK .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-6QC7MyzxQoffwAzK .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-6QC7MyzxQoffwAzK .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-6QC7MyzxQoffwAzK .marker{fill:#333333;stroke:#333333;}#mermaid-svg-6QC7MyzxQoffwAzK .marker.cross{stroke:#333333;}#mermaid-svg-6QC7MyzxQoffwAzK svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-6QC7MyzxQoffwAzK p{margin:0;}#mermaid-svg-6QC7MyzxQoffwAzK .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-6QC7MyzxQoffwAzK text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-6QC7MyzxQoffwAzK .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-6QC7MyzxQoffwAzK .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-6QC7MyzxQoffwAzK .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-6QC7MyzxQoffwAzK .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-6QC7MyzxQoffwAzK #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-6QC7MyzxQoffwAzK .sequenceNumber{fill:white;}#mermaid-svg-6QC7MyzxQoffwAzK #sequencenumber{fill:#333;}#mermaid-svg-6QC7MyzxQoffwAzK #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-6QC7MyzxQoffwAzK .messageText{fill:#333;stroke:none;}#mermaid-svg-6QC7MyzxQoffwAzK .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-6QC7MyzxQoffwAzK .labelText,#mermaid-svg-6QC7MyzxQoffwAzK .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-6QC7MyzxQoffwAzK .loopText,#mermaid-svg-6QC7MyzxQoffwAzK .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-6QC7MyzxQoffwAzK .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-6QC7MyzxQoffwAzK .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-6QC7MyzxQoffwAzK .noteText,#mermaid-svg-6QC7MyzxQoffwAzK .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-6QC7MyzxQoffwAzK .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-6QC7MyzxQoffwAzK .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-6QC7MyzxQoffwAzK .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-6QC7MyzxQoffwAzK .actorPopupMenu{position:absolute;}#mermaid-svg-6QC7MyzxQoffwAzK .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-6QC7MyzxQoffwAzK .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-6QC7MyzxQoffwAzK .actor-man circle,#mermaid-svg-6QC7MyzxQoffwAzK line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-6QC7MyzxQoffwAzK :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} kubectl label node node1 nvidia.com/device-plugin.config=sharing-config Node Update 事件 Set("sharing-config") Get() 返回 "sharing-config" updateConfigName("sharing-config") 读取 /etc/nvidia-config-manager/ 目录 验证 "sharing-config" 文件存在 EvalSymlinks(/etc/nvidia-config-manager/sharing-config) EvalSymlinks(/etc/nvidia-device-plugin/config.json) 比较路径 → 不同 → 需要更新 Remove /etc/nvidia-device-plugin/config.json Symlink → /etc/nvidia-config-manager/sharing-config findPidToSignal("nvidia-device-plugin") syscall.Kill(pid, SIGHUP) 收到 SIGHUP,重新加载配置 使用新的共享配置运行 Get() 阻塞等待下一次变更
12.4 代码依赖关系图
#mermaid-svg-hgsLPxOuKbZ4hCms{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-hgsLPxOuKbZ4hCms .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-hgsLPxOuKbZ4hCms .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-hgsLPxOuKbZ4hCms .error-icon{fill:#552222;}#mermaid-svg-hgsLPxOuKbZ4hCms .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-hgsLPxOuKbZ4hCms .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-hgsLPxOuKbZ4hCms .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-hgsLPxOuKbZ4hCms .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-hgsLPxOuKbZ4hCms .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-hgsLPxOuKbZ4hCms .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-hgsLPxOuKbZ4hCms .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-hgsLPxOuKbZ4hCms .marker{fill:#333333;stroke:#333333;}#mermaid-svg-hgsLPxOuKbZ4hCms .marker.cross{stroke:#333333;}#mermaid-svg-hgsLPxOuKbZ4hCms svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-hgsLPxOuKbZ4hCms p{margin:0;}#mermaid-svg-hgsLPxOuKbZ4hCms .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-hgsLPxOuKbZ4hCms .cluster-label text{fill:#333;}#mermaid-svg-hgsLPxOuKbZ4hCms .cluster-label span{color:#333;}#mermaid-svg-hgsLPxOuKbZ4hCms .cluster-label span p{background-color:transparent;}#mermaid-svg-hgsLPxOuKbZ4hCms .label text,#mermaid-svg-hgsLPxOuKbZ4hCms span{fill:#333;color:#333;}#mermaid-svg-hgsLPxOuKbZ4hCms .node rect,#mermaid-svg-hgsLPxOuKbZ4hCms .node circle,#mermaid-svg-hgsLPxOuKbZ4hCms .node ellipse,#mermaid-svg-hgsLPxOuKbZ4hCms .node polygon,#mermaid-svg-hgsLPxOuKbZ4hCms .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-hgsLPxOuKbZ4hCms .rough-node .label text,#mermaid-svg-hgsLPxOuKbZ4hCms .node .label text,#mermaid-svg-hgsLPxOuKbZ4hCms .image-shape .label,#mermaid-svg-hgsLPxOuKbZ4hCms .icon-shape .label{text-anchor:middle;}#mermaid-svg-hgsLPxOuKbZ4hCms .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-hgsLPxOuKbZ4hCms .rough-node .label,#mermaid-svg-hgsLPxOuKbZ4hCms .node .label,#mermaid-svg-hgsLPxOuKbZ4hCms .image-shape .label,#mermaid-svg-hgsLPxOuKbZ4hCms .icon-shape .label{text-align:center;}#mermaid-svg-hgsLPxOuKbZ4hCms .node.clickable{cursor:pointer;}#mermaid-svg-hgsLPxOuKbZ4hCms .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-hgsLPxOuKbZ4hCms .arrowheadPath{fill:#333333;}#mermaid-svg-hgsLPxOuKbZ4hCms .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-hgsLPxOuKbZ4hCms .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-hgsLPxOuKbZ4hCms .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-hgsLPxOuKbZ4hCms .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-hgsLPxOuKbZ4hCms .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-hgsLPxOuKbZ4hCms .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-hgsLPxOuKbZ4hCms .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-hgsLPxOuKbZ4hCms .cluster text{fill:#333;}#mermaid-svg-hgsLPxOuKbZ4hCms .cluster span{color:#333;}#mermaid-svg-hgsLPxOuKbZ4hCms 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-hgsLPxOuKbZ4hCms .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-hgsLPxOuKbZ4hCms rect.text{fill:none;stroke-width:0;}#mermaid-svg-hgsLPxOuKbZ4hCms .icon-shape,#mermaid-svg-hgsLPxOuKbZ4hCms .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-hgsLPxOuKbZ4hCms .icon-shape p,#mermaid-svg-hgsLPxOuKbZ4hCms .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-hgsLPxOuKbZ4hCms .icon-shape .label rect,#mermaid-svg-hgsLPxOuKbZ4hCms .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-hgsLPxOuKbZ4hCms .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-hgsLPxOuKbZ4hCms .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-hgsLPxOuKbZ4hCms :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} cmd/gpu-feature-discovery
api/config/v1
internal/flags
internal/info
internal/lm
internal/resource
internal/vgpu
internal/watch
go-nvml
go-nvlib
urfave/cli
cmd/config-manager
k8s.io/client-go
procfs
cmd/mps-control-daemon
internal/rm
mps/manager
mount/mount-shm
mount-utils
mps/daemon
mps/root
mps/log-tailer
selinux
mps/device
semver
mps/options
12.5 关键设计模式总结
| 设计模式 | 应用位置 | 说明 |
|---|---|---|
| 函数式选项 | mps/options.go |
WithConfig() 返回闭包配置 manager |
| 空对象模式 | mps/manager.go |
nullManager 在非 MPS 模式下返回空 Daemon 列表 |
| 生产者-消费者 | config-manager/main.go |
SyncableConfig 用 sync.Cond 实现配置变更通知 |
| Informer 模式 | config-manager/main.go |
使用 K8s Informer 监听 Node 变化,避免轮询 |
| 管道通信 | mps/daemon.go |
EchoPipeToControl 通过 io.Pipe 与 MPS 控制进程交互 |
| 信号驱动 | 所有三个命令 | 使用 syscall.Signal 处理 SIGHUP/SIGINT/SIGTERM |
| 类型别名 | mps/device.go |
mpsDevice 是 rm.Device 的别名,附加 MPS 专有方法 |
12.3 信号处理对比
| 命令 | SIGHUP | SIGINT/SIGTERM/SIGQUIT |
|---|---|---|
| GFD | 重新加载配置 + 重启 | 优雅退出 |
| Config Manager | 无显式处理 | 无显式处理 |
| MPS Daemon | 停止所有 daemon → 重启 | 停止所有 daemon → 退出 |
附录:完整文件索引
| 文件路径 | 行数 | 核心职责 |
|---|---|---|
cmd/gpu-feature-discovery/main.go |
~270 | GFD 入口、CLI 定义、标注循环 |
cmd/config-manager/main.go |
~310 | 配置管理入口、Informer、符号链接管理 |
cmd/mps-control-daemon/main.go |
~160 | MPS Daemon 入口、生命周期管理 |
cmd/mps-control-daemon/mps/daemon.go |
~210 | MPS Daemon 实现(启停、控制、限制) |
cmd/mps-control-daemon/mps/device.go |
~45 | MPS 设备校验(副本数限制) |
cmd/mps-control-daemon/mps/log-tailer.go |
~50 | 日志跟踪器 |
cmd/mps-control-daemon/mps/manager.go |
~95 | Daemon 管理器(创建 Daemon 集合) |
cmd/mps-control-daemon/mps/options.go |
~20 | 函数式选项 |
cmd/mps-control-daemon/mps/root.go |
~50 | 路径管理 |
cmd/mps-control-daemon/mount/mount-shm.go |
~90 | tmpfs 共享内存挂载 |
本文档基于 NVIDIA k8s-device-plugin v0.19.3 源码,严格依据代码分析生成。