Node Feature Discovery 0.20.0-devel 源码深度解析 --- 第四卷:CLI入口、工具链与全局架构总结
文档卷次 : 第四卷(共四卷)
覆盖模块 :
cmd/全部入口、pkg/utils/、pkg/features/、pkg/client-nfd/compat/、pkg/kubectl-nfd/、pkg/cpuid/、pkg/version/
目录
- [第十二章 命令行入口层 (cmd/)](#第十二章 命令行入口层 (cmd/))
- [12.1 组件入口架构](#12.1 组件入口架构)
- [12.2 nfd-master 入口](#12.2 nfd-master 入口)
- [12.3 nfd-worker 入口](#12.3 nfd-worker 入口)
- [12.4 nfd-topology-updater 入口](#12.4 nfd-topology-updater 入口)
- [12.5 nfd-gc 入口](#12.5 nfd-gc 入口)
- [12.6 nfd 统一CLI入口](#12.6 nfd 统一CLI入口)
- [12.7 kubectl-nfd 插件入口](#12.7 kubectl-nfd 插件入口)
- [第十三章 工具与基础设施层](#第十三章 工具与基础设施层)
- [13.1 FeatureGate 系统](#13.1 FeatureGate 系统)
- [13.2 NFD Feature 定义](#13.2 NFD Feature 定义)
- [13.3 工具函数 (utils/)](#13.3 工具函数 (utils/))
- [13.4 HostPath 路径管理](#13.4 HostPath 路径管理)
- [13.5 Klog 日志管理](#13.5 Klog 日志管理)
- [13.6 Kubeconfig 工具](#13.6 Kubeconfig 工具)
- [13.7 版本管理](#13.7 版本管理)
- [13.8 CPUID 封装](#13.8 CPUID 封装)
- [13.9 JSON Patch 工具](#13.9 JSON Patch 工具)
- [13.10 内存资源工具](#13.10 内存资源工具)
- [13.11 延迟转储工具](#13.11 延迟转储工具)
- [第十四章 镜像兼容性客户端](#第十四章 镜像兼容性客户端)
- [14.1 ArtifactClient 架构](#14.1 ArtifactClient 架构)
- [14.2 NodeValidator 节点验证器](#14.2 NodeValidator 节点验证器)
- [14.3 验证状态模型](#14.3 验证状态模型)
- [第十五章 kubectl-nfd 插件](#第十五章 kubectl-nfd 插件)
- [15.1 子命令架构](#15.1 子命令架构)
- [15.2 dryrun 子命令](#15.2 dryrun 子命令)
- [15.3 test 子命令](#15.3 test 子命令)
- [15.4 validate 子命令](#15.4 validate 子命令)
- [第十六章 全局架构总结](#第十六章 全局架构总结)
- [16.1 组件交互全景图](#16.1 组件交互全景图)
- [16.2 设计模式矩阵](#16.2 设计模式矩阵)
- [16.3 数据模型全景](#16.3 数据模型全景)
- [16.4 安全设计总结](#16.4 安全设计总结)
- [16.5 可观测性体系](#16.5 可观测性体系)
- [16.6 代码统计](#16.6 代码统计)
第十二章 命令行入口层 (cmd/)
12.1 组件入口架构
#mermaid-svg-UeRSSnxmjdcHwP0o{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-UeRSSnxmjdcHwP0o .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-UeRSSnxmjdcHwP0o .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-UeRSSnxmjdcHwP0o .error-icon{fill:#552222;}#mermaid-svg-UeRSSnxmjdcHwP0o .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-UeRSSnxmjdcHwP0o .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-UeRSSnxmjdcHwP0o .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-UeRSSnxmjdcHwP0o .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-UeRSSnxmjdcHwP0o .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-UeRSSnxmjdcHwP0o .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-UeRSSnxmjdcHwP0o .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-UeRSSnxmjdcHwP0o .marker{fill:#333333;stroke:#333333;}#mermaid-svg-UeRSSnxmjdcHwP0o .marker.cross{stroke:#333333;}#mermaid-svg-UeRSSnxmjdcHwP0o svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-UeRSSnxmjdcHwP0o p{margin:0;}#mermaid-svg-UeRSSnxmjdcHwP0o .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-UeRSSnxmjdcHwP0o .cluster-label text{fill:#333;}#mermaid-svg-UeRSSnxmjdcHwP0o .cluster-label span{color:#333;}#mermaid-svg-UeRSSnxmjdcHwP0o .cluster-label span p{background-color:transparent;}#mermaid-svg-UeRSSnxmjdcHwP0o .label text,#mermaid-svg-UeRSSnxmjdcHwP0o span{fill:#333;color:#333;}#mermaid-svg-UeRSSnxmjdcHwP0o .node rect,#mermaid-svg-UeRSSnxmjdcHwP0o .node circle,#mermaid-svg-UeRSSnxmjdcHwP0o .node ellipse,#mermaid-svg-UeRSSnxmjdcHwP0o .node polygon,#mermaid-svg-UeRSSnxmjdcHwP0o .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-UeRSSnxmjdcHwP0o .rough-node .label text,#mermaid-svg-UeRSSnxmjdcHwP0o .node .label text,#mermaid-svg-UeRSSnxmjdcHwP0o .image-shape .label,#mermaid-svg-UeRSSnxmjdcHwP0o .icon-shape .label{text-anchor:middle;}#mermaid-svg-UeRSSnxmjdcHwP0o .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-UeRSSnxmjdcHwP0o .rough-node .label,#mermaid-svg-UeRSSnxmjdcHwP0o .node .label,#mermaid-svg-UeRSSnxmjdcHwP0o .image-shape .label,#mermaid-svg-UeRSSnxmjdcHwP0o .icon-shape .label{text-align:center;}#mermaid-svg-UeRSSnxmjdcHwP0o .node.clickable{cursor:pointer;}#mermaid-svg-UeRSSnxmjdcHwP0o .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-UeRSSnxmjdcHwP0o .arrowheadPath{fill:#333333;}#mermaid-svg-UeRSSnxmjdcHwP0o .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-UeRSSnxmjdcHwP0o .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-UeRSSnxmjdcHwP0o .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-UeRSSnxmjdcHwP0o .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-UeRSSnxmjdcHwP0o .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-UeRSSnxmjdcHwP0o .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-UeRSSnxmjdcHwP0o .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-UeRSSnxmjdcHwP0o .cluster text{fill:#333;}#mermaid-svg-UeRSSnxmjdcHwP0o .cluster span{color:#333;}#mermaid-svg-UeRSSnxmjdcHwP0o 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-UeRSSnxmjdcHwP0o .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-UeRSSnxmjdcHwP0o rect.text{fill:none;stroke-width:0;}#mermaid-svg-UeRSSnxmjdcHwP0o .icon-shape,#mermaid-svg-UeRSSnxmjdcHwP0o .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-UeRSSnxmjdcHwP0o .icon-shape p,#mermaid-svg-UeRSSnxmjdcHwP0o .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-UeRSSnxmjdcHwP0o .icon-shape .label rect,#mermaid-svg-UeRSSnxmjdcHwP0o .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-UeRSSnxmjdcHwP0o .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-UeRSSnxmjdcHwP0o .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-UeRSSnxmjdcHwP0o :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 核心包
可执行文件
cmd/nfd-master/main.go
Master 进程
cmd/nfd-worker/main.go
Worker 进程
cmd/nfd-topology-updater/main.go
Topology-Updater 进程
cmd/nfd-gc/main.go
GC 进程
cmd/nfd/main.go
统一 CLI
cmd/kubectl-nfd/main.go
kubectl 插件
pkg/nfd-master
pkg/nfd-worker
pkg/nfd-topology-updater
pkg/nfd-gc
pkg/kubectl-nfd
cmd/nfd/subcmd/
compat + export
12.2 nfd-master 入口
入口模式
所有组件入口采用相同的模式:
#mermaid-svg-Cs2ymVdME2h9kPrM{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-Cs2ymVdME2h9kPrM .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-Cs2ymVdME2h9kPrM .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-Cs2ymVdME2h9kPrM .error-icon{fill:#552222;}#mermaid-svg-Cs2ymVdME2h9kPrM .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-Cs2ymVdME2h9kPrM .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-Cs2ymVdME2h9kPrM .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-Cs2ymVdME2h9kPrM .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-Cs2ymVdME2h9kPrM .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-Cs2ymVdME2h9kPrM .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-Cs2ymVdME2h9kPrM .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-Cs2ymVdME2h9kPrM .marker{fill:#333333;stroke:#333333;}#mermaid-svg-Cs2ymVdME2h9kPrM .marker.cross{stroke:#333333;}#mermaid-svg-Cs2ymVdME2h9kPrM svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-Cs2ymVdME2h9kPrM p{margin:0;}#mermaid-svg-Cs2ymVdME2h9kPrM .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-Cs2ymVdME2h9kPrM .cluster-label text{fill:#333;}#mermaid-svg-Cs2ymVdME2h9kPrM .cluster-label span{color:#333;}#mermaid-svg-Cs2ymVdME2h9kPrM .cluster-label span p{background-color:transparent;}#mermaid-svg-Cs2ymVdME2h9kPrM .label text,#mermaid-svg-Cs2ymVdME2h9kPrM span{fill:#333;color:#333;}#mermaid-svg-Cs2ymVdME2h9kPrM .node rect,#mermaid-svg-Cs2ymVdME2h9kPrM .node circle,#mermaid-svg-Cs2ymVdME2h9kPrM .node ellipse,#mermaid-svg-Cs2ymVdME2h9kPrM .node polygon,#mermaid-svg-Cs2ymVdME2h9kPrM .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-Cs2ymVdME2h9kPrM .rough-node .label text,#mermaid-svg-Cs2ymVdME2h9kPrM .node .label text,#mermaid-svg-Cs2ymVdME2h9kPrM .image-shape .label,#mermaid-svg-Cs2ymVdME2h9kPrM .icon-shape .label{text-anchor:middle;}#mermaid-svg-Cs2ymVdME2h9kPrM .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-Cs2ymVdME2h9kPrM .rough-node .label,#mermaid-svg-Cs2ymVdME2h9kPrM .node .label,#mermaid-svg-Cs2ymVdME2h9kPrM .image-shape .label,#mermaid-svg-Cs2ymVdME2h9kPrM .icon-shape .label{text-align:center;}#mermaid-svg-Cs2ymVdME2h9kPrM .node.clickable{cursor:pointer;}#mermaid-svg-Cs2ymVdME2h9kPrM .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-Cs2ymVdME2h9kPrM .arrowheadPath{fill:#333333;}#mermaid-svg-Cs2ymVdME2h9kPrM .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-Cs2ymVdME2h9kPrM .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-Cs2ymVdME2h9kPrM .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Cs2ymVdME2h9kPrM .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-Cs2ymVdME2h9kPrM .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Cs2ymVdME2h9kPrM .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-Cs2ymVdME2h9kPrM .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-Cs2ymVdME2h9kPrM .cluster text{fill:#333;}#mermaid-svg-Cs2ymVdME2h9kPrM .cluster span{color:#333;}#mermaid-svg-Cs2ymVdME2h9kPrM 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-Cs2ymVdME2h9kPrM .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-Cs2ymVdME2h9kPrM rect.text{fill:none;stroke-width:0;}#mermaid-svg-Cs2ymVdME2h9kPrM .icon-shape,#mermaid-svg-Cs2ymVdME2h9kPrM .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Cs2ymVdME2h9kPrM .icon-shape p,#mermaid-svg-Cs2ymVdME2h9kPrM .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-Cs2ymVdME2h9kPrM .icon-shape .label rect,#mermaid-svg-Cs2ymVdME2h9kPrM .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Cs2ymVdME2h9kPrM .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-Cs2ymVdME2h9kPrM .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-Cs2ymVdME2h9kPrM :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} yes
no
yes
no
main
创建 FlagSet
注册 FeatureGates
features.NFDMutableFeatureGate.Add
parseArgs 解析命令行
--version?
打印版本退出
检查版本是否定义
创建核心实例
NewNfd*
instance.Run()
error?
os.Exit(1)
正常退出
nfd-master 命令行参数
go
func initFlags(flagset *flag.FlagSet) (*master.Args, *master.ConfigOverrideArgs) {
args := &master.Args{}
flagset.StringVar(&args.Instance, "instance", "",
"Instance name. Used to separate annotation namespaces for multiple parallel deployments.")
flagset.StringVar(&args.ConfigFile, "config", "/etc/kubernetes/node-feature-discovery/nfd-master.conf",
"Config file to use.")
flagset.StringVar(&args.Kubeconfig, "kubeconfig", "",
"Kubeconfig to use")
flagset.IntVar(&args.Port, "port", 8080,
"Port which metrics and healthz endpoints are served on")
flagset.BoolVar(&args.Prune, "prune", false,
"Prune all NFD related attributes from all nodes of the cluster and exit.")
flagset.StringVar(&args.Options, "options", "",
"Specify config options from command line.")
flagset.BoolVar(&args.EnableLeaderElection, "enable-leader-election", false,
"Enables a leader election.")
// Override 参数
overrides := &master.ConfigOverrideArgs{
LabelWhiteList: &utils.RegexpVal{},
DenyLabelNs: &utils.StringSetVal{},
ExtraLabelNs: &utils.StringSetVal{},
ResyncPeriod: &utils.DurationVal{Duration: time.Duration(1) * time.Hour},
}
// ...
}
参数解析与 Override 机制
go
func parseArgs(flags *flag.FlagSet, osArgs ...string) *master.Args {
args, overrides := initFlags(flags)
// 过滤空参数
filtered := osArgs[:0]
for _, a := range osArgs {
if len(a) > 0 {
filtered = append(filtered, a)
}
}
_ = flags.Parse(filtered)
// 处理 overrides:只有命令行显式设置的才覆盖配置文件
flags.Visit(func(f *flag.Flag) {
switch f.Name {
case "extra-label-ns":
args.Overrides.ExtraLabelNs = overrides.ExtraLabelNs
case "deny-label-ns":
args.Overrides.DenyLabelNs = overrides.DenyLabelNs
case "label-whitelist":
args.Overrides.LabelWhiteList = overrides.LabelWhiteList
case "enable-taints":
args.Overrides.EnableTaints = overrides.EnableTaints
case "no-publish":
args.Overrides.NoPublish = overrides.NoPublish
case "resync-period":
args.Overrides.ResyncPeriod = overrides.ResyncPeriod
case "nfd-api-parallelism":
args.Overrides.NfdApiParallelism = overrides.NfdApiParallelism
case "informer-page-size":
args.Overrides.InformerPageSize = overrides.InformerPageSize
}
})
return args
}
- flags.Visit 模式:只处理用户显式设置的命令行参数,未设置的保持 nil,让配置文件的值生效。这是"命令行覆盖配置文件"的标准实现。
- 空参数过滤:过滤掉空字符串参数,避免某些容器运行时传递空参数导致解析错误。
Master 参数清单
| 参数 | 默认值 | 说明 |
|---|---|---|
-instance |
"" | 实例名称,用于多实例隔离 |
-config |
/etc/kubernetes/node-feature-discovery/nfd-master.conf |
配置文件路径 |
-kubeconfig |
"" | Kubeconfig 路径 |
-port |
8080 | HTTP metrics/healthz 端口 |
-prune |
false | 清理模式:删除所有 NFD 标签后退出 |
-options |
"" | 命令行配置选项 |
-enable-leader-election |
false | 启用 Leader Election |
-extra-label-ns |
"" | 额外允许的标签命名空间 |
-deny-label-ns |
"" | 禁止的标签命名空间 |
-label-whitelist |
"" | 标签白名单正则表达式 |
-enable-taints |
false | 启用污点功能 |
-no-publish |
false | 禁用发布 |
-resync-period |
1h | API Controller resync 周期 |
-nfd-api-parallelism |
10 | 节点更新并发数 |
-informer-page-size |
200 | Informer 分页大小 |
12.3 nfd-worker 入口
Worker 参数清单
| 参数 | 默认值 | 说明 |
|---|---|---|
-config |
/etc/kubernetes/node-feature-discovery/nfd-worker.conf |
配置文件路径 |
-kubeconfig |
"" | Kubeconfig 路径 |
-oneshot |
false | 单次模式 |
-port |
8080 | HTTP metrics/healthz 端口 |
-options |
"" | 命令行配置选项 |
-no-publish |
false | 禁用发布 |
-feature-sources |
"" | 启用的特征源 |
-label-sources |
"" | 启用的标签源 |
-no-publish-features |
"" | 不发布的特征 |
-owner-refs |
"pod,ds" | OwnerReference 来源 |
-no-owner-refs |
false | 禁用 OwnerReference(已废弃) |
Worker Override 机制
go
overrides := &worker.ConfigOverrideArgs{
FeatureSources: &utils.StringSliceVal{},
LabelSources: &utils.StringSliceVal{},
NoPublishFeatures: &utils.StringSliceVal{},
OwnerRefs: &worker.OwnerRefSources{"pod", "ds"}, // 默认 Pod+DS
}
- OwnerRefs 默认值 :
{"pod", "ds"}表示默认同时引用 Pod 和 DaemonSet 作为 Owner,双重保险。 - FeatureSources/LabelSources :支持
all、-source、source三种格式灵活控制启用/禁用。
12.4 nfd-topology-updater 入口
Topology-Updater 参数清单
| 参数 | 默认值 | 说明 |
|---|---|---|
-oneshot |
false | 单次模式 |
-no-publish |
false | 禁用发布 NRT |
-kubeconfig |
"" | Kubeconfig 路径 |
-port |
8080 | HTTP metrics/healthz 端口 |
-sleep-interval |
60s | 扫描间隔 |
-watch-namespace |
"*" | 监控命名空间 |
-kubelet-config-uri |
"" | kubelet 配置 URI |
-api-auth-token-file |
/var/run/secrets/.../token |
API 认证 token |
-podresources-socket |
/var/lib/kubelet/pod-resources/kubelet.sock |
PodResources Socket |
-config |
/etc/kubernetes/node-feature-discovery/nfd-topology-updater.conf |
配置文件 |
-pods-fingerprint |
true | 是否计算 Pod 指纹 |
-kubelet-state-dir |
/var/lib/kubelet |
kubelet 状态目录 |
Kubelet Config URI 自动构建
go
if len(resourcemonitorArgs.KubeletConfigURI) == 0 {
nodeAddress := os.Getenv("NODE_ADDRESS")
if len(nodeAddress) == 0 {
// NODE_ADDRESS 环境变量必须设置
os.Exit(1)
}
if isIPv6(nodeAddress) {
nodeAddress = "[" + nodeAddress + "]" // IPv6 需要方括号
}
resourcemonitorArgs.KubeletConfigURI = fmt.Sprintf("https://%s:%d/configz", nodeAddress, kubeletSecurePort)
}
- NODE_ADDRESS 环境变量:通常由 Kubernetes Downward API 注入。
- IPv6 处理 :IPv6 地址需要用方括号包裹,因为后续要拼接
:port。 - kubeletSecurePort = 10250:kubelet 的安全端口。
12.5 nfd-gc 入口
go
func initFlags(flagset *flag.FlagSet) *nfdgarbagecollector.Args {
args := &nfdgarbagecollector.Args{}
flagset.DurationVar(&args.GCPeriod, "gc-interval", time.Duration(1)*time.Hour,
"interval between cleanup of obsolete api objects")
flagset.StringVar(&args.Kubeconfig, "kubeconfig", "",
"Kubeconfig to use")
flagset.IntVar(&args.Port, "port", 8080,
"Port which metrics and healthz endpoints are served on")
flagset.Int64Var(&args.ListSize, "list-size", 200,
"the pagination size used when listing node features")
klog.InitFlags(flagset)
return args
}
- GC 参数简洁:GC 组件配置最简单,只有 4 个参数。
- GCPeriod = 1 小时:默认每小时执行一次全量垃圾回收。
- ListSize = 200:分页大小,与 Master 的 InformerPageSize 一致。
12.6 nfd 统一CLI入口
架构
#mermaid-svg-bTiI61vOmtM3cO9k{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-bTiI61vOmtM3cO9k .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-bTiI61vOmtM3cO9k .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-bTiI61vOmtM3cO9k .error-icon{fill:#552222;}#mermaid-svg-bTiI61vOmtM3cO9k .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-bTiI61vOmtM3cO9k .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-bTiI61vOmtM3cO9k .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-bTiI61vOmtM3cO9k .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-bTiI61vOmtM3cO9k .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-bTiI61vOmtM3cO9k .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-bTiI61vOmtM3cO9k .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-bTiI61vOmtM3cO9k .marker{fill:#333333;stroke:#333333;}#mermaid-svg-bTiI61vOmtM3cO9k .marker.cross{stroke:#333333;}#mermaid-svg-bTiI61vOmtM3cO9k svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-bTiI61vOmtM3cO9k p{margin:0;}#mermaid-svg-bTiI61vOmtM3cO9k .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-bTiI61vOmtM3cO9k .cluster-label text{fill:#333;}#mermaid-svg-bTiI61vOmtM3cO9k .cluster-label span{color:#333;}#mermaid-svg-bTiI61vOmtM3cO9k .cluster-label span p{background-color:transparent;}#mermaid-svg-bTiI61vOmtM3cO9k .label text,#mermaid-svg-bTiI61vOmtM3cO9k span{fill:#333;color:#333;}#mermaid-svg-bTiI61vOmtM3cO9k .node rect,#mermaid-svg-bTiI61vOmtM3cO9k .node circle,#mermaid-svg-bTiI61vOmtM3cO9k .node ellipse,#mermaid-svg-bTiI61vOmtM3cO9k .node polygon,#mermaid-svg-bTiI61vOmtM3cO9k .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-bTiI61vOmtM3cO9k .rough-node .label text,#mermaid-svg-bTiI61vOmtM3cO9k .node .label text,#mermaid-svg-bTiI61vOmtM3cO9k .image-shape .label,#mermaid-svg-bTiI61vOmtM3cO9k .icon-shape .label{text-anchor:middle;}#mermaid-svg-bTiI61vOmtM3cO9k .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-bTiI61vOmtM3cO9k .rough-node .label,#mermaid-svg-bTiI61vOmtM3cO9k .node .label,#mermaid-svg-bTiI61vOmtM3cO9k .image-shape .label,#mermaid-svg-bTiI61vOmtM3cO9k .icon-shape .label{text-align:center;}#mermaid-svg-bTiI61vOmtM3cO9k .node.clickable{cursor:pointer;}#mermaid-svg-bTiI61vOmtM3cO9k .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-bTiI61vOmtM3cO9k .arrowheadPath{fill:#333333;}#mermaid-svg-bTiI61vOmtM3cO9k .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-bTiI61vOmtM3cO9k .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-bTiI61vOmtM3cO9k .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-bTiI61vOmtM3cO9k .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-bTiI61vOmtM3cO9k .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-bTiI61vOmtM3cO9k .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-bTiI61vOmtM3cO9k .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-bTiI61vOmtM3cO9k .cluster text{fill:#333;}#mermaid-svg-bTiI61vOmtM3cO9k .cluster span{color:#333;}#mermaid-svg-bTiI61vOmtM3cO9k 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-bTiI61vOmtM3cO9k .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-bTiI61vOmtM3cO9k rect.text{fill:none;stroke-width:0;}#mermaid-svg-bTiI61vOmtM3cO9k .icon-shape,#mermaid-svg-bTiI61vOmtM3cO9k .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-bTiI61vOmtM3cO9k .icon-shape p,#mermaid-svg-bTiI61vOmtM3cO9k .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-bTiI61vOmtM3cO9k .icon-shape .label rect,#mermaid-svg-bTiI61vOmtM3cO9k .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-bTiI61vOmtM3cO9k .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-bTiI61vOmtM3cO9k .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-bTiI61vOmtM3cO9k :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} cmd/nfd/main.go
cmd/nfd/subcmd/root.go
RootCmd
compat.CompatCmd
镜像兼容性命令组
export.ExportCmd
导出命令组
validate-node
节点验证
features
导出特征 JSON
labels
导出标签 JSON
go
// cmd/nfd/main.go
func main() {
subcmd.Execute()
}
// cmd/nfd/subcmd/root.go
var RootCmd = &cobra.Command{
Use: "nfd",
Short: "Node Feature Discovery client",
}
func init() {
RootCmd.AddCommand(compat.CompatCmd)
RootCmd.AddCommand(export.ExportCmd)
RootCmd.SilenceUsage = true
}
- Cobra 框架 :使用
spf13/cobra构建 CLI,支持子命令、帮助、参数验证。 - 两个子命令组 :
compat(镜像兼容性)和export(导出特征/标签)。
export features 子命令
go
func NewExportCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "features",
Short: "Export features for given node",
RunE: func(cmd *cobra.Command, args []string) error {
// 1. 获取所有非 Supplemental 源
sources := map[string]source.FeatureSource{}
for k, v := range source.GetAllFeatureSources() {
if ts, ok := v.(source.SupplementalSource); ok && ts.DisableByDefault() {
continue
}
sources[k] = v
}
// 2. 执行发现
for _, s := range sources {
if err := s.Discover(); err != nil {
return err
}
}
// 3. 收集特征并输出 JSON
features := source.GetAllFeatures()
exportedLabels, err := json.MarshalIndent(features, "", " ")
// ...
},
}
cmd.Flags().StringVarP(&outputPath, "path", "p", "", "export to this JSON path")
return cmd
}
- 本地执行 :
nfd export在本地执行特征发现,不连接 K8s API Server。 - JSON 输出:格式化输出到 stdout 或文件。
- 用途 :调试和诊断------用户可以在节点上运行
nfd export features查看发现的所有特征。
export labels 子命令
go
func NewLabelsCmd() *cobra.Command {
// 类似 features 命令,但调用 LabelSource.GetLabels()
// 按 Priority 排序 LabelSource
// 使用 worker.GetFeatureLabels() 生成标签(应用白名单)
// 输出 JSON
}
- 标签生成 :调用
worker.GetFeatureLabels()复用 Worker 的标签生成逻辑。 - 白名单 :默认空正则(匹配所有),可通过
--label-whitelist设置。
12.7 kubectl-nfd 插件入口
go
// cmd/kubectl-nfd/main.go
func main() {
subcmd.Execute()
}
// cmd/kubectl-nfd/subcmd/root.go
var RootCmd = &cobra.Command{
Use: "kubectl-nfd",
Short: "Node Feature Discovery kubectl plugin",
}
- kubectl 插件机制 :命名为
kubectl-nfd的二进制文件可以被kubectl nfd调用。 - 三个子命令 :
dryrun、test、validate。
第十三章 工具与基础设施层
13.1 FeatureGate 系统
模块定位
pkg/utils/featuregate/featuregate.go 实现了一个轻量级 FeatureGate 框架,不依赖 Kubernetes 的 featuregate 包,避免版本耦合。
#mermaid-svg-9qJOhSEeYh448mKR{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-9qJOhSEeYh448mKR .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-9qJOhSEeYh448mKR .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-9qJOhSEeYh448mKR .error-icon{fill:#552222;}#mermaid-svg-9qJOhSEeYh448mKR .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-9qJOhSEeYh448mKR .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-9qJOhSEeYh448mKR .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-9qJOhSEeYh448mKR .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-9qJOhSEeYh448mKR .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-9qJOhSEeYh448mKR .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-9qJOhSEeYh448mKR .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-9qJOhSEeYh448mKR .marker{fill:#333333;stroke:#333333;}#mermaid-svg-9qJOhSEeYh448mKR .marker.cross{stroke:#333333;}#mermaid-svg-9qJOhSEeYh448mKR svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-9qJOhSEeYh448mKR p{margin:0;}#mermaid-svg-9qJOhSEeYh448mKR g.classGroup text{fill:#9370DB;stroke:none;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:10px;}#mermaid-svg-9qJOhSEeYh448mKR g.classGroup text .title{font-weight:bolder;}#mermaid-svg-9qJOhSEeYh448mKR .cluster-label text{fill:#333;}#mermaid-svg-9qJOhSEeYh448mKR .cluster-label span{color:#333;}#mermaid-svg-9qJOhSEeYh448mKR .cluster-label span p{background-color:transparent;}#mermaid-svg-9qJOhSEeYh448mKR .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-9qJOhSEeYh448mKR .cluster text{fill:#333;}#mermaid-svg-9qJOhSEeYh448mKR .cluster span{color:#333;}#mermaid-svg-9qJOhSEeYh448mKR .nodeLabel,#mermaid-svg-9qJOhSEeYh448mKR .edgeLabel{color:#131300;}#mermaid-svg-9qJOhSEeYh448mKR .edgeLabel .label rect{fill:#ECECFF;}#mermaid-svg-9qJOhSEeYh448mKR .label text{fill:#131300;}#mermaid-svg-9qJOhSEeYh448mKR .labelBkg{background:#ECECFF;}#mermaid-svg-9qJOhSEeYh448mKR .edgeLabel .label span{background:#ECECFF;}#mermaid-svg-9qJOhSEeYh448mKR .classTitle{font-weight:bolder;}#mermaid-svg-9qJOhSEeYh448mKR .node rect,#mermaid-svg-9qJOhSEeYh448mKR .node circle,#mermaid-svg-9qJOhSEeYh448mKR .node ellipse,#mermaid-svg-9qJOhSEeYh448mKR .node polygon,#mermaid-svg-9qJOhSEeYh448mKR .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-9qJOhSEeYh448mKR .divider{stroke:#9370DB;stroke-width:1;}#mermaid-svg-9qJOhSEeYh448mKR g.clickable{cursor:pointer;}#mermaid-svg-9qJOhSEeYh448mKR g.classGroup rect{fill:#ECECFF;stroke:#9370DB;}#mermaid-svg-9qJOhSEeYh448mKR g.classGroup line{stroke:#9370DB;stroke-width:1;}#mermaid-svg-9qJOhSEeYh448mKR .classLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.5;}#mermaid-svg-9qJOhSEeYh448mKR .classLabel .label{fill:#9370DB;font-size:10px;}#mermaid-svg-9qJOhSEeYh448mKR .relation{stroke:#333333;stroke-width:1;fill:none;}#mermaid-svg-9qJOhSEeYh448mKR .dashed-line{stroke-dasharray:3;}#mermaid-svg-9qJOhSEeYh448mKR .dotted-line{stroke-dasharray:1 2;}#mermaid-svg-9qJOhSEeYh448mKR #compositionStart,#mermaid-svg-9qJOhSEeYh448mKR .composition{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-9qJOhSEeYh448mKR #compositionEnd,#mermaid-svg-9qJOhSEeYh448mKR .composition{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-9qJOhSEeYh448mKR #dependencyStart,#mermaid-svg-9qJOhSEeYh448mKR .dependency{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-9qJOhSEeYh448mKR #dependencyStart,#mermaid-svg-9qJOhSEeYh448mKR .dependency{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-9qJOhSEeYh448mKR #extensionStart,#mermaid-svg-9qJOhSEeYh448mKR .extension{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-9qJOhSEeYh448mKR #extensionEnd,#mermaid-svg-9qJOhSEeYh448mKR .extension{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-9qJOhSEeYh448mKR #aggregationStart,#mermaid-svg-9qJOhSEeYh448mKR .aggregation{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-9qJOhSEeYh448mKR #aggregationEnd,#mermaid-svg-9qJOhSEeYh448mKR .aggregation{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-9qJOhSEeYh448mKR #lollipopStart,#mermaid-svg-9qJOhSEeYh448mKR .lollipop{fill:#ECECFF!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-9qJOhSEeYh448mKR #lollipopEnd,#mermaid-svg-9qJOhSEeYh448mKR .lollipop{fill:#ECECFF!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-9qJOhSEeYh448mKR .edgeTerminals{font-size:11px;line-height:initial;}#mermaid-svg-9qJOhSEeYh448mKR .classTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-9qJOhSEeYh448mKR .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-9qJOhSEeYh448mKR .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-9qJOhSEeYh448mKR :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} <<interface>>
FeatureGate
+Enabled(key Feature) : bool
+KnownFeatures() : \[\]string
+DeepCopy() : MutableFeatureGate
<<interface>>
MutableFeatureGate
+AddFlag(fs FlagSet)
+Set(value string) : error
+SetFromMap(m map<bool>) : error
+Add(features map<Feature,FeatureSpec>) : error
+GetAll() : map<Feature,FeatureSpec>
+OverrideDefault(name Feature, override bool) : error
FeatureSpec
+bool Default
+bool LockToDefault
+prerelease PreRelease
featureGate
-string featureGateName
-sync.Mutex lock
-atomic.Value known
-atomic.Value enabled
-bool closed
关键设计
go
const (
Alpha = prerelease("ALPHA")
Beta = prerelease("BETA")
GA = prerelease("")
Deprecated = prerelease("DEPRECATED")
)
- 成熟度等级:Alpha → Beta → GA → Deprecated,与 Kubernetes FeatureGate 一致。
- LockToDefault:锁定默认值,不允许通过命令行修改。用于 GA 级特性。
- closed 标志 :
AddFlag()后设为 true,阻止后续Add()调用。
线程安全设计
go
type featureGate struct {
lock sync.Mutex
known atomic.Value // map[Feature]FeatureSpec
enabled atomic.Value // map[Feature]bool
closed bool
}
- atomic.Value 用于读多写少 :
known和enabled使用 atomic.Value 实现 lock-free 读取。 - lock 用于写操作 :
Set、SetFromMap、Add等修改操作持有 mutex。 - closed 标志:防止在 AddFlag 后继续添加新特性。
Set 解析
go
func (f *featureGate) Set(value string) error {
m := make(map[string]bool)
for s := range strings.SplitSeq(value, ",") {
if len(s) == 0 { continue }
arr := strings.SplitN(s, "=", 2)
k := strings.TrimSpace(arr[0])
boolValue, err := strconv.ParseBool(strings.TrimSpace(arr[1]))
if err != nil { return err }
m[k] = boolValue
}
return f.SetFromMap(m)
}
- 格式 :
feature1=true,feature2=false,... - 环境变量 :
NFD_FEATURE_GATES环境变量也可设置。
13.2 NFD Feature 定义
go
var DefaultNFDFeatureGates = map[featuregate.Feature]featuregate.FeatureSpec{
NodeFeatureAPI: {Default: true, PreRelease: featuregate.GA, LockToDefault: true},
DisableAutoPrefix: {Default: false, PreRelease: featuregate.Alpha},
NodeFeatureGroupAPI: {Default: false, PreRelease: featuregate.Alpha},
}
| Feature | 默认 | 成熟度 | 锁定 | 说明 |
|---|---|---|---|---|
| NodeFeatureAPI | true | GA | yes | 使用 CRD API 通信(唯一支持的模式) |
| DisableAutoPrefix | false | Alpha | no | 禁用标签自动前缀 |
| NodeFeatureGroupAPI | false | Alpha | no | 启用 NodeFeatureGroup CRD |
- NodeFeatureAPI:GA 且 LockToDefault=true,意味着无法关闭------CRD API 是唯一通信模式,旧的 gRPC 模式已完全废弃。
- NodeFeatureGroupAPI:Alpha 状态,默认禁用,需要显式启用。
13.3 工具函数 (utils/)
flags.go --- 命令行参数类型
#mermaid-svg-XpVM714Yt1fGeeD9{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-XpVM714Yt1fGeeD9 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-XpVM714Yt1fGeeD9 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-XpVM714Yt1fGeeD9 .error-icon{fill:#552222;}#mermaid-svg-XpVM714Yt1fGeeD9 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-XpVM714Yt1fGeeD9 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-XpVM714Yt1fGeeD9 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-XpVM714Yt1fGeeD9 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-XpVM714Yt1fGeeD9 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-XpVM714Yt1fGeeD9 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-XpVM714Yt1fGeeD9 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-XpVM714Yt1fGeeD9 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-XpVM714Yt1fGeeD9 .marker.cross{stroke:#333333;}#mermaid-svg-XpVM714Yt1fGeeD9 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-XpVM714Yt1fGeeD9 p{margin:0;}#mermaid-svg-XpVM714Yt1fGeeD9 g.classGroup text{fill:#9370DB;stroke:none;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:10px;}#mermaid-svg-XpVM714Yt1fGeeD9 g.classGroup text .title{font-weight:bolder;}#mermaid-svg-XpVM714Yt1fGeeD9 .cluster-label text{fill:#333;}#mermaid-svg-XpVM714Yt1fGeeD9 .cluster-label span{color:#333;}#mermaid-svg-XpVM714Yt1fGeeD9 .cluster-label span p{background-color:transparent;}#mermaid-svg-XpVM714Yt1fGeeD9 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-XpVM714Yt1fGeeD9 .cluster text{fill:#333;}#mermaid-svg-XpVM714Yt1fGeeD9 .cluster span{color:#333;}#mermaid-svg-XpVM714Yt1fGeeD9 .nodeLabel,#mermaid-svg-XpVM714Yt1fGeeD9 .edgeLabel{color:#131300;}#mermaid-svg-XpVM714Yt1fGeeD9 .edgeLabel .label rect{fill:#ECECFF;}#mermaid-svg-XpVM714Yt1fGeeD9 .label text{fill:#131300;}#mermaid-svg-XpVM714Yt1fGeeD9 .labelBkg{background:#ECECFF;}#mermaid-svg-XpVM714Yt1fGeeD9 .edgeLabel .label span{background:#ECECFF;}#mermaid-svg-XpVM714Yt1fGeeD9 .classTitle{font-weight:bolder;}#mermaid-svg-XpVM714Yt1fGeeD9 .node rect,#mermaid-svg-XpVM714Yt1fGeeD9 .node circle,#mermaid-svg-XpVM714Yt1fGeeD9 .node ellipse,#mermaid-svg-XpVM714Yt1fGeeD9 .node polygon,#mermaid-svg-XpVM714Yt1fGeeD9 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-XpVM714Yt1fGeeD9 .divider{stroke:#9370DB;stroke-width:1;}#mermaid-svg-XpVM714Yt1fGeeD9 g.clickable{cursor:pointer;}#mermaid-svg-XpVM714Yt1fGeeD9 g.classGroup rect{fill:#ECECFF;stroke:#9370DB;}#mermaid-svg-XpVM714Yt1fGeeD9 g.classGroup line{stroke:#9370DB;stroke-width:1;}#mermaid-svg-XpVM714Yt1fGeeD9 .classLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.5;}#mermaid-svg-XpVM714Yt1fGeeD9 .classLabel .label{fill:#9370DB;font-size:10px;}#mermaid-svg-XpVM714Yt1fGeeD9 .relation{stroke:#333333;stroke-width:1;fill:none;}#mermaid-svg-XpVM714Yt1fGeeD9 .dashed-line{stroke-dasharray:3;}#mermaid-svg-XpVM714Yt1fGeeD9 .dotted-line{stroke-dasharray:1 2;}#mermaid-svg-XpVM714Yt1fGeeD9 #compositionStart,#mermaid-svg-XpVM714Yt1fGeeD9 .composition{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-XpVM714Yt1fGeeD9 #compositionEnd,#mermaid-svg-XpVM714Yt1fGeeD9 .composition{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-XpVM714Yt1fGeeD9 #dependencyStart,#mermaid-svg-XpVM714Yt1fGeeD9 .dependency{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-XpVM714Yt1fGeeD9 #dependencyStart,#mermaid-svg-XpVM714Yt1fGeeD9 .dependency{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-XpVM714Yt1fGeeD9 #extensionStart,#mermaid-svg-XpVM714Yt1fGeeD9 .extension{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-XpVM714Yt1fGeeD9 #extensionEnd,#mermaid-svg-XpVM714Yt1fGeeD9 .extension{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-XpVM714Yt1fGeeD9 #aggregationStart,#mermaid-svg-XpVM714Yt1fGeeD9 .aggregation{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-XpVM714Yt1fGeeD9 #aggregationEnd,#mermaid-svg-XpVM714Yt1fGeeD9 .aggregation{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-XpVM714Yt1fGeeD9 #lollipopStart,#mermaid-svg-XpVM714Yt1fGeeD9 .lollipop{fill:#ECECFF!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-XpVM714Yt1fGeeD9 #lollipopEnd,#mermaid-svg-XpVM714Yt1fGeeD9 .lollipop{fill:#ECECFF!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-XpVM714Yt1fGeeD9 .edgeTerminals{font-size:11px;line-height:initial;}#mermaid-svg-XpVM714Yt1fGeeD9 .classTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-XpVM714Yt1fGeeD9 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-XpVM714Yt1fGeeD9 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-XpVM714Yt1fGeeD9 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} RegexpVal
+regexp.Regexp
+Set(val string) : error
StringSetVal
-map<string,struct>
+Set(val string) : error
+String() : string
+UnmarshalJSON(data \[\]byte) : error
StringSliceVal
-\[\]string
+Set(val string) : error
+String() : string
DurationVal
+time.Duration
+UnmarshalJSON(data \[\]byte) : error
+Set(val string) : error
KlogFlagVal
-flag.Flag flag
-bool isSetFromCmdLine
+Set(value string) : error
+SetFromConfig(value string) : error
+IsSetFromCmdline() : bool
+IsBoolFlag() : bool
- RegexpVal :封装
regexp.Regexp,实现flag.Value接口。 - StringSetVal :逗号分隔的字符串集合,实现
flag.Value和json.Unmarshaler。 - StringSliceVal:逗号分隔的字符串切片,自动去除空白和空条目。
- DurationVal:时间间隔,支持 JSON 反序列化(数字→纳秒,字符串→ParseDuration)。
- KlogFlagVal:包装 klog flag,区分命令行设置和配置文件设置(命令行优先)。
KlogFlagVal 双通道设置机制
go
func (k *KlogFlagVal) Set(value string) error {
k.isSetFromCmdLine = true // 标记为命令行设置
return k.flag.Value.Set(value)
}
func (k *KlogFlagVal) SetFromConfig(value string) error {
return k.flag.Value.Set(value) // 不标记
}
func (k *KlogFlagVal) IsSetFromCmdline() bool {
return k.isSetFromCmdLine
}
- 双通道 :
Set()用于命令行(标记 isSetFromCmdLine),SetFromConfig()用于配置文件(不标记)。 - 优先级 :
MergeKlogConfiguration()中检查IsSetFromCmdline(),如果命令行已设置则跳过配置文件的值。
kubernetes.go --- K8s 工具函数
go
var nodeName string
func NodeName() string {
if nodeName == "" {
nodeName = os.Getenv("NODE_NAME")
}
return nodeName
}
func GetKubernetesNamespace() string {
// 1. 尝试从 serviceaccount 文件读取
const kubernetesNamespaceFilePath = "/var/run/secrets/kubernetes.io/serviceaccount/namespace"
if _, err := os.Stat(kubernetesNamespaceFilePath); err == nil {
data, err := os.ReadFile(kubernetesNamespaceFilePath)
if err == nil {
return strings.TrimSpace(string(data))
}
}
// 2. 回退到环境变量
return os.Getenv("KUBERNETES_NAMESPACE")
}
func GetKubeconfig(path string) (*restclient.Config, error) {
if path == "" {
return restclient.InClusterConfig() // 集群内配置
}
return clientcmd.BuildConfigFromFlags("", path) // 集群外配置
}
- NodeName 缓存:首次调用读取环境变量并缓存,避免重复系统调用。
- 命名空间获取双路径:优先从 serviceaccount 文件读取(Pod 挂载的投影卷),回退到环境变量。
- Kubeconfig 双模式:空路径→集群内配置(InClusterConfig),有路径→从文件加载。
13.4 HostPath 路径管理
go
var (
pathPrefix = "/"
BootDir = HostDir(pathPrefix + "boot")
EtcDir = HostDir(pathPrefix + "etc")
SysfsDir = HostDir(pathPrefix + "sys")
UsrDir = HostDir(pathPrefix + "usr")
VarDir = HostDir(pathPrefix + "var")
LibDir = HostDir(pathPrefix + "lib")
ProcDir = HostDir(pathPrefix + "proc")
)
type HostDir string
func (d HostDir) Path(elem ...string) string {
return filepath.Join(append([]string{string(d)}, elem...)...)
}
- 集中路径管理:所有主机文件系统路径集中定义,避免分散在各源中的硬编码路径。
- HostDir.Path() :链式路径拼接,如
SysfsDir.Path("bus/node/devices")→/sys/bus/node/devices。 - 可测试性 :测试时可以修改
pathPrefix为 mock 目录。
13.5 Klog 日志管理
go
func InitKlogFlags(flagset *flag.FlagSet) map[string]*utils.KlogFlagVal {
klogFlags := make(map[string]*utils.KlogFlagVal)
flags := flag.NewFlagSet("klog flags", flag.ContinueOnError)
klog.InitFlags(flags)
flags.VisitAll(func(f *flag.Flag) {
name := klogConfigOptName(f.Name) // snake_case → camelCase
klogFlags[name] = utils.NewKlogFlagVal(f)
flagset.Var(klogFlags[name], f.Name, f.Usage)
})
return klogFlags
}
- 代理模式:将 klog 的内部 flag 代理为 KlogFlagVal,注册到主 flagset。
- 命名转换 :
log_dir→logDir,vmodule→vmodule(保持不变),用于配置文件键名。
go
func MergeKlogConfiguration(klogArgs map[string]*utils.KlogFlagVal, klogConfig KlogConfigOpts) error {
for k, a := range klogArgs {
if !a.IsSetFromCmdline() {
v, ok := klogConfig[k]
if !ok {
v = a.DefValue() // 配置文件未指定则使用默认值
}
if err := a.SetFromConfig(v); err != nil {
return err
}
}
// 命令行已设置的跳过(命令行优先)
}
// 检查配置文件中的未知键
for k := range klogConfig {
if _, ok := klogArgs[k]; !ok {
klog.InfoS("unknown logger option in config", "optionName", k)
}
}
return nil
}
- 三级优先级:命令行 > 配置文件 > 默认值。
- 未知键检测:配置文件中的未知 klog 键会记录警告。
13.6 Kubeconfig 工具
kubelet_configz.go --- HTTP 模式
go
func GetKubeletConfiguration(restConfig *rest.Config) (*kubeletconfigv1beta1.KubeletConfiguration, error) {
discoveryClient, err := discovery.NewDiscoveryClientForConfig(restConfig)
// ...
// GET /configz → {"kubeletconfig": {...}}
type configzWrapper struct {
ComponentConfig kubeletconfigv1beta1.KubeletConfiguration `json:"kubeletconfig"`
}
bytes, err := discoveryClient.RESTClient().Get().Do(context.TODO()).Raw()
// ...
configz := configzWrapper{}
json.Unmarshal(bytes, &configz)
return &configz.ComponentConfig, nil
}
func InsecureConfig(host, tokenFile string) (*rest.Config, error) {
token, err := os.ReadFile(tokenFile)
// ...
return &rest.Config{
Host: host,
TLSClientConfig: rest.TLSClientConfig{Insecure: true}, // 跳过 TLS 验证
BearerToken: string(token),
BearerTokenFile: tokenFile,
}, nil
}
- Insecure TLS:kubelet 通常使用自签名证书,因此跳过 TLS 验证。
- Bearer Token:使用 ServiceAccount token 认证。
- configz 端点 :kubelet 的
/configz端点返回当前配置的 JSON 包装。
kubelet_config_file.go --- 文件模式
go
func GetKubeletConfigFromLocalFile(kubeletConfigPath string) (*kubeletconfigv1beta1.KubeletConfiguration, error) {
loader, err := configfiles.NewFsLoader(&utilfs.DefaultFs{}, kubeletConfigPath)
// ...
kc, err := loader.Load(context.Background())
// ...
scheme, _, err := kubeletconfigscheme.NewSchemeAndCodecs()
// ...
kubeletConfig := &kubeletconfigv1beta1.KubeletConfiguration{}
scheme.Convert(kc, kubeletConfig, nil)
return kubeletConfig, nil
}
- 复用 kubelet 内部库 :使用
k8s.io/kubernetes/pkg/kubelet/kubeletconfig/configfiles加载配置文件,与 kubelet 自身的加载逻辑一致。 - Scheme 转换:通过 scheme.Convert 将内部类型转换为 v1beta1 API 类型。
13.7 版本管理
go
const undefinedVersion string = "undefined"
var version = undefinedVersion // 构建时通过 ldflags 设置
func Get() string { return version }
func Undefined() bool { return version == undefinedVersion }
- ldflags 注入 :构建时通过
-ldflags "-X sigs.k8s.io/node-feature-discovery/pkg/version.version=$(git describe --tags --dirty --always --match 'v*')"注入版本号。 - Undefined 检查:运行时检查版本是否被设置,未设置时记录警告。
13.8 CPUID 封装
go
// pkg/cpuid/cpuid_amd64.go
// 仅在 amd64 架构编译,提供 CPUID 指令的 Go 封装
- 与
source/cpu/cpuid_amd64.go功能重叠,但pkg/cpuid是独立包,可被其他组件复用。
13.9 JSON Patch 工具
go
type JsonPatch struct {
Op string `json:"op"`
Path string `json:"path"`
Value string `json:"value,omitempty"`
}
func NewJsonPatch(verb string, jsonpath string, key string, value string) JsonPatch {
return JsonPatch{verb, path.Join(jsonpath, strings.ReplaceAll(key, "/", "~1")), value}
}
- JSON Patch RFC 6902:实现标准的 JSON Patch 操作。
- 路径转义 :将 key 中的
/替换为~1,符合 RFC 6902 的 JSON Pointer 规范。 - 使用场景:Master 组件使用 JSON Patch 更新 Node 对象的标签/注解/扩展资源。
13.10 内存资源工具
go
func GetNumaMemoryResources() (NumaMemoryResources, error) {
// 读取 /sys/bus/node/devices/nodeN/meminfo → 总内存
// 读取 /sys/bus/node/devices/nodeN/hugepages/hugepages-XkB/nr_hugepages → 大页
// 返回: map[numaID]map[ResourceName]int64
}
- NUMA 内存信息来源 :
/sys/bus/node/devices/nodeN/meminfo:每个 NUMA 节点的内存信息/sys/bus/node/devices/nodeN/hugepages/hugepages-XkB/nr_hugepages:每种大页的数量
- 输出 :
NumaMemoryResources=map[int]MemoryResourceInfo,其中MemoryResourceInfo=map[ResourceName]int64
大页资源名转换
go
q, err := resource.ParseQuantity(strings.Replace(split[1], "kB", "Ki", 1))
// ...
name := corev1.ResourceName(resourcehelper.HugePageResourceName(q))
hugepagesBytes[name] = nr * size
- 单位转换 :
kB→Ki(kibibyte),因为 Kubernetes quantity 解析器使用Ki而非kB。 - 大页资源名 :使用
HugePageResourceName()将大小转换为标准的 Kubernetes 大页资源名(如hugepages-2Mi)。 - 字节数 :存储的是字节数(
nr_hugepages × page_size),而非页数。
13.11 延迟转储工具
go
func DelayedDumper(obj interface{}) fmt.Stringer {
return &dumper{obj: obj}
}
func (d *dumper) String() string {
return Dump(d.obj)
}
func Dump(obj interface{}) string {
out, err := yaml.Marshal(obj)
if err != nil {
return fmt.Sprintf("<!!! FAILED TO MARSHAL %T (%v) !!!>\n", obj, err)
}
return string(out)
}
- 延迟序列化 :
DelayedDumper返回一个fmt.Stringer,只在真正需要字符串时(如 klog 输出时)才执行 YAML 序列化。 - 性能优化 :在
klog.V(4).InfoS("...", "data", utils.DelayedDumper(obj))中,如果日志级别不够(如 V(4) 未启用),则不会调用String(),避免不必要的序列化开销。
第十四章 镜像兼容性客户端
14.1 ArtifactClient 架构
#mermaid-svg-g40yA2DNEVg2xRrU{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-g40yA2DNEVg2xRrU .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-g40yA2DNEVg2xRrU .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-g40yA2DNEVg2xRrU .error-icon{fill:#552222;}#mermaid-svg-g40yA2DNEVg2xRrU .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-g40yA2DNEVg2xRrU .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-g40yA2DNEVg2xRrU .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-g40yA2DNEVg2xRrU .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-g40yA2DNEVg2xRrU .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-g40yA2DNEVg2xRrU .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-g40yA2DNEVg2xRrU .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-g40yA2DNEVg2xRrU .marker{fill:#333333;stroke:#333333;}#mermaid-svg-g40yA2DNEVg2xRrU .marker.cross{stroke:#333333;}#mermaid-svg-g40yA2DNEVg2xRrU svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-g40yA2DNEVg2xRrU p{margin:0;}#mermaid-svg-g40yA2DNEVg2xRrU g.classGroup text{fill:#9370DB;stroke:none;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:10px;}#mermaid-svg-g40yA2DNEVg2xRrU g.classGroup text .title{font-weight:bolder;}#mermaid-svg-g40yA2DNEVg2xRrU .cluster-label text{fill:#333;}#mermaid-svg-g40yA2DNEVg2xRrU .cluster-label span{color:#333;}#mermaid-svg-g40yA2DNEVg2xRrU .cluster-label span p{background-color:transparent;}#mermaid-svg-g40yA2DNEVg2xRrU .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-g40yA2DNEVg2xRrU .cluster text{fill:#333;}#mermaid-svg-g40yA2DNEVg2xRrU .cluster span{color:#333;}#mermaid-svg-g40yA2DNEVg2xRrU .nodeLabel,#mermaid-svg-g40yA2DNEVg2xRrU .edgeLabel{color:#131300;}#mermaid-svg-g40yA2DNEVg2xRrU .edgeLabel .label rect{fill:#ECECFF;}#mermaid-svg-g40yA2DNEVg2xRrU .label text{fill:#131300;}#mermaid-svg-g40yA2DNEVg2xRrU .labelBkg{background:#ECECFF;}#mermaid-svg-g40yA2DNEVg2xRrU .edgeLabel .label span{background:#ECECFF;}#mermaid-svg-g40yA2DNEVg2xRrU .classTitle{font-weight:bolder;}#mermaid-svg-g40yA2DNEVg2xRrU .node rect,#mermaid-svg-g40yA2DNEVg2xRrU .node circle,#mermaid-svg-g40yA2DNEVg2xRrU .node ellipse,#mermaid-svg-g40yA2DNEVg2xRrU .node polygon,#mermaid-svg-g40yA2DNEVg2xRrU .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-g40yA2DNEVg2xRrU .divider{stroke:#9370DB;stroke-width:1;}#mermaid-svg-g40yA2DNEVg2xRrU g.clickable{cursor:pointer;}#mermaid-svg-g40yA2DNEVg2xRrU g.classGroup rect{fill:#ECECFF;stroke:#9370DB;}#mermaid-svg-g40yA2DNEVg2xRrU g.classGroup line{stroke:#9370DB;stroke-width:1;}#mermaid-svg-g40yA2DNEVg2xRrU .classLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.5;}#mermaid-svg-g40yA2DNEVg2xRrU .classLabel .label{fill:#9370DB;font-size:10px;}#mermaid-svg-g40yA2DNEVg2xRrU .relation{stroke:#333333;stroke-width:1;fill:none;}#mermaid-svg-g40yA2DNEVg2xRrU .dashed-line{stroke-dasharray:3;}#mermaid-svg-g40yA2DNEVg2xRrU .dotted-line{stroke-dasharray:1 2;}#mermaid-svg-g40yA2DNEVg2xRrU #compositionStart,#mermaid-svg-g40yA2DNEVg2xRrU .composition{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-g40yA2DNEVg2xRrU #compositionEnd,#mermaid-svg-g40yA2DNEVg2xRrU .composition{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-g40yA2DNEVg2xRrU #dependencyStart,#mermaid-svg-g40yA2DNEVg2xRrU .dependency{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-g40yA2DNEVg2xRrU #dependencyStart,#mermaid-svg-g40yA2DNEVg2xRrU .dependency{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-g40yA2DNEVg2xRrU #extensionStart,#mermaid-svg-g40yA2DNEVg2xRrU .extension{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-g40yA2DNEVg2xRrU #extensionEnd,#mermaid-svg-g40yA2DNEVg2xRrU .extension{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-g40yA2DNEVg2xRrU #aggregationStart,#mermaid-svg-g40yA2DNEVg2xRrU .aggregation{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-g40yA2DNEVg2xRrU #aggregationEnd,#mermaid-svg-g40yA2DNEVg2xRrU .aggregation{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-g40yA2DNEVg2xRrU #lollipopStart,#mermaid-svg-g40yA2DNEVg2xRrU .lollipop{fill:#ECECFF!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-g40yA2DNEVg2xRrU #lollipopEnd,#mermaid-svg-g40yA2DNEVg2xRrU .lollipop{fill:#ECECFF!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-g40yA2DNEVg2xRrU .edgeTerminals{font-size:11px;line-height:initial;}#mermaid-svg-g40yA2DNEVg2xRrU .classTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-g40yA2DNEVg2xRrU .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-g40yA2DNEVg2xRrU .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-g40yA2DNEVg2xRrU :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} <<interface>>
ArtifactClient
+FetchCompatibilitySpec(ctx) : Spec, error
Client
+Args Args
+RegistryReference RegReference
+Platform Platform
-auth.Client orasClient
Args
+bool PlainHttp
FetchCompatibilitySpec 流程
ORAS OCI Registry Client ORAS OCI Registry Client #mermaid-svg-p7nnkqd0iudjhlCs{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-p7nnkqd0iudjhlCs .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-p7nnkqd0iudjhlCs .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-p7nnkqd0iudjhlCs .error-icon{fill:#552222;}#mermaid-svg-p7nnkqd0iudjhlCs .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-p7nnkqd0iudjhlCs .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-p7nnkqd0iudjhlCs .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-p7nnkqd0iudjhlCs .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-p7nnkqd0iudjhlCs .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-p7nnkqd0iudjhlCs .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-p7nnkqd0iudjhlCs .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-p7nnkqd0iudjhlCs .marker{fill:#333333;stroke:#333333;}#mermaid-svg-p7nnkqd0iudjhlCs .marker.cross{stroke:#333333;}#mermaid-svg-p7nnkqd0iudjhlCs svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-p7nnkqd0iudjhlCs p{margin:0;}#mermaid-svg-p7nnkqd0iudjhlCs .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-p7nnkqd0iudjhlCs text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-p7nnkqd0iudjhlCs .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-p7nnkqd0iudjhlCs .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-p7nnkqd0iudjhlCs .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-p7nnkqd0iudjhlCs .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-p7nnkqd0iudjhlCs #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-p7nnkqd0iudjhlCs .sequenceNumber{fill:white;}#mermaid-svg-p7nnkqd0iudjhlCs #sequencenumber{fill:#333;}#mermaid-svg-p7nnkqd0iudjhlCs #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-p7nnkqd0iudjhlCs .messageText{fill:#333;stroke:none;}#mermaid-svg-p7nnkqd0iudjhlCs .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-p7nnkqd0iudjhlCs .labelText,#mermaid-svg-p7nnkqd0iudjhlCs .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-p7nnkqd0iudjhlCs .loopText,#mermaid-svg-p7nnkqd0iudjhlCs .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-p7nnkqd0iudjhlCs .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-p7nnkqd0iudjhlCs .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-p7nnkqd0iudjhlCs .noteText,#mermaid-svg-p7nnkqd0iudjhlCs .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-p7nnkqd0iudjhlCs .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-p7nnkqd0iudjhlCs .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-p7nnkqd0iudjhlCs .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-p7nnkqd0iudjhlCs .actorPopupMenu{position:absolute;}#mermaid-svg-p7nnkqd0iudjhlCs .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-p7nnkqd0iudjhlCs .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-p7nnkqd0iudjhlCs .actor-man circle,#mermaid-svg-p7nnkqd0iudjhlCs line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-p7nnkqd0iudjhlCs :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} NewRepository(ref.String()) oras.Resolve(ctx, repo, ref.Reference, opts) 解析镜像引用 target descriptor registry.Referrers(ctx, repo, targetDesc, artifactType) 兼容性 artifact 描述符列表 按创建时间排序(取最新) oras.FetchBytes(artifact manifest) OCI Manifest JSON oras.FetchBytes(spec layer) Spec YAML yaml.Unmarshal → Spec
逐行解析
go
func (c *Client) FetchCompatibilitySpec(ctx context.Context) (*compatv1alpha1.Spec, error) {
// 1. 创建远程仓库客户端
repo, err := remote.NewRepository(c.RegReference.String())
if err != nil {
return nil, err
}
repo.Client = c.orasClient
repo.PlainHTTP = c.Args.PlainHttp
// 2. 解析目标镜像
opts := oras.DefaultResolveOptions
if c.Platform != nil {
opts.TargetPlatform = c.Platform
}
targetDesc, err := oras.Resolve(ctx, repo, c.RegReference.Reference, opts)
if err != nil {
return nil, err
}
// 3. 查找兼容性 artifact(通过 OCI Referrers API)
descs, err := registry.Referrers(ctx, repo, targetDesc, compatv1alpha1.ArtifactType)
if err != nil {
return nil, nil
} else if len(descs) < 1 {
return nil, fmt.Errorf("compatibility artifact not found")
}
// 4. 按创建时间排序,取最新的
slices.SortFunc(descs, func(i, j ocispec.Descriptor) int {
it, _ := time.Parse(time.RFC3339, i.Annotations[ArtifactCreationTimestampKey])
jt, _ := time.Parse(time.RFC3339, j.Annotations[ArtifactCreationTimestampKey])
return it.Compare(jt)
})
artifactDesc := descs[len(descs)-1] // 最新的
// 5. 获取 artifact manifest
_, content, err := oras.FetchBytes(ctx, repo.Manifests(), artifactDesc.Digest.String(), oras.DefaultFetchBytesOptions)
// ...
manifest := ocispec.Manifest{}
json.Unmarshal(content, &manifest)
// 6. 获取 spec layer(兼容性规范内容)
if len(manifest.Layers) < 1 {
return nil, fmt.Errorf("compatibility layer not found")
}
specDesc := manifest.Layers[0]
_, compatSpecRaw, err := oras.FetchBytes(ctx, repo.Blobs(), specDesc.Digest.String(), oras.DefaultFetchBytesOptions)
// ...
// 7. 解析 YAML
compatSpec := compatv1alpha1.Spec{}
yaml.Unmarshal(compatSpecRaw, &compatSpec)
return &compatSpec, nil
}
- OCI Referrers API:使用 OCI Image Format Spec 的 Referrers API 查找关联的 artifact。
- 时间戳排序 :通过
org.opencontainers.image.created注解排序,取最新的兼容性规范。 - Manifest + Layer 架构:兼容性 artifact 是一个 OCI Manifest,其第一个 layer 包含实际的兼容性规范 YAML。
认证选项
go
func WithAuthPassword(username, password string) ArtifactClientOpts {
return &artifactClientOpt{f: func(c *Client) {
c.orasClient = &auth.Client{
Client: retry.DefaultClient,
Cache: auth.NewCache(),
Credential: auth.StaticCredential(c.RegReference.Registry, auth.Credential{
Username: username,
Password: password,
}),
}
}}
}
func WithAuthToken(token string) ArtifactClientOpts {
// 使用 AccessToken 认证
}
func WithAuthDefault() ArtifactClientOpts {
// 使用 auth.DefaultClient(通常匿名)
}
- 三种认证模式:用户名/密码、Token、默认(匿名)。
- 选项模式:使用函数式选项模式,灵活组合认证方式。
14.2 NodeValidator 节点验证器
执行流程
#mermaid-svg-k0MhvZlqdXBV991g{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-k0MhvZlqdXBV991g .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-k0MhvZlqdXBV991g .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-k0MhvZlqdXBV991g .error-icon{fill:#552222;}#mermaid-svg-k0MhvZlqdXBV991g .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-k0MhvZlqdXBV991g .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-k0MhvZlqdXBV991g .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-k0MhvZlqdXBV991g .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-k0MhvZlqdXBV991g .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-k0MhvZlqdXBV991g .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-k0MhvZlqdXBV991g .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-k0MhvZlqdXBV991g .marker{fill:#333333;stroke:#333333;}#mermaid-svg-k0MhvZlqdXBV991g .marker.cross{stroke:#333333;}#mermaid-svg-k0MhvZlqdXBV991g svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-k0MhvZlqdXBV991g p{margin:0;}#mermaid-svg-k0MhvZlqdXBV991g .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-k0MhvZlqdXBV991g .cluster-label text{fill:#333;}#mermaid-svg-k0MhvZlqdXBV991g .cluster-label span{color:#333;}#mermaid-svg-k0MhvZlqdXBV991g .cluster-label span p{background-color:transparent;}#mermaid-svg-k0MhvZlqdXBV991g .label text,#mermaid-svg-k0MhvZlqdXBV991g span{fill:#333;color:#333;}#mermaid-svg-k0MhvZlqdXBV991g .node rect,#mermaid-svg-k0MhvZlqdXBV991g .node circle,#mermaid-svg-k0MhvZlqdXBV991g .node ellipse,#mermaid-svg-k0MhvZlqdXBV991g .node polygon,#mermaid-svg-k0MhvZlqdXBV991g .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-k0MhvZlqdXBV991g .rough-node .label text,#mermaid-svg-k0MhvZlqdXBV991g .node .label text,#mermaid-svg-k0MhvZlqdXBV991g .image-shape .label,#mermaid-svg-k0MhvZlqdXBV991g .icon-shape .label{text-anchor:middle;}#mermaid-svg-k0MhvZlqdXBV991g .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-k0MhvZlqdXBV991g .rough-node .label,#mermaid-svg-k0MhvZlqdXBV991g .node .label,#mermaid-svg-k0MhvZlqdXBV991g .image-shape .label,#mermaid-svg-k0MhvZlqdXBV991g .icon-shape .label{text-align:center;}#mermaid-svg-k0MhvZlqdXBV991g .node.clickable{cursor:pointer;}#mermaid-svg-k0MhvZlqdXBV991g .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-k0MhvZlqdXBV991g .arrowheadPath{fill:#333333;}#mermaid-svg-k0MhvZlqdXBV991g .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-k0MhvZlqdXBV991g .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-k0MhvZlqdXBV991g .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-k0MhvZlqdXBV991g .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-k0MhvZlqdXBV991g .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-k0MhvZlqdXBV991g .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-k0MhvZlqdXBV991g .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-k0MhvZlqdXBV991g .cluster text{fill:#333;}#mermaid-svg-k0MhvZlqdXBV991g .cluster span{color:#333;}#mermaid-svg-k0MhvZlqdXBV991g 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-k0MhvZlqdXBV991g .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-k0MhvZlqdXBV991g rect.text{fill:none;stroke-width:0;}#mermaid-svg-k0MhvZlqdXBV991g .icon-shape,#mermaid-svg-k0MhvZlqdXBV991g .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-k0MhvZlqdXBV991g .icon-shape p,#mermaid-svg-k0MhvZlqdXBV991g .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-k0MhvZlqdXBV991g .icon-shape .label rect,#mermaid-svg-k0MhvZlqdXBV991g .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-k0MhvZlqdXBV991g .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-k0MhvZlqdXBV991g .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-k0MhvZlqdXBV991g :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 不匹配
匹配或无 Tags
完成
完成
Execute
拉取兼容性规范
执行所有 FeatureSource
Discover
GetAllFeatures
遍历每个
Compatibility
Tags 过滤?
跳过
遍历每个
GroupRule
ExecuteGroupRule
evaluateRuleStatus
构建详细状态
插入回引 vars
到 features
添加 CompatibilityStatus
返回结果
逐行解析
go
func (nv *nodeValidator) Execute(ctx context.Context) ([]*CompatibilityStatus, error) {
// 1. 拉取兼容性规范
spec, err := nv.artifactClient.FetchCompatibilitySpec(ctx)
if err != nil {
return nil, fmt.Errorf("failed to fetch compatibility spec: %w", err)
}
// 2. 本地特征发现
for _, s := range nv.sources {
if err := s.Discover(); err != nil {
return nil, fmt.Errorf("error during discovery of source %s: %w", s.Name(), err)
}
}
features := source.GetAllFeatures()
// 3. 执行兼容性规则
compats := []*CompatibilityStatus{}
for _, c := range spec.Compatibilties {
// Tags 过滤
if len(nv.args.Tags) > 0 && !slices.Contains(nv.args.Tags, c.Tag) {
continue
}
compat := newCompatibilityStatus(&c)
for _, r := range c.Rules {
// 使用 GroupRule(只生成 vars,不生成 labels)
ruleOut, err := nodefeaturerule.ExecuteGroupRule(&r, features, false)
if err != nil {
return nil, err
}
// 构建详细状态报告
compat.Rules = append(compat.Rules, nv.evaluateRuleStatus(&r, ruleOut.MatchStatus))
// 回引机制:允许后续规则引用前序规则的输出
features.InsertAttributeFeatures(
nfdv1alpha1.RuleBackrefDomain,
nfdv1alpha1.RuleBackrefFeature,
ruleOut.Vars,
)
}
compats = append(compats, &compat)
}
return compats, nil
}
- failFast=false :验证器使用
failFast=false,评估所有表达式以提供完整的匹配状态报告。 - GroupRule :使用
ExecuteGroupRule而非Execute,因为验证器只关心匹配/不匹配,不需要生成标签/污点。 - 回引机制:与 Master 的规则执行一致,前序规则的 vars 被插入到 features 中供后续规则引用。
14.3 验证状态模型
#mermaid-svg-q3UBk4hlh1m80oD0{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-q3UBk4hlh1m80oD0 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-q3UBk4hlh1m80oD0 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-q3UBk4hlh1m80oD0 .error-icon{fill:#552222;}#mermaid-svg-q3UBk4hlh1m80oD0 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-q3UBk4hlh1m80oD0 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-q3UBk4hlh1m80oD0 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-q3UBk4hlh1m80oD0 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-q3UBk4hlh1m80oD0 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-q3UBk4hlh1m80oD0 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-q3UBk4hlh1m80oD0 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-q3UBk4hlh1m80oD0 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-q3UBk4hlh1m80oD0 .marker.cross{stroke:#333333;}#mermaid-svg-q3UBk4hlh1m80oD0 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-q3UBk4hlh1m80oD0 p{margin:0;}#mermaid-svg-q3UBk4hlh1m80oD0 g.classGroup text{fill:#9370DB;stroke:none;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:10px;}#mermaid-svg-q3UBk4hlh1m80oD0 g.classGroup text .title{font-weight:bolder;}#mermaid-svg-q3UBk4hlh1m80oD0 .cluster-label text{fill:#333;}#mermaid-svg-q3UBk4hlh1m80oD0 .cluster-label span{color:#333;}#mermaid-svg-q3UBk4hlh1m80oD0 .cluster-label span p{background-color:transparent;}#mermaid-svg-q3UBk4hlh1m80oD0 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-q3UBk4hlh1m80oD0 .cluster text{fill:#333;}#mermaid-svg-q3UBk4hlh1m80oD0 .cluster span{color:#333;}#mermaid-svg-q3UBk4hlh1m80oD0 .nodeLabel,#mermaid-svg-q3UBk4hlh1m80oD0 .edgeLabel{color:#131300;}#mermaid-svg-q3UBk4hlh1m80oD0 .edgeLabel .label rect{fill:#ECECFF;}#mermaid-svg-q3UBk4hlh1m80oD0 .label text{fill:#131300;}#mermaid-svg-q3UBk4hlh1m80oD0 .labelBkg{background:#ECECFF;}#mermaid-svg-q3UBk4hlh1m80oD0 .edgeLabel .label span{background:#ECECFF;}#mermaid-svg-q3UBk4hlh1m80oD0 .classTitle{font-weight:bolder;}#mermaid-svg-q3UBk4hlh1m80oD0 .node rect,#mermaid-svg-q3UBk4hlh1m80oD0 .node circle,#mermaid-svg-q3UBk4hlh1m80oD0 .node ellipse,#mermaid-svg-q3UBk4hlh1m80oD0 .node polygon,#mermaid-svg-q3UBk4hlh1m80oD0 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-q3UBk4hlh1m80oD0 .divider{stroke:#9370DB;stroke-width:1;}#mermaid-svg-q3UBk4hlh1m80oD0 g.clickable{cursor:pointer;}#mermaid-svg-q3UBk4hlh1m80oD0 g.classGroup rect{fill:#ECECFF;stroke:#9370DB;}#mermaid-svg-q3UBk4hlh1m80oD0 g.classGroup line{stroke:#9370DB;stroke-width:1;}#mermaid-svg-q3UBk4hlh1m80oD0 .classLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.5;}#mermaid-svg-q3UBk4hlh1m80oD0 .classLabel .label{fill:#9370DB;font-size:10px;}#mermaid-svg-q3UBk4hlh1m80oD0 .relation{stroke:#333333;stroke-width:1;fill:none;}#mermaid-svg-q3UBk4hlh1m80oD0 .dashed-line{stroke-dasharray:3;}#mermaid-svg-q3UBk4hlh1m80oD0 .dotted-line{stroke-dasharray:1 2;}#mermaid-svg-q3UBk4hlh1m80oD0 #compositionStart,#mermaid-svg-q3UBk4hlh1m80oD0 .composition{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-q3UBk4hlh1m80oD0 #compositionEnd,#mermaid-svg-q3UBk4hlh1m80oD0 .composition{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-q3UBk4hlh1m80oD0 #dependencyStart,#mermaid-svg-q3UBk4hlh1m80oD0 .dependency{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-q3UBk4hlh1m80oD0 #dependencyStart,#mermaid-svg-q3UBk4hlh1m80oD0 .dependency{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-q3UBk4hlh1m80oD0 #extensionStart,#mermaid-svg-q3UBk4hlh1m80oD0 .extension{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-q3UBk4hlh1m80oD0 #extensionEnd,#mermaid-svg-q3UBk4hlh1m80oD0 .extension{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-q3UBk4hlh1m80oD0 #aggregationStart,#mermaid-svg-q3UBk4hlh1m80oD0 .aggregation{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-q3UBk4hlh1m80oD0 #aggregationEnd,#mermaid-svg-q3UBk4hlh1m80oD0 .aggregation{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-q3UBk4hlh1m80oD0 #lollipopStart,#mermaid-svg-q3UBk4hlh1m80oD0 .lollipop{fill:#ECECFF!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-q3UBk4hlh1m80oD0 #lollipopEnd,#mermaid-svg-q3UBk4hlh1m80oD0 .lollipop{fill:#ECECFF!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-q3UBk4hlh1m80oD0 .edgeTerminals{font-size:11px;line-height:initial;}#mermaid-svg-q3UBk4hlh1m80oD0 .classTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-q3UBk4hlh1m80oD0 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-q3UBk4hlh1m80oD0 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-q3UBk4hlh1m80oD0 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} CompatibilityStatus
+string Tag
+int Weight
+string Description
+\[\]ProcessedRuleStatus Rules
ProcessedRuleStatus
+string Name
+bool IsMatch
+\[\]MatchedExpression MatchedExpressions
+\[\]MatchAnyElem MatchedAny
MatchedExpression
+string Feature
+string Name
+MatchExpression Expression
+MatcherType MatcherType
+bool IsMatch
MatchAnyElem
+\[\]MatchedExpression MatchedExpressions
<<enumeration>>
MatcherType
MatchExpressionType
MatchNameType
- 详细状态:每个表达式的匹配结果(IsMatch)都被记录,用于生成用户友好的表格输出。
- MatcherType 区分 :
MatchExpressionType(按值匹配)和MatchNameType(按名称匹配)区分展示。
第十五章 kubectl-nfd 插件
15.1 子命令架构
#mermaid-svg-7vXjZh1qZ0L0aewU{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-7vXjZh1qZ0L0aewU .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-7vXjZh1qZ0L0aewU .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-7vXjZh1qZ0L0aewU .error-icon{fill:#552222;}#mermaid-svg-7vXjZh1qZ0L0aewU .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-7vXjZh1qZ0L0aewU .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-7vXjZh1qZ0L0aewU .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-7vXjZh1qZ0L0aewU .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-7vXjZh1qZ0L0aewU .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-7vXjZh1qZ0L0aewU .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-7vXjZh1qZ0L0aewU .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-7vXjZh1qZ0L0aewU .marker{fill:#333333;stroke:#333333;}#mermaid-svg-7vXjZh1qZ0L0aewU .marker.cross{stroke:#333333;}#mermaid-svg-7vXjZh1qZ0L0aewU svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-7vXjZh1qZ0L0aewU p{margin:0;}#mermaid-svg-7vXjZh1qZ0L0aewU .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-7vXjZh1qZ0L0aewU .cluster-label text{fill:#333;}#mermaid-svg-7vXjZh1qZ0L0aewU .cluster-label span{color:#333;}#mermaid-svg-7vXjZh1qZ0L0aewU .cluster-label span p{background-color:transparent;}#mermaid-svg-7vXjZh1qZ0L0aewU .label text,#mermaid-svg-7vXjZh1qZ0L0aewU span{fill:#333;color:#333;}#mermaid-svg-7vXjZh1qZ0L0aewU .node rect,#mermaid-svg-7vXjZh1qZ0L0aewU .node circle,#mermaid-svg-7vXjZh1qZ0L0aewU .node ellipse,#mermaid-svg-7vXjZh1qZ0L0aewU .node polygon,#mermaid-svg-7vXjZh1qZ0L0aewU .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-7vXjZh1qZ0L0aewU .rough-node .label text,#mermaid-svg-7vXjZh1qZ0L0aewU .node .label text,#mermaid-svg-7vXjZh1qZ0L0aewU .image-shape .label,#mermaid-svg-7vXjZh1qZ0L0aewU .icon-shape .label{text-anchor:middle;}#mermaid-svg-7vXjZh1qZ0L0aewU .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-7vXjZh1qZ0L0aewU .rough-node .label,#mermaid-svg-7vXjZh1qZ0L0aewU .node .label,#mermaid-svg-7vXjZh1qZ0L0aewU .image-shape .label,#mermaid-svg-7vXjZh1qZ0L0aewU .icon-shape .label{text-align:center;}#mermaid-svg-7vXjZh1qZ0L0aewU .node.clickable{cursor:pointer;}#mermaid-svg-7vXjZh1qZ0L0aewU .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-7vXjZh1qZ0L0aewU .arrowheadPath{fill:#333333;}#mermaid-svg-7vXjZh1qZ0L0aewU .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-7vXjZh1qZ0L0aewU .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-7vXjZh1qZ0L0aewU .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-7vXjZh1qZ0L0aewU .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-7vXjZh1qZ0L0aewU .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-7vXjZh1qZ0L0aewU .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-7vXjZh1qZ0L0aewU .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-7vXjZh1qZ0L0aewU .cluster text{fill:#333;}#mermaid-svg-7vXjZh1qZ0L0aewU .cluster span{color:#333;}#mermaid-svg-7vXjZh1qZ0L0aewU 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-7vXjZh1qZ0L0aewU .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-7vXjZh1qZ0L0aewU rect.text{fill:none;stroke-width:0;}#mermaid-svg-7vXjZh1qZ0L0aewU .icon-shape,#mermaid-svg-7vXjZh1qZ0L0aewU .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-7vXjZh1qZ0L0aewU .icon-shape p,#mermaid-svg-7vXjZh1qZ0L0aewU .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-7vXjZh1qZ0L0aewU .icon-shape .label rect,#mermaid-svg-7vXjZh1qZ0L0aewU .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-7vXjZh1qZ0L0aewU .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-7vXjZh1qZ0L0aewU .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-7vXjZh1qZ0L0aewU :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} RootCmd
kubectl-nfd
dryrun
规则试运行
test
特征测试
validate
规则验证
15.2 dryrun 子命令
go
// pkg/kubectl-nfd/dryrun.go
// dryrun 允许用户在本地测试 NodeFeatureRule 规则
// 不实际应用到集群,只显示规则匹配结果
- 用途:在将 NodeFeatureRule 部署到集群之前,先在本地验证规则逻辑。
- 输入 :规则文件(YAML)+ 特征数据(JSON,可通过
nfd export features生成)。 - 输出:规则匹配结果,包括生成的标签/注解/扩展资源/污点。
15.3 test 子命令
go
// pkg/kubectl-nfd/test.go
// test 对 NodeFeature 和 NodeFeatureRule 对象进行结构化测试
- 用途:测试 NodeFeature/NodeFeatureRule CR 的有效性。
- 验证内容:CR 结构、字段格式、匹配逻辑。
15.4 validate 子命令
go
// pkg/kubectl-nfd/validate.go
// validate 验证 NodeFeatureRule 规则的语法和语义
- 用途 :调用
pkg/apis/nfd/validate包验证规则。 - 验证项目:标签键值格式、命名空间策略、模板语法、特征名格式。
第十六章 全局架构总结
16.1 组件交互全景图
#mermaid-svg-7UycdPNPqqrACa9h{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-7UycdPNPqqrACa9h .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-7UycdPNPqqrACa9h .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-7UycdPNPqqrACa9h .error-icon{fill:#552222;}#mermaid-svg-7UycdPNPqqrACa9h .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-7UycdPNPqqrACa9h .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-7UycdPNPqqrACa9h .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-7UycdPNPqqrACa9h .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-7UycdPNPqqrACa9h .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-7UycdPNPqqrACa9h .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-7UycdPNPqqrACa9h .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-7UycdPNPqqrACa9h .marker{fill:#333333;stroke:#333333;}#mermaid-svg-7UycdPNPqqrACa9h .marker.cross{stroke:#333333;}#mermaid-svg-7UycdPNPqqrACa9h svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-7UycdPNPqqrACa9h p{margin:0;}#mermaid-svg-7UycdPNPqqrACa9h .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-7UycdPNPqqrACa9h .cluster-label text{fill:#333;}#mermaid-svg-7UycdPNPqqrACa9h .cluster-label span{color:#333;}#mermaid-svg-7UycdPNPqqrACa9h .cluster-label span p{background-color:transparent;}#mermaid-svg-7UycdPNPqqrACa9h .label text,#mermaid-svg-7UycdPNPqqrACa9h span{fill:#333;color:#333;}#mermaid-svg-7UycdPNPqqrACa9h .node rect,#mermaid-svg-7UycdPNPqqrACa9h .node circle,#mermaid-svg-7UycdPNPqqrACa9h .node ellipse,#mermaid-svg-7UycdPNPqqrACa9h .node polygon,#mermaid-svg-7UycdPNPqqrACa9h .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-7UycdPNPqqrACa9h .rough-node .label text,#mermaid-svg-7UycdPNPqqrACa9h .node .label text,#mermaid-svg-7UycdPNPqqrACa9h .image-shape .label,#mermaid-svg-7UycdPNPqqrACa9h .icon-shape .label{text-anchor:middle;}#mermaid-svg-7UycdPNPqqrACa9h .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-7UycdPNPqqrACa9h .rough-node .label,#mermaid-svg-7UycdPNPqqrACa9h .node .label,#mermaid-svg-7UycdPNPqqrACa9h .image-shape .label,#mermaid-svg-7UycdPNPqqrACa9h .icon-shape .label{text-align:center;}#mermaid-svg-7UycdPNPqqrACa9h .node.clickable{cursor:pointer;}#mermaid-svg-7UycdPNPqqrACa9h .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-7UycdPNPqqrACa9h .arrowheadPath{fill:#333333;}#mermaid-svg-7UycdPNPqqrACa9h .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-7UycdPNPqqrACa9h .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-7UycdPNPqqrACa9h .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-7UycdPNPqqrACa9h .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-7UycdPNPqqrACa9h .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-7UycdPNPqqrACa9h .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-7UycdPNPqqrACa9h .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-7UycdPNPqqrACa9h .cluster text{fill:#333;}#mermaid-svg-7UycdPNPqqrACa9h .cluster span{color:#333;}#mermaid-svg-7UycdPNPqqrACa9h 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-7UycdPNPqqrACa9h .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-7UycdPNPqqrACa9h rect.text{fill:none;stroke-width:0;}#mermaid-svg-7UycdPNPqqrACa9h .icon-shape,#mermaid-svg-7UycdPNPqqrACa9h .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-7UycdPNPqqrACa9h .icon-shape p,#mermaid-svg-7UycdPNPqqrACa9h .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-7UycdPNPqqrACa9h .icon-shape .label rect,#mermaid-svg-7UycdPNPqqrACa9h .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-7UycdPNPqqrACa9h .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-7UycdPNPqqrACa9h .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-7UycdPNPqqrACa9h :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Local System
Kubernetes Cluster
K8s Objects
CRD Objects
NFD Components
创建/更新
创建/更新
监听
监听
监听
监听
更新 Labels/Taints/ER
更新 Labels/Taints/ER
创建/更新
创建/更新
监听 Node 删除
清理过期 NF
清理过期 NRT
K8s API Server
nfd-worker
DaemonSet
每节点一个
nfd-worker
DaemonSet
nfd-master
Deployment
1+副本
nfd-topology-updater
DaemonSet
nfd-topology-updater
DaemonSet
nfd-gc
Deployment
1副本
NodeFeature CR
node1
NodeFeature CR
node2
NodeFeatureRule CR
集群级
NodeFeatureGroup CR
集群级
NodeResourceTopology
node1
NodeResourceTopology
node2
Node node1
Labels/Taints/ER
Node node2
Labels/Taints/ER
kubelet
PodResources API
kubelet
PodResources API
sys /proc /etc
sys /proc /etc
16.2 设计模式矩阵
#mermaid-svg-8PyP8sRCyJP7xB68{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-8PyP8sRCyJP7xB68 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-8PyP8sRCyJP7xB68 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-8PyP8sRCyJP7xB68 .error-icon{fill:#552222;}#mermaid-svg-8PyP8sRCyJP7xB68 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-8PyP8sRCyJP7xB68 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-8PyP8sRCyJP7xB68 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-8PyP8sRCyJP7xB68 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-8PyP8sRCyJP7xB68 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-8PyP8sRCyJP7xB68 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-8PyP8sRCyJP7xB68 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-8PyP8sRCyJP7xB68 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-8PyP8sRCyJP7xB68 .marker.cross{stroke:#333333;}#mermaid-svg-8PyP8sRCyJP7xB68 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-8PyP8sRCyJP7xB68 p{margin:0;}#mermaid-svg-8PyP8sRCyJP7xB68 .edge{stroke-width:3;}#mermaid-svg-8PyP8sRCyJP7xB68 .section--1 rect,#mermaid-svg-8PyP8sRCyJP7xB68 .section--1 path,#mermaid-svg-8PyP8sRCyJP7xB68 .section--1 circle,#mermaid-svg-8PyP8sRCyJP7xB68 .section--1 polygon,#mermaid-svg-8PyP8sRCyJP7xB68 .section--1 path{fill:hsl(240, 100%, 76.2745098039%);}#mermaid-svg-8PyP8sRCyJP7xB68 .section--1 text{fill:#ffffff;}#mermaid-svg-8PyP8sRCyJP7xB68 .node-icon--1{font-size:40px;color:#ffffff;}#mermaid-svg-8PyP8sRCyJP7xB68 .section-edge--1{stroke:hsl(240, 100%, 76.2745098039%);}#mermaid-svg-8PyP8sRCyJP7xB68 .edge-depth--1{stroke-width:17;}#mermaid-svg-8PyP8sRCyJP7xB68 .section--1 line{stroke:hsl(60, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-8PyP8sRCyJP7xB68 .disabled,#mermaid-svg-8PyP8sRCyJP7xB68 .disabled circle,#mermaid-svg-8PyP8sRCyJP7xB68 .disabled text{fill:lightgray;}#mermaid-svg-8PyP8sRCyJP7xB68 .disabled text{fill:#efefef;}#mermaid-svg-8PyP8sRCyJP7xB68 .section-0 rect,#mermaid-svg-8PyP8sRCyJP7xB68 .section-0 path,#mermaid-svg-8PyP8sRCyJP7xB68 .section-0 circle,#mermaid-svg-8PyP8sRCyJP7xB68 .section-0 polygon,#mermaid-svg-8PyP8sRCyJP7xB68 .section-0 path{fill:hsl(60, 100%, 73.5294117647%);}#mermaid-svg-8PyP8sRCyJP7xB68 .section-0 text{fill:black;}#mermaid-svg-8PyP8sRCyJP7xB68 .node-icon-0{font-size:40px;color:black;}#mermaid-svg-8PyP8sRCyJP7xB68 .section-edge-0{stroke:hsl(60, 100%, 73.5294117647%);}#mermaid-svg-8PyP8sRCyJP7xB68 .edge-depth-0{stroke-width:14;}#mermaid-svg-8PyP8sRCyJP7xB68 .section-0 line{stroke:hsl(240, 100%, 83.5294117647%);stroke-width:3;}#mermaid-svg-8PyP8sRCyJP7xB68 .disabled,#mermaid-svg-8PyP8sRCyJP7xB68 .disabled circle,#mermaid-svg-8PyP8sRCyJP7xB68 .disabled text{fill:lightgray;}#mermaid-svg-8PyP8sRCyJP7xB68 .disabled text{fill:#efefef;}#mermaid-svg-8PyP8sRCyJP7xB68 .section-1 rect,#mermaid-svg-8PyP8sRCyJP7xB68 .section-1 path,#mermaid-svg-8PyP8sRCyJP7xB68 .section-1 circle,#mermaid-svg-8PyP8sRCyJP7xB68 .section-1 polygon,#mermaid-svg-8PyP8sRCyJP7xB68 .section-1 path{fill:hsl(80, 100%, 76.2745098039%);}#mermaid-svg-8PyP8sRCyJP7xB68 .section-1 text{fill:black;}#mermaid-svg-8PyP8sRCyJP7xB68 .node-icon-1{font-size:40px;color:black;}#mermaid-svg-8PyP8sRCyJP7xB68 .section-edge-1{stroke:hsl(80, 100%, 76.2745098039%);}#mermaid-svg-8PyP8sRCyJP7xB68 .edge-depth-1{stroke-width:11;}#mermaid-svg-8PyP8sRCyJP7xB68 .section-1 line{stroke:hsl(260, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-8PyP8sRCyJP7xB68 .disabled,#mermaid-svg-8PyP8sRCyJP7xB68 .disabled circle,#mermaid-svg-8PyP8sRCyJP7xB68 .disabled text{fill:lightgray;}#mermaid-svg-8PyP8sRCyJP7xB68 .disabled text{fill:#efefef;}#mermaid-svg-8PyP8sRCyJP7xB68 .section-2 rect,#mermaid-svg-8PyP8sRCyJP7xB68 .section-2 path,#mermaid-svg-8PyP8sRCyJP7xB68 .section-2 circle,#mermaid-svg-8PyP8sRCyJP7xB68 .section-2 polygon,#mermaid-svg-8PyP8sRCyJP7xB68 .section-2 path{fill:hsl(270, 100%, 76.2745098039%);}#mermaid-svg-8PyP8sRCyJP7xB68 .section-2 text{fill:#ffffff;}#mermaid-svg-8PyP8sRCyJP7xB68 .node-icon-2{font-size:40px;color:#ffffff;}#mermaid-svg-8PyP8sRCyJP7xB68 .section-edge-2{stroke:hsl(270, 100%, 76.2745098039%);}#mermaid-svg-8PyP8sRCyJP7xB68 .edge-depth-2{stroke-width:8;}#mermaid-svg-8PyP8sRCyJP7xB68 .section-2 line{stroke:hsl(90, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-8PyP8sRCyJP7xB68 .disabled,#mermaid-svg-8PyP8sRCyJP7xB68 .disabled circle,#mermaid-svg-8PyP8sRCyJP7xB68 .disabled text{fill:lightgray;}#mermaid-svg-8PyP8sRCyJP7xB68 .disabled text{fill:#efefef;}#mermaid-svg-8PyP8sRCyJP7xB68 .section-3 rect,#mermaid-svg-8PyP8sRCyJP7xB68 .section-3 path,#mermaid-svg-8PyP8sRCyJP7xB68 .section-3 circle,#mermaid-svg-8PyP8sRCyJP7xB68 .section-3 polygon,#mermaid-svg-8PyP8sRCyJP7xB68 .section-3 path{fill:hsl(300, 100%, 76.2745098039%);}#mermaid-svg-8PyP8sRCyJP7xB68 .section-3 text{fill:black;}#mermaid-svg-8PyP8sRCyJP7xB68 .node-icon-3{font-size:40px;color:black;}#mermaid-svg-8PyP8sRCyJP7xB68 .section-edge-3{stroke:hsl(300, 100%, 76.2745098039%);}#mermaid-svg-8PyP8sRCyJP7xB68 .edge-depth-3{stroke-width:5;}#mermaid-svg-8PyP8sRCyJP7xB68 .section-3 line{stroke:hsl(120, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-8PyP8sRCyJP7xB68 .disabled,#mermaid-svg-8PyP8sRCyJP7xB68 .disabled circle,#mermaid-svg-8PyP8sRCyJP7xB68 .disabled text{fill:lightgray;}#mermaid-svg-8PyP8sRCyJP7xB68 .disabled text{fill:#efefef;}#mermaid-svg-8PyP8sRCyJP7xB68 .section-4 rect,#mermaid-svg-8PyP8sRCyJP7xB68 .section-4 path,#mermaid-svg-8PyP8sRCyJP7xB68 .section-4 circle,#mermaid-svg-8PyP8sRCyJP7xB68 .section-4 polygon,#mermaid-svg-8PyP8sRCyJP7xB68 .section-4 path{fill:hsl(330, 100%, 76.2745098039%);}#mermaid-svg-8PyP8sRCyJP7xB68 .section-4 text{fill:black;}#mermaid-svg-8PyP8sRCyJP7xB68 .node-icon-4{font-size:40px;color:black;}#mermaid-svg-8PyP8sRCyJP7xB68 .section-edge-4{stroke:hsl(330, 100%, 76.2745098039%);}#mermaid-svg-8PyP8sRCyJP7xB68 .edge-depth-4{stroke-width:2;}#mermaid-svg-8PyP8sRCyJP7xB68 .section-4 line{stroke:hsl(150, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-8PyP8sRCyJP7xB68 .disabled,#mermaid-svg-8PyP8sRCyJP7xB68 .disabled circle,#mermaid-svg-8PyP8sRCyJP7xB68 .disabled text{fill:lightgray;}#mermaid-svg-8PyP8sRCyJP7xB68 .disabled text{fill:#efefef;}#mermaid-svg-8PyP8sRCyJP7xB68 .section-5 rect,#mermaid-svg-8PyP8sRCyJP7xB68 .section-5 path,#mermaid-svg-8PyP8sRCyJP7xB68 .section-5 circle,#mermaid-svg-8PyP8sRCyJP7xB68 .section-5 polygon,#mermaid-svg-8PyP8sRCyJP7xB68 .section-5 path{fill:hsl(0, 100%, 76.2745098039%);}#mermaid-svg-8PyP8sRCyJP7xB68 .section-5 text{fill:black;}#mermaid-svg-8PyP8sRCyJP7xB68 .node-icon-5{font-size:40px;color:black;}#mermaid-svg-8PyP8sRCyJP7xB68 .section-edge-5{stroke:hsl(0, 100%, 76.2745098039%);}#mermaid-svg-8PyP8sRCyJP7xB68 .edge-depth-5{stroke-width:-1;}#mermaid-svg-8PyP8sRCyJP7xB68 .section-5 line{stroke:hsl(180, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-8PyP8sRCyJP7xB68 .disabled,#mermaid-svg-8PyP8sRCyJP7xB68 .disabled circle,#mermaid-svg-8PyP8sRCyJP7xB68 .disabled text{fill:lightgray;}#mermaid-svg-8PyP8sRCyJP7xB68 .disabled text{fill:#efefef;}#mermaid-svg-8PyP8sRCyJP7xB68 .section-6 rect,#mermaid-svg-8PyP8sRCyJP7xB68 .section-6 path,#mermaid-svg-8PyP8sRCyJP7xB68 .section-6 circle,#mermaid-svg-8PyP8sRCyJP7xB68 .section-6 polygon,#mermaid-svg-8PyP8sRCyJP7xB68 .section-6 path{fill:hsl(30, 100%, 76.2745098039%);}#mermaid-svg-8PyP8sRCyJP7xB68 .section-6 text{fill:black;}#mermaid-svg-8PyP8sRCyJP7xB68 .node-icon-6{font-size:40px;color:black;}#mermaid-svg-8PyP8sRCyJP7xB68 .section-edge-6{stroke:hsl(30, 100%, 76.2745098039%);}#mermaid-svg-8PyP8sRCyJP7xB68 .edge-depth-6{stroke-width:-4;}#mermaid-svg-8PyP8sRCyJP7xB68 .section-6 line{stroke:hsl(210, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-8PyP8sRCyJP7xB68 .disabled,#mermaid-svg-8PyP8sRCyJP7xB68 .disabled circle,#mermaid-svg-8PyP8sRCyJP7xB68 .disabled text{fill:lightgray;}#mermaid-svg-8PyP8sRCyJP7xB68 .disabled text{fill:#efefef;}#mermaid-svg-8PyP8sRCyJP7xB68 .section-7 rect,#mermaid-svg-8PyP8sRCyJP7xB68 .section-7 path,#mermaid-svg-8PyP8sRCyJP7xB68 .section-7 circle,#mermaid-svg-8PyP8sRCyJP7xB68 .section-7 polygon,#mermaid-svg-8PyP8sRCyJP7xB68 .section-7 path{fill:hsl(90, 100%, 76.2745098039%);}#mermaid-svg-8PyP8sRCyJP7xB68 .section-7 text{fill:black;}#mermaid-svg-8PyP8sRCyJP7xB68 .node-icon-7{font-size:40px;color:black;}#mermaid-svg-8PyP8sRCyJP7xB68 .section-edge-7{stroke:hsl(90, 100%, 76.2745098039%);}#mermaid-svg-8PyP8sRCyJP7xB68 .edge-depth-7{stroke-width:-7;}#mermaid-svg-8PyP8sRCyJP7xB68 .section-7 line{stroke:hsl(270, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-8PyP8sRCyJP7xB68 .disabled,#mermaid-svg-8PyP8sRCyJP7xB68 .disabled circle,#mermaid-svg-8PyP8sRCyJP7xB68 .disabled text{fill:lightgray;}#mermaid-svg-8PyP8sRCyJP7xB68 .disabled text{fill:#efefef;}#mermaid-svg-8PyP8sRCyJP7xB68 .section-8 rect,#mermaid-svg-8PyP8sRCyJP7xB68 .section-8 path,#mermaid-svg-8PyP8sRCyJP7xB68 .section-8 circle,#mermaid-svg-8PyP8sRCyJP7xB68 .section-8 polygon,#mermaid-svg-8PyP8sRCyJP7xB68 .section-8 path{fill:hsl(150, 100%, 76.2745098039%);}#mermaid-svg-8PyP8sRCyJP7xB68 .section-8 text{fill:black;}#mermaid-svg-8PyP8sRCyJP7xB68 .node-icon-8{font-size:40px;color:black;}#mermaid-svg-8PyP8sRCyJP7xB68 .section-edge-8{stroke:hsl(150, 100%, 76.2745098039%);}#mermaid-svg-8PyP8sRCyJP7xB68 .edge-depth-8{stroke-width:-10;}#mermaid-svg-8PyP8sRCyJP7xB68 .section-8 line{stroke:hsl(330, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-8PyP8sRCyJP7xB68 .disabled,#mermaid-svg-8PyP8sRCyJP7xB68 .disabled circle,#mermaid-svg-8PyP8sRCyJP7xB68 .disabled text{fill:lightgray;}#mermaid-svg-8PyP8sRCyJP7xB68 .disabled text{fill:#efefef;}#mermaid-svg-8PyP8sRCyJP7xB68 .section-9 rect,#mermaid-svg-8PyP8sRCyJP7xB68 .section-9 path,#mermaid-svg-8PyP8sRCyJP7xB68 .section-9 circle,#mermaid-svg-8PyP8sRCyJP7xB68 .section-9 polygon,#mermaid-svg-8PyP8sRCyJP7xB68 .section-9 path{fill:hsl(180, 100%, 76.2745098039%);}#mermaid-svg-8PyP8sRCyJP7xB68 .section-9 text{fill:black;}#mermaid-svg-8PyP8sRCyJP7xB68 .node-icon-9{font-size:40px;color:black;}#mermaid-svg-8PyP8sRCyJP7xB68 .section-edge-9{stroke:hsl(180, 100%, 76.2745098039%);}#mermaid-svg-8PyP8sRCyJP7xB68 .edge-depth-9{stroke-width:-13;}#mermaid-svg-8PyP8sRCyJP7xB68 .section-9 line{stroke:hsl(0, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-8PyP8sRCyJP7xB68 .disabled,#mermaid-svg-8PyP8sRCyJP7xB68 .disabled circle,#mermaid-svg-8PyP8sRCyJP7xB68 .disabled text{fill:lightgray;}#mermaid-svg-8PyP8sRCyJP7xB68 .disabled text{fill:#efefef;}#mermaid-svg-8PyP8sRCyJP7xB68 .section-10 rect,#mermaid-svg-8PyP8sRCyJP7xB68 .section-10 path,#mermaid-svg-8PyP8sRCyJP7xB68 .section-10 circle,#mermaid-svg-8PyP8sRCyJP7xB68 .section-10 polygon,#mermaid-svg-8PyP8sRCyJP7xB68 .section-10 path{fill:hsl(210, 100%, 76.2745098039%);}#mermaid-svg-8PyP8sRCyJP7xB68 .section-10 text{fill:black;}#mermaid-svg-8PyP8sRCyJP7xB68 .node-icon-10{font-size:40px;color:black;}#mermaid-svg-8PyP8sRCyJP7xB68 .section-edge-10{stroke:hsl(210, 100%, 76.2745098039%);}#mermaid-svg-8PyP8sRCyJP7xB68 .edge-depth-10{stroke-width:-16;}#mermaid-svg-8PyP8sRCyJP7xB68 .section-10 line{stroke:hsl(30, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-8PyP8sRCyJP7xB68 .disabled,#mermaid-svg-8PyP8sRCyJP7xB68 .disabled circle,#mermaid-svg-8PyP8sRCyJP7xB68 .disabled text{fill:lightgray;}#mermaid-svg-8PyP8sRCyJP7xB68 .disabled text{fill:#efefef;}#mermaid-svg-8PyP8sRCyJP7xB68 .section-root rect,#mermaid-svg-8PyP8sRCyJP7xB68 .section-root path,#mermaid-svg-8PyP8sRCyJP7xB68 .section-root circle,#mermaid-svg-8PyP8sRCyJP7xB68 .section-root polygon{fill:hsl(240, 100%, 46.2745098039%);}#mermaid-svg-8PyP8sRCyJP7xB68 .section-root text{fill:#ffffff;}#mermaid-svg-8PyP8sRCyJP7xB68 .section-root span{color:#ffffff;}#mermaid-svg-8PyP8sRCyJP7xB68 .section-2 span{color:#ffffff;}#mermaid-svg-8PyP8sRCyJP7xB68 .icon-container{height:100%;display:flex;justify-content:center;align-items:center;}#mermaid-svg-8PyP8sRCyJP7xB68 .edge{fill:none;}#mermaid-svg-8PyP8sRCyJP7xB68 .mindmap-node-label{dy:1em;alignment-baseline:middle;text-anchor:middle;dominant-baseline:middle;text-align:center;}#mermaid-svg-8PyP8sRCyJP7xB68 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} NFD 设计模式
接口抽象
FeatureSource 统一接口
LabelSource 扩展接口
ResourcesScanner 接口
ResourcesAggregator 接口
ArtifactClient 接口
注册模式
init 副作用注册
全局 map 存储
GetAllFeatureSources
选项模式
NfdMasterOption
NfdWorkerOption
ArtifactClientOpts
NodeValidatorOpts
控制器模式
Informer + Controller
Event Handler 回调
UpdaterPool 工作池
Debounce 防抖
事件驱动
fsnotify 文件监听
channel 事件传递
select 多路复用
nil channel 禁用
特征发现
Build tag
Stub 降级
多路径回退
容错设计
规则引擎
AND/OR
failFast vs 全量评估
模板展开
回引机制
正则缓存
安全设计
命名空间白/黑名单
模板函数移除
模板大小限制
Label 验证
kubelet
可观测性
Prometheus 指标
健康检查端点
延迟转储
klog 分级日志
并发控制
atomic.Value
sync.Mutex
正则缓存
信号量限制并行
每节点 goroutine
16.3 数据模型全景
#mermaid-svg-VewaqOnd0aRPiWKQ{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-VewaqOnd0aRPiWKQ .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-VewaqOnd0aRPiWKQ .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-VewaqOnd0aRPiWKQ .error-icon{fill:#552222;}#mermaid-svg-VewaqOnd0aRPiWKQ .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-VewaqOnd0aRPiWKQ .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-VewaqOnd0aRPiWKQ .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-VewaqOnd0aRPiWKQ .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-VewaqOnd0aRPiWKQ .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-VewaqOnd0aRPiWKQ .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-VewaqOnd0aRPiWKQ .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-VewaqOnd0aRPiWKQ .marker{fill:#333333;stroke:#333333;}#mermaid-svg-VewaqOnd0aRPiWKQ .marker.cross{stroke:#333333;}#mermaid-svg-VewaqOnd0aRPiWKQ svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-VewaqOnd0aRPiWKQ p{margin:0;}#mermaid-svg-VewaqOnd0aRPiWKQ .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-VewaqOnd0aRPiWKQ .cluster-label text{fill:#333;}#mermaid-svg-VewaqOnd0aRPiWKQ .cluster-label span{color:#333;}#mermaid-svg-VewaqOnd0aRPiWKQ .cluster-label span p{background-color:transparent;}#mermaid-svg-VewaqOnd0aRPiWKQ .label text,#mermaid-svg-VewaqOnd0aRPiWKQ span{fill:#333;color:#333;}#mermaid-svg-VewaqOnd0aRPiWKQ .node rect,#mermaid-svg-VewaqOnd0aRPiWKQ .node circle,#mermaid-svg-VewaqOnd0aRPiWKQ .node ellipse,#mermaid-svg-VewaqOnd0aRPiWKQ .node polygon,#mermaid-svg-VewaqOnd0aRPiWKQ .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-VewaqOnd0aRPiWKQ .rough-node .label text,#mermaid-svg-VewaqOnd0aRPiWKQ .node .label text,#mermaid-svg-VewaqOnd0aRPiWKQ .image-shape .label,#mermaid-svg-VewaqOnd0aRPiWKQ .icon-shape .label{text-anchor:middle;}#mermaid-svg-VewaqOnd0aRPiWKQ .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-VewaqOnd0aRPiWKQ .rough-node .label,#mermaid-svg-VewaqOnd0aRPiWKQ .node .label,#mermaid-svg-VewaqOnd0aRPiWKQ .image-shape .label,#mermaid-svg-VewaqOnd0aRPiWKQ .icon-shape .label{text-align:center;}#mermaid-svg-VewaqOnd0aRPiWKQ .node.clickable{cursor:pointer;}#mermaid-svg-VewaqOnd0aRPiWKQ .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-VewaqOnd0aRPiWKQ .arrowheadPath{fill:#333333;}#mermaid-svg-VewaqOnd0aRPiWKQ .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-VewaqOnd0aRPiWKQ .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-VewaqOnd0aRPiWKQ .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-VewaqOnd0aRPiWKQ .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-VewaqOnd0aRPiWKQ .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-VewaqOnd0aRPiWKQ .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-VewaqOnd0aRPiWKQ .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-VewaqOnd0aRPiWKQ .cluster text{fill:#333;}#mermaid-svg-VewaqOnd0aRPiWKQ .cluster span{color:#333;}#mermaid-svg-VewaqOnd0aRPiWKQ 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-VewaqOnd0aRPiWKQ .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-VewaqOnd0aRPiWKQ rect.text{fill:none;stroke-width:0;}#mermaid-svg-VewaqOnd0aRPiWKQ .icon-shape,#mermaid-svg-VewaqOnd0aRPiWKQ .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-VewaqOnd0aRPiWKQ .icon-shape p,#mermaid-svg-VewaqOnd0aRPiWKQ .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-VewaqOnd0aRPiWKQ .icon-shape .label rect,#mermaid-svg-VewaqOnd0aRPiWKQ .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-VewaqOnd0aRPiWKQ .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-VewaqOnd0aRPiWKQ .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-VewaqOnd0aRPiWKQ :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 输出
规则定义
CRD 类型
特征数据 (Features)
Flags
mapdomain.featureFlagFeatureSet
存在性标记
Attributes
mapdomain.featureAttributeFeatureSet
键值对
Instances
mapdomain.featureInstanceFeatureSet
多实例属性集
NodeFeature
Worker → API
NodeFeatureRule
用户 → API
NodeFeatureGroup
用户 → API
NodeResourceTopology
TopologyUpdater → API
Rule
labels + vars + annotations + ER + taints
GroupRule
vars only
MatchAny
逻辑 OR
MatchFeatures
逻辑 AND
MatchExpressionSet
AND over expressions
Node Labels
Node Taints
Extended Resources
Node Annotations
NUMA ZoneList
16.4 安全设计总结
| 安全措施 | 实现位置 | 说明 |
|---|---|---|
| 标签命名空间白名单 | validate.go |
只允许 *.node.kubernetes.io 命名空间 |
| 标签命名空间黑名单 | validate.go |
禁止 kubernetes.io 和 *.kubernetes.io(NFD 自己的除外) |
| 无命名空间标签禁止 | validate.go |
所有标签必须有命名空间前缀 |
| 模板函数移除 | template.go |
移除 env/expandenv/getHostByName |
| 模板大小限制 | template.go |
64KB 上限防止栈溢出 |
| 模板 missingkey=error | template.go |
引用不存在变量时报错 |
| 注解值长度限制 | validate.go |
最大 4096 字符 |
| 扩展资源值验证 | validate.go |
必须是合法的 Quantity |
| 污点效果验证 | validate.go |
只允许 NoSchedule/PreferNoSchedule/NoExecute |
| TLS 跳过仅限 kubelet | kubelet_configz.go |
仅 kubelet /configz 使用 Insecure TLS |
| FeatureGate LockToDefault | featuregate.go |
GA 特性锁定为默认值 |
| 正则缓存无淘汰 | expression.go |
安全 trade-off:只有操作者编写的模式进入缓存 |
16.5 可观测性体系
#mermaid-svg-PvbxVCCylhkKRgZD{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-PvbxVCCylhkKRgZD .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-PvbxVCCylhkKRgZD .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-PvbxVCCylhkKRgZD .error-icon{fill:#552222;}#mermaid-svg-PvbxVCCylhkKRgZD .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-PvbxVCCylhkKRgZD .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-PvbxVCCylhkKRgZD .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-PvbxVCCylhkKRgZD .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-PvbxVCCylhkKRgZD .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-PvbxVCCylhkKRgZD .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-PvbxVCCylhkKRgZD .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-PvbxVCCylhkKRgZD .marker{fill:#333333;stroke:#333333;}#mermaid-svg-PvbxVCCylhkKRgZD .marker.cross{stroke:#333333;}#mermaid-svg-PvbxVCCylhkKRgZD svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-PvbxVCCylhkKRgZD p{margin:0;}#mermaid-svg-PvbxVCCylhkKRgZD .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-PvbxVCCylhkKRgZD .cluster-label text{fill:#333;}#mermaid-svg-PvbxVCCylhkKRgZD .cluster-label span{color:#333;}#mermaid-svg-PvbxVCCylhkKRgZD .cluster-label span p{background-color:transparent;}#mermaid-svg-PvbxVCCylhkKRgZD .label text,#mermaid-svg-PvbxVCCylhkKRgZD span{fill:#333;color:#333;}#mermaid-svg-PvbxVCCylhkKRgZD .node rect,#mermaid-svg-PvbxVCCylhkKRgZD .node circle,#mermaid-svg-PvbxVCCylhkKRgZD .node ellipse,#mermaid-svg-PvbxVCCylhkKRgZD .node polygon,#mermaid-svg-PvbxVCCylhkKRgZD .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-PvbxVCCylhkKRgZD .rough-node .label text,#mermaid-svg-PvbxVCCylhkKRgZD .node .label text,#mermaid-svg-PvbxVCCylhkKRgZD .image-shape .label,#mermaid-svg-PvbxVCCylhkKRgZD .icon-shape .label{text-anchor:middle;}#mermaid-svg-PvbxVCCylhkKRgZD .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-PvbxVCCylhkKRgZD .rough-node .label,#mermaid-svg-PvbxVCCylhkKRgZD .node .label,#mermaid-svg-PvbxVCCylhkKRgZD .image-shape .label,#mermaid-svg-PvbxVCCylhkKRgZD .icon-shape .label{text-align:center;}#mermaid-svg-PvbxVCCylhkKRgZD .node.clickable{cursor:pointer;}#mermaid-svg-PvbxVCCylhkKRgZD .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-PvbxVCCylhkKRgZD .arrowheadPath{fill:#333333;}#mermaid-svg-PvbxVCCylhkKRgZD .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-PvbxVCCylhkKRgZD .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-PvbxVCCylhkKRgZD .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-PvbxVCCylhkKRgZD .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-PvbxVCCylhkKRgZD .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-PvbxVCCylhkKRgZD .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-PvbxVCCylhkKRgZD .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-PvbxVCCylhkKRgZD .cluster text{fill:#333;}#mermaid-svg-PvbxVCCylhkKRgZD .cluster span{color:#333;}#mermaid-svg-PvbxVCCylhkKRgZD 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-PvbxVCCylhkKRgZD .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-PvbxVCCylhkKRgZD rect.text{fill:none;stroke-width:0;}#mermaid-svg-PvbxVCCylhkKRgZD .icon-shape,#mermaid-svg-PvbxVCCylhkKRgZD .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-PvbxVCCylhkKRgZD .icon-shape p,#mermaid-svg-PvbxVCCylhkKRgZD .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-PvbxVCCylhkKRgZD .icon-shape .label rect,#mermaid-svg-PvbxVCCylhkKRgZD .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-PvbxVCCylhkKRgZD .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-PvbxVCCylhkKRgZD .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-PvbxVCCylhkKRgZD :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 日志
klog/v2
分级日志 V(1)-V(5)
DelayedDumper
延迟序列化
健康检查
/healthz
所有组件
Prometheus 指标
nfd-gc
build_info
objects_deleted_total by kind
object_delete_errors_total by kind
nfd-topology-updater
build_info
scan_errors_total
nfd-worker
build_info
feature_discovery_errors_total
feature_discovery_duration_seconds
nodefeature_publish_errors_total
nodefeature_publish_duration_seconds
nfd-master
build_info
node_update_requests_total
node_updates_total by status
node_update_duration_seconds
rule_processing_duration_seconds
16.6 代码统计
| 模块 | 目录 | 非测试 Go 文件数 | 关键功能 |
|---|---|---|---|
| API 层 | api/ |
7 | CRD 类型定义、deepcopy |
| Source 层 | source/ |
~35 | 12 种特征发现源 |
| Master | pkg/nfd-master/ |
5 | 规则执行、节点更新、API Controller |
| Worker | pkg/nfd-worker/ |
2 | 特征发现、NodeFeature 发布 |
| Topology-Updater | pkg/nfd-topology-updater/ |
3 | NUMA 拓扑报告 |
| GC | pkg/nfd-gc/ |
2 | 垃圾回收 |
| 规则引擎 | pkg/apis/nfd/ |
4 | 规则执行、表达式匹配、模板、验证 |
| ResourceMonitor | pkg/resourcemonitor/ |
4 | Pod 资源扫描与聚合 |
| Utils | pkg/utils/ |
~10 | 工具函数、路径管理、Klog |
| Features | pkg/features/ |
1 | FeatureGate 定义 |
| CLI | cmd/ |
~15 | 6 个可执行入口 |
| 兼容性客户端 | pkg/client-nfd/ |
3 | OCI artifact 客户端、节点验证器 |
| kubectl 插件 | pkg/kubectl-nfd/ |
3 | dryrun/test/validate |
| 版本 | pkg/version/ |
1 | 版本管理 |
| 总计 | ~95 | 完整 NFD 系统 |
附录:四卷文档索引
| 卷次 | 文件名 | 大小 | 覆盖内容 |
|---|---|---|---|
| 第一卷 | nfd-analysis-vol1-arch-api-source.md |
~42KB | 项目整体架构、API层(CRD类型/特征模型/注册机制)、Source层(12种特征发现源) |
| 第二卷 | nfd-analysis-vol2-master-worker.md |
~36KB | nfd-master(核心逻辑/API Controller/Updater Pool/Namespace Lister/Metrics)、nfd-worker(核心逻辑/特征发现/NodeFeature发布/标签生成/配置系统/Metrics) |
| 第三卷 | nfd-analysis-vol3-topo-gc-rules.md |
~63KB | nfd-topology-updater(NUMA拓扑/事件驱动/CRD等待/NRT管理)、nfd-gc(垃圾回收/双重清理/Node删除处理)、resourcemonitor(Pod扫描/NUMA聚合/内存扣减)、规则引擎(表达式匹配/模板引擎/验证器)、topologypolicy、podres |
| 第四卷 | nfd-analysis-vol4-cli-utils-summary.md |
~38KB | cmd/全部入口(6个可执行文件)、FeatureGate系统、工具函数、HostPath/Klog/Kubeconfig、版本管理、镜像兼容性客户端、kubectl插件、全局架构总结(设计模式矩阵/安全设计/可观测性/代码统计) |
| 合计 | ~179KB | ~95个Go文件,100%模块覆盖,50+ Mermaid图表 |
第四卷结束。全部分析完成。
本系列四卷文档完整覆盖了
node-feature-discovery-0.20.0-devel代码库的所有模块,包括:
- 6 个可执行组件入口
- 12 种特征发现源
- 3 种 CRD 类型定义
- 核心规则引擎(14种操作符 + 模板引擎 + 回引机制)
- NUMA 拓扑聚合算法
- 垃圾回收双重清理机制
- 镜像兼容性验证
- 50+ 个 Mermaid 架构/流程/类图
- 完整的设计模式矩阵和安全设计总结