【SRIOV】SR-IOV Network Operator 1.6.0 Part 1 — main.go 超深度分析

SR-IOV Network Operator 1.6.0 — main.go 超深度分析

文件路径: main.go (305行)
模块定位: Operator 主入口,基于 controller-runtime 的 Manager 模式


一、模块定位

1.1 业务职责

main.go 是 SR-IOV Network Operator 的主控制器入口,运行在 Kubernetes 集群中作为 Deployment。它的核心职责包括:

  1. 初始化运行时 Scheme:注册所有需要管理的 CRD 资源类型到 runtime.Scheme
  2. 创建双 Manager 架构:一个命名空间级别 Manager(监听 operator 所在 namespace),一个全局 Manager(跨命名空间监听网络资源)
  3. 注册 6+1 个 Controller:SriovNetwork、SriovIBNetwork、OVSNetwork、SriovNetworkNodePolicy、SriovOperatorConfig、SriovNetworkPoolConfig + DrainReconcile
  4. 健康检查:配置 healthz/readyz 探针
  5. 优雅关闭:捕获终止信号,执行清理(调用 utils.Shutdown
  6. NIC ID 映射初始化:从 ConfigMap 加载支持的 NIC 设备 ID 列表

1.2 在系统中的位置

┌─────────────────────────────────────────────────┐
│              Kubernetes Cluster                  │
│                                                  │
│  ┌──────────────────────────────────────────┐   │
│  │     SR-IOV Operator Deployment            │   │
│  │  ┌────────────────────────────────────┐  │   │
│  │  │         main.go (本文件)            │  │   │
│  │  │  ┌─────────┐    ┌──────────────┐   │  │   │
│  │  │  │  mgr     │    │  mgrGlobal    │   │  │   │
│  │  │  │(ns级)    │    │ (全局级)      │   │  │   │
│  │  │  └────┬─────┘    └──────┬───────┘   │  │   │
│  │  │       │                 │           │  │   │
│  │  │  ┌────▼─────┐    ┌──────▼───────┐   │  │   │
│  │  │  │NodePolicy│    │SriovNetwork  │   │  │   │
│  │  │  │Operator  │    │SriovIBNetwork│   │  │   │
│  │  │  │Config    │    │OVSNetwork    │   │  │   │
│  │  │  │PoolConfig│    │              │   │  │   │
│  │  │  │Drain     │    │              │   │  │   │
│  │  │  └──────────┘    └──────────────┘   │  │   │
│  │  └────────────────────────────────────┘  │   │
│  └──────────────────────────────────────────┘   │
│                                                  │
│  ┌──────────────┐  ┌────────────────────────┐   │
│  │  Config Daemon│  │  Webhook Server        │   │
│  │ (DaemonSet)   │  │  (Validating/Mutating) │   │
│  └──────────────┘  └────────────────────────┘   │
└─────────────────────────────────────────────────┘

二、模块整体结构

2.1 包级变量与 init() 函数

var (
    scheme   = runtime.NewScheme()           // 全局 runtime.Scheme,用于序列化/反序列化 K8s 对象
    setupLog = ctrl.Log.WithName("setup")    // 带有 "setup" 标识的日志器
)
init() 函数 — Scheme 注册
func init() {
    // 注册 Kubernetes 核心API(Pod, Node, ConfigMap 等)到 scheme
    utilruntime.Must(clientgoscheme.AddToScheme(scheme))

    // 注册 SR-IOV 自定义资源(SriovNetwork, SriovNetworkNodeState 等)
    utilruntime.Must(sriovnetworkv1.AddToScheme(scheme))

    // 注册 NetworkAttachmentDefinition CRD(用于多网络接口)
    utilruntime.Must(netattdefv1.AddToScheme(scheme))

    // 注册 OpenShift MachineConfiguration CRD(用于节点配置管理)
    utilruntime.Must(mcfgv1.AddToScheme(scheme))

    // 注册 OpenShift Config CRD(用于集群配置信息)
    utilruntime.Must(openshiftconfigv1.AddToScheme(scheme))
}

设计意图: init() 在包加载时自动执行,确保 Scheme 在 main() 之前就准备好。utilruntime.Must 会在失败时 panic,这是合理的——如果 Scheme 注册失败,Operator 根本无法工作。

2.2 import 分析

Import 路径用途
sriovnetworkv1SR-IOV CRD API 定义
netattdefv1NetworkAttachmentDefinition CRD (CNI 多网络)
openshiftconfigv1OpenShift 集群配置 API
mcfgv1OpenShift MachineConfig API (节点配置)
clientgoschemeK8s 核心 API scheme
_ "k8s.io/client-go/plugin/pkg/client/auth"匿名导入,注册所有认证插件
_ "k8s.io/client-go/plugin/pkg/client/auth/gcp"GCP 认证插件(重复注册,兼容性考虑)
ctrlcontroller-runtime 框架核心
cachecontroller-runtime 缓存管理
healthz健康检查端点
featuregate特性门控
platforms平台检测(OpenShift vs Kubernetes)
vars全局变量存储
utils工具函数

2.3 核心方法清单

方法作用调用时机
init()注册所有 CRD 类型到 Scheme包加载时
main()程序入口,创建 Manager,注册控制器,启动管理器程序启动
initNicIDMap()从 ConfigMap 初始化支持的 NIC ID 映射main() 中调用

2.4 双 Manager 架构数据流

main.go 双 Manager 架构

索引

K8s API Server

mgr (命名空间 Manager)

mgrGlobal (全局 Manager)

spec.networkNamespace
SriovNetwork 索引

SriovNetworkReconciler

全局 Cache
跨 namespace

SriovIBNetworkReconciler

OVSNetworkReconciler

spec.networkNamespace
SriovIBNetwork 索引

spec.networkNamespace
OVSNetwork 索引

kube-apiserver

SriovNetworkNodePolicyReconciler

命名空间 Cache
仅 operator namespace

SriovOperatorConfigReconciler

SriovNetworkPoolConfigReconciler

DrainReconcileController

为什么需要双 Manager?

  1. mgrGlobal:需要跨命名空间监听 SriovNetworkSriovIBNetworkOVSNetwork——这些网络资源可以定义在任何 namespace 中,但其 spec.networkNamespace 指向目标 namespace
  2. mgr:只需要监听 operator 所在 namespace 的 SriovNetworkNodePolicySriovOperatorConfigSriovNetworkPoolConfig 等,缩小缓存范围,降低内存消耗

三、核心业务逻辑深度解析

3.1 完整执行流程

为空

非空

stop signal

global error

ns error

程序启动

init: Scheme 注册

main: flag 解析

日志初始化 snolog.InitLog

获取 K8s REST Config

RESOURCE_PREFIX 检查

报错退出

创建命名空间 Manager mgr

配置健康检查 healthz/readyz

创建全局 Manager mgrGlobal

为 mgrGlobal 添加 3 个 Field 索引

initNicIDMap: 加载 NIC ID

创建 PlatformHelper

创建 FeatureGate

注册 3 个全局控制器到 mgrGlobal

注册 3 个命名空间控制器到 mgr

创建 Drain 控制器并注册

创建 shutdown client

启动 mgrGlobal goroutine

启动 mgr goroutine

select 等待终止/错误

取消两个 Manager

取消 ns Manager + 退出

取消 global Manager + 退出

utils.Shutdown 清理

3.2 main() 函数逐行解析

3.2.1 Flag 解析与日志初始化
func main() {
    var metricsAddr string
    var probeAddr string

    // 定义命令行 flag:metrics 端口,默认 :8080
    flag.StringVar(&metricsAddr, "metrics-bind-address", ":8080",
        "The address the metric endpoint binds to.")
    // 定义命令行 flag:健康探针端口,默认 :8081
    flag.StringVar(&probeAddr, "health-probe-bind-address", ":8081",
        "The address the probe endpoint binds to.")

    // 将日志相关 flag 绑定到标准 flag 库(如 log-level 等)
    snolog.BindFlags(flag.CommandLine)
    flag.Parse()           // 解析命令行参数
    snolog.InitLog()        // 初始化日志系统

设计意图: 使用标准的 flag 包而非 cobra,因为 Operator 是简单的单命令程序。snolog.BindFlags 允许用户通过 --log-level 等参数控制日志详细程度。

3.2.2 获取 K8s 配置与 RESOURCE_PREFIX 检查
    // 获取 in-cluster REST 配置,失败则 panic
    // 内部读取 /etc/kubernetes/serviceaccount/ 下的 token 和 ca.crt
    restConfig := ctrl.GetConfigOrDie()

    // 检查 RESOURCE_PREFIX 环境变量(由 vars.init() 从环境读取)
    // RESOURCE_PREFIX 用于 device plugin 暴露设备时的资源名前缀
    // 例如: "openshift.io" → 资源名 "openshift.io/intel_nic_kmod_..."
    if vars.ResourcePrefix == "" {
        setupLog.Error(nil, "RESOURCE_PREFIX environment variable can't be empty")
        os.Exit(1)  // 没有 prefix 就无法暴露设备资源,必须退出
    }

RESOURCE_PREFIX 的重要性: SR-IOV Device Plugin 通过这个前缀向 Kubernetes 注册扩展资源。例如 intel.com/sriov_net_device 中的 intel.com 就是前缀。没有它,Pod 无法请求 SR-IOV VF 设备。

3.2.3 创建命名空间 Manager (mgr)
    // 创建命名空间级别的 controller-runtime Manager
    mgr, err := ctrl.NewManager(restConfig, ctrl.Options{
        Scheme:                 scheme,           // 使用全局 scheme
        HealthProbeBindAddress: probeAddr,        // 健康检查地址 :8081
        Metrics:                server.Options{BindAddress: metricsAddr},  // Metrics 端点 :8080
        WebhookServer:          webhook.NewServer(webhook.Options{Port: 9443}),  // Webhook 端口 9443
        Cache: cache.Options{
            DefaultNamespaces: map[string]cache.Config{
                vars.Namespace: {},  // 只缓存 operator 所在 namespace 的资源
            },
        },
    })
    if err != nil {
        setupLog.Error(err, "unable to create manager")
        os.Exit(1)
    }

关键设计决策——命名空间缓存: DefaultNamespaces 限制 cache 只监听 vars.Namespace(operator 所在 namespace)的资源变更。这大幅减少了内存消耗和 API Server 压力,因为 NodePolicy、OperatorConfig 等资源只存在于 operator namespace。

3.2.4 健康检查注册
    // 注册 liveness 探针(healthz)
    // healthz.Ping 是一个简单的健康检查,只要 Manager 能响应就返回 OK
    if err := mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil {
        setupLog.Error(err, "unable to set up health check")
        os.Exit(1)
    }
    // 注册 readiness 探针(readyz)
    // readyz.Ping 表示 controller 已准备好处理请求
    if err := mgr.AddReadyzCheck("readyz", healthz.Ping); err != nil {
        setupLog.Error(err, "unable to set up ready check")
        os.Exit(1)
    }

K8s 探针映射:

  • healthz → livenessProbe → 失败则重启 Pod
  • readyz → readinessProbe → 失败则从 Service Endpoints 移除
3.2.5 创建全局 Manager (mgrGlobal)
    // 创建全局 Manager(不限制 namespace)
    // 用于监听跨 namespace 的网络资源
    mgrGlobal, err := ctrl.NewManager(restConfig, ctrl.Options{
        Scheme:  scheme,
        Metrics: server.Options{BindAddress: "0"},  // 禁用 metrics(避免端口冲突)
    })
    if err != nil {
        setupLog.Error(err, "unable to start global manager")
        os.Exit(1)
    }

BindAddress: "0" 的含义: 设置为 “0” 表示禁用 metrics server。因为 mgr 已经在 :8080 上暴露了 metrics,mgrGlobal 不需要重复暴露。

3.2.6 Field 索引注册(3 个)
    // 为 SriovNetwork 创建 field index: "spec.networkNamespace"
    // 这个索引允许通过 networkNamespace 快速查找所有指向同一 namespace 的 SriovNetwork
    err = mgrGlobal.GetCache().IndexField(context.Background(),
        &sriovnetworkv1.SriovNetwork{},      // 目标 CRD 类型
        "spec.networkNamespace",              // 索引字段名
        func(o client.Object) []string {
            // 提取函数:返回该对象的 networkNamespace 值
            return []string{o.(*sriovnetworkv1.SriovNetwork).Spec.NetworkNamespace}
        })
    if err != nil {
        setupLog.Error(err, "unable to create index field for cache")
        os.Exit(1)
    }

Field Index 的作用: controller-runtime 的 cache 默认只能按 namespace 和 name 索引对象。通过 IndexField,我们创建了一个自定义索引,使得控制器可以通过 client.MatchingFields{"spec.networkNamespace": "my-ns"} 快速查询所有指向特定 namespace 的 SriovNetwork。这对网络资源到 namespace 的映射至关重要。

同样的模式重复两次,分别为 SriovIBNetworkOVSNetwork 创建索引:

    // SriovIBNetwork 的 networkNamespace 索引
    err = mgrGlobal.GetCache().IndexField(context.Background(),
        &sriovnetworkv1.SriovIBNetwork{}, "spec.networkNamespace",
        func(o client.Object) []string {
            return []string{o.(*sriovnetworkv1.SriovIBNetwork).Spec.NetworkNamespace}
        })
    // ... 错误处理 ...

    // OVSNetwork 的 networkNamespace 索引
    err = mgrGlobal.GetCache().IndexField(context.Background(),
        &sriovnetworkv1.OVSNetwork{}, "spec.networkNamespace",
        func(o client.Object) []string {
            return []string{o.(*sriovnetworkv1.OVSNetwork).Spec.NetworkNamespace}
        })
    // ... 错误处理 ...

Field Index 示意

ns-a

ns-a

ns-b

SriovNetwork: net1
networkNamespace=ns-a

SriovNetwork: net2
networkNamespace=ns-a

SriovNetwork: net3
networkNamespace=ns-b

Index: spec.networkNamespace

查询: networkNamespace=ns-a

返回 [net1, net2]

3.2.7 NIC ID 映射初始化
    // 从 ConfigMap 加载支持的 NIC ID 列表
    // 这个 ConfigMap 包含了所有经过测试的 NIC 的 PCI Vendor:Device ID
    if err := initNicIDMap(); err != nil {
        setupLog.Error(err, "unable to init NicIdMap")
        os.Exit(1)
    }

initNicIDMap 函数实现:

func initNicIDMap() error {
    // 创建一个原始的 Kubernetes client(非 controller-runtime client)
    // NewForConfigOrDie 在失败时 panic
    kubeclient := kubernetes.NewForConfigOrDie(ctrl.GetConfigOrDie())
    // 从 operator namespace 中的 ConfigMap 读取 NIC ID 数据
    if err := sriovnetworkv1.InitNicIDMapFromConfigMap(kubeclient, vars.Namespace); err != nil {
        return err
    }
    return nil
}

NIC ID Map 的作用: SR-IOV 只支持特定的网卡型号。这个 ConfigMap 包含了所有支持的 PCI Vendor:Device ID 组合(如 8086:1583 对应 Intel XXV710)。在运行时,Daemon 会检查节点上的网卡 PCI ID 是否在支持列表中。

3.2.8 全局变量赋值
    // 将 REST 配置和 Scheme 存入全局变量
    // 其他包(如 daemon、helper)通过 vars.Config 和 vars.Scheme 访问
    vars.Config = restConfig
    vars.Scheme = mgrGlobal.GetScheme()

    // 创建平台检测器
    // 用于判断运行在 OpenShift 还是原生 Kubernetes 上
    platformsHelper, err := platforms.NewDefaultPlatformHelper()
    if err != nil {
        setupLog.Error(err, "couldn't create openshift context")
        os.Exit(1)
    }

    // 创建特性门控实例
    // FeatureGate 用于控制新特性的启用/禁用
    featureGate := featuregate.New()
3.2.9 注册全局控制器(3 个)
    // 1. SriovNetworkReconciler — 管理 SriovNetwork CR
    //    负责创建/更新/删除 NetworkAttachmentDefinition
    //    使用 mgrGlobal 的 client(跨 namespace)
    if err = (&controllers.SriovNetworkReconciler{
        Client: mgrGlobal.GetClient(),
        Scheme: mgrGlobal.GetScheme(),
    }).SetupWithManager(mgrGlobal); err != nil {
        setupLog.Error(err, "unable to create controller", "controller", "SriovNetwork")
        os.Exit(1)
    }

    // 2. SriovIBNetworkReconciler — 管理 SriovIBNetwork CR
    //    类似 SriovNetwork,但针对 InfiniBand 网络
    if err = (&controllers.SriovIBNetworkReconciler{
        Client: mgrGlobal.GetClient(),
        Scheme: mgrGlobal.GetScheme(),
    }).SetupWithManager(mgrGlobal); err != nil {
        setupLog.Error(err, "unable to create controller", "controller", "SriovIBNetwork")
        os.Exit(1)
    }

    // 3. OVSNetworkReconciler — 管理 OVSNetwork CR
    //    管理 OVS(Open vSwitch)网络资源
    if err = (&controllers.OVSNetworkReconciler{
        Client: mgrGlobal.GetClient(),
        Scheme: mgrGlobal.GetScheme(),
    }).SetupWithManager(mgrGlobal); err != nil {
        setupLog.Error(err, "unable to create controller", "controller", "OVSNetwork")
        os.Exit(1)
    }
3.2.10 注册命名空间控制器(3 个)
    // 4. SriovNetworkNodePolicyReconciler — 管理 SriovNetworkNodePolicy CR
    //    策略资源:定义哪些节点应该配置哪些 PF/VF
    //    使用 mgr 的 client(仅 namespace 级别)
    if err = (&controllers.SriovNetworkNodePolicyReconciler{
        Client:      mgr.GetClient(),
        Scheme:      mgr.GetScheme(),
        FeatureGate: featureGate,  // 注入特性门控
    }).SetupWithManager(mgr); err != nil {
        setupLog.Error(err, "unable to create controller", "controller", "SriovNetworkNodePolicy")
        os.Exit(1)
    }

    // 5. SriovOperatorConfigReconciler — 管理 SriovOperatorConfig CR
    //    Operator 自身的配置资源(日志级别、特性门控等)
    //    注入 PlatformHelper 和 UncachedAPIReader
    if err = (&controllers.SriovOperatorConfigReconciler{
        Client:            mgr.GetClient(),
        Scheme:            mgr.GetScheme(),
        PlatformHelper:    platformsHelper,
        FeatureGate:       featureGate,
        UncachedAPIReader: mgr.GetAPIReader(),  // 不经过缓存的直接 API reader
    }).SetupWithManager(mgr); err != nil {
        setupLog.Error(err, "unable to create controller", "controller", "SriovOperatorConfig")
        os.Exit(1)
    }

    // 6. SriovNetworkPoolConfigReconciler — 管理 SriovNetworkPoolConfig CR
    //    节点池配置:定义一组节点的 SR-IOV 配置
    if err = (&controllers.SriovNetworkPoolConfigReconciler{
        Client:         mgr.GetClient(),
        Scheme:         mgr.GetScheme(),
        PlatformHelper: platformsHelper,
    }).SetupWithManager(mgr); err != nil {
        setupLog.Error(err, "unable to create controller", "controller", "SriovNetworkPoolConfig")
        os.Exit(1)
    }

UncachedAPIReader 的设计意图: mgr.GetAPIReader() 返回一个直接访问 API Server 的 reader,不经过本地缓存。这在需要读取最新数据时很重要——SriovOperatorConfig 的变更需要立即反映,不能容忍缓存的延迟。

3.2.11 Drain 控制器创建
    // 创建一个禁用缓存的 client,专门用于 Drain 控制器
    // Drain 操作需要读取最新的 NodeState、Node、MachineConfigPool 状态
    // 使用缓存可能导致 drain 决策基于过期数据
    drainKClient, err := client.New(restConfig, client.Options{
        Scheme: scheme,
        Cache: &client.CacheOptions{
            DisableFor: []client.Object{
                &sriovnetworkv1.SriovNetworkNodeState{},  // 节点状态必须实时
                &corev1.Node{},                           // 节点信息必须实时
                &mcfgv1.MachineConfigPool{},              // MCP 状态必须实时
            },
        },
    })
    if err != nil {
        setupLog.Error(err, "unable to create drain kubernetes client")
        os.Exit(1)
    }

    // 创建 Drain 控制器
    // 负责在配置变更前 drain 节点(驱逐 Pod),确保安全变更
    drainController, err := controllers.NewDrainReconcileController(
        drainKClient,
        mgr.GetScheme(),
        mgr.GetEventRecorderFor("SR-IOV operator"),  // 事件记录器
        platformsHelper)
    if err != nil {
        setupLog.Error(err, "unable to create controller", "controller", "DrainReconcile")
        os.Exit(1)
    }

    if err = drainController.SetupWithManager(mgr); err != nil {
        setupLog.Error(err, "unable to setup controller with manager", "controller", "DrainReconcile")
        os.Exit(1)
    }

Drain 控制器的禁用缓存策略

读取

读取

读取

实时查询

实时查询

实时查询

事件

DrainReconcileController

SriovNetworkNodeState
不缓存

Node
不缓存

MachineConfigPool
不缓存

kube-apiserver

EventRecorder
记录 drain 事件

3.2.12 双 Manager 启动
    // 设置信号处理器,监听 SIGINT/SIGTERM
    stopSignalCh := ctrl.SetupSignalHandler()

    // 为 globalManager 创建可取消的 context
    globalManagerErr := make(chan error)
    globalManagerCtx, globalManagerCancel := context.WithCancel(context.Background())
    go func() {
        setupLog.Info("starting global manager")
        // 启动全局 Manager(阻塞,直到 context 取消)
        globalManagerErr <- mgrGlobal.Start(globalManagerCtx)
    }()

    // 为 namespacedManager 创建可取消的 context
    namespacedManagerErr := make(chan error)
    namespacedManagerCtx, namespacedManagerCancel := context.WithCancel(context.Background())
    go func() {
        setupLog.Info("starting namespaced manager")
        // 启动命名空间 Manager(阻塞,直到 context 取消)
        namespacedManagerErr <- mgr.Start(namespacedManagerCtx)
    }()

为什么用 goroutine? mgr.Start() 是阻塞调用,会一直运行直到 context 取消或出错。要在同一进程中运行两个 Manager,必须在各自的 goroutine 中启动。

3.2.13 Shutdown Client 创建
    // 创建一个专用于关闭流程的 client
    // 禁用 SriovNetwork 缓存——需要读取最新的网络资源状态来清理
    shutdownClient, err := client.New(restConfig, client.Options{
        Scheme: vars.Scheme,
        Cache: &client.CacheOptions{
            DisableFor: []client.Object{
                &sriovnetworkv1.SriovNetwork{},
            },
        },
    })
    if err != nil {
        setupLog.Error(err, "unable to create generic client for shutdown process")
        os.Exit(1)
    }
3.2.14 优雅关闭 select 循环
    select {
    // 情况1:收到终止信号(SIGINT/SIGTERM)
    case <-stopSignalCh.Done():
        setupLog.Info("Stop signal received")
        // 取消两个 Manager 的 context
        globalManagerCancel()
        namespacedManagerCancel()
        // 等待两个 Manager 完全停止
        <-globalManagerErr
        <-namespacedManagerErr
        // 执行清理(清理 webhook、daemonset 等资源)
        utils.Shutdown(shutdownClient)

    // 情况2:全局 Manager 出错
    case err := <-globalManagerErr:
        setupLog.Error(err, "Global Manager error")
        // 取消命名空间 Manager
        namespacedManagerCancel()
        <-namespacedManagerErr
        utils.Shutdown(shutdownClient)
        os.Exit(1)  // 出错退出码 1

    // 情况3:命名空间 Manager 出错
    case err := <-namespacedManagerErr:
        setupLog.Error(err, "Namsepaced Manager error")
        // 取消全局 Manager
        globalManagerCancel()
        <-globalManagerErr
        utils.Shutdown(shutdownClient)
        os.Exit(1)
    }
utils.Shutdownmgr (namespaced)mgrGlobalmain.goKubernetesutils.Shutdownmgr (namespaced)mgrGlobalmain.goKubernetes正常退出 (exit 0)SIGTERMglobalManagerCancel()namespacedManagerCancel()globalManagerErr (stopped)namespacedManagerErr (stopped)utils.Shutdown(shutdownClient)清理 Webhook 配置清理 DaemonSet清理 ClusterRole/Binding

3.3 控制器注册全景图

CRD 层

mgr (命名空间 Manager)

mgrGlobal (全局 Manager)

生成

SriovNetworkReconciler
管理 SriovNetwork CR
→ 创建/更新 NAD

SriovIBNetworkReconciler
管理 SriovIBNetwork CR
→ 创建/更新 IB NAD

OVSNetworkReconciler
管理 OVSNetwork CR
→ 创建/更新 OVS NAD

SriovNetworkNodePolicyReconciler
管理 SriovNetworkNodePolicy CR
→ 生成 SriovNetworkNodeState

SriovOperatorConfigReconciler
管理 SriovOperatorConfig CR
→ 配置 Operator 行为

SriovNetworkPoolConfigReconciler
管理 SriovNetworkPoolConfig CR
→ 配置节点池

DrainReconcileController
管理节点 Drain
→ 安全驱逐 Pod

SriovNetwork

SriovIBNetwork

OVSNetwork

SriovNetworkNodePolicy

SriovOperatorConfig

SriovNetworkPoolConfig

SriovNetworkNodeState


四、Mermaid 图表汇总

4.1 模块架构图

K8s 生态

依赖模块

main.go 架构

注册类型

创建 Manager

注册控制器

初始化

读取 ConfigMap

通信

informer/cache

init()
Scheme 注册

main()
入口函数

initNicIDMap()
NIC ID 初始化

vars
全局变量

snolog
日志系统

featuregate
特性门控

platforms
平台检测

controllers
控制器实现

utils
工具函数

controller-runtime

k8s.io/client-go

kube-apiserver

4.2 类关系图

mgr, mgrGlobal

注册

注册

注册

注册

注册

注册

注册

main_package

+init()

+main()

-initNicIDMap() : error

Manager

+Start(ctx) : error

+GetClient() : client.Client

+GetScheme() : *runtime.Scheme

+GetCache() : cache.Cache

+GetAPIReader() : client.Reader

+GetEventRecorderFor(name) : record.EventRecorder

+AddHealthzCheck(name, check) : error

+AddReadyzCheck(name, check) : error

Reconciler

+Client client.Client

+Scheme *runtime.Scheme

+SetupWithManager(mgr) : error

SriovNetworkReconciler

+Client

+Scheme

SriovIBNetworkReconciler

+Client

+Scheme

OVSNetworkReconciler

+Client

+Scheme

SriovNetworkNodePolicyReconciler

+Client

+Scheme

+FeatureGate

SriovOperatorConfigReconciler

+Client

+Scheme

+PlatformHelper

+FeatureGate

+UncachedAPIReader

SriovNetworkPoolConfigReconciler

+Client

+Scheme

+PlatformHelper

DrainReconcileController

+NewDrainReconcileController()

+SetupWithManager(mgr) : error

4.3 启动序列图

mgr goroutinemgrGlobal goroutinekube-apiservercontroller-runtimemain.go操作系统mgr goroutinemgrGlobal goroutinekube-apiservercontroller-runtimemain.go操作系统par[并行运行]启动进程init() 注册 Scheme解析 flags初始化日志ctrl.GetConfigOrDie()读取 in-cluster configREST ConfigrestConfig检查 RESOURCE_PREFIXctrl.NewManager (mgr)mgrmgr.AddHealthzCheck/AddReadyzCheckctrl.NewManager (mgrGlobal)mgrGlobalmgrGlobal.GetCache().IndexField × 3initNicIDMap()读取 device-plugin-config ConfigMapNIC ID 数据创建 PlatformHelper创建 FeatureGate注册 6 个 Reconciler + Drain 控制器创建 shutdown clientgo mgrGlobal.Start()go mgr.Start()select {等待信号/错误}监听全局资源监听 namespace 资源SIGTERMcancel()cancel()stoppedstoppedutils.Shutdown (清理)exit(0)

4.4 错误处理流程图

os.Exit 1

os.Exit 1

os.Exit 1

os.Exit 1

os.Exit 1

os.Exit 1

os.Exit 1

os.Exit 1

os.Exit 1

os.Exit 1

os.Exit 1

os.Exit 1

os.Exit 1

RESOURCE_PREFIX 为空

程序退出

NewManager 失败

AddHealthzCheck 失败

AddReadyzCheck 失败

NewManager Global 失败

IndexField 失败

initNicIDMap 失败

PlatformHelper 创建失败

Reconciler 注册失败

Drain 控制器创建失败

Shutdown Client 创建失败

收到 SIGTERM

取消两个 Manager Context

等待两个 Manager 停止

utils.Shutdown 清理

正常退出

mgrGlobal 运行出错

取消 mgr Context

等待 mgr 停止

utils.Shutdown

mgr 运行出错

取消 mgrGlobal Context

等待 mgrGlobal 停止

utils.Shutdown

4.5 资源监听范围对比图

mgrGlobal (全局 Manager)

监听

监听

监听

索引

Cache 范围: 所有 namespaces

SriovNetwork - 所有 ns

SriovIBNetwork - 所有 ns

OVSNetwork - 所有 ns

spec.networkNamespace 索引

mgr (命名空间 Manager)

监听

监听

监听

监听

监听

监听

监听

Cache 范围: operator namespace only

SriovNetworkNodePolicy

SriovOperatorConfig

SriovNetworkPoolConfig

SriovNetworkNodeState

Node

MachineConfigPool

Pod (drain)

4.6 Controller 依赖注入图

注入到命名空间控制器的依赖

mgr.GetClient

SriovNetworkNodePolicyReconciler

SriovOperatorConfigReconciler

SriovNetworkPoolConfigReconciler

mgr.GetScheme

FeatureGate

PlatformHelper

mgr.GetAPIReader
Uncached

mgr.GetEventRecorderFor

DrainReconcileController

drainKClient
禁用缓存

mgr.GetScheme

PlatformHelper

注入到全局控制器的依赖

mgrGlobal.GetClient

SriovNetworkReconciler

SriovIBNetworkReconciler

OVSNetworkReconciler

mgrGlobal.GetScheme


五、关键设计模式总结

5.1 双 Manager 模式

Operator 使用两个 Manager 实例分离关注点:全局网络资源 vs 命名空间级配置资源。这是 controller-runtime 中的高级模式,适用于需要跨命名空间监听部分资源但又想限制其他资源缓存范围的场景。

5.2 依赖注入模式

所有 Reconciler 都通过结构体字段接收依赖(Client、Scheme、FeatureGate 等),而非全局变量。这使得控制器可测试——在单元测试中可以注入 mock client。

5.3 优雅关闭模式

通过 context.WithCancel + chan error + select 实现优雅关闭。无论哪个 Manager 出错,都会取消另一个并执行 utils.Shutdown 清理。

5.4 分层缓存策略

  • mgr: namespace 级缓存(减少内存)
  • mgrGlobal: 全局缓存 + Field 索引(快速跨 ns 查询)
  • drainKClient: 禁用缓存(实时数据)
  • shutdownClient: 部分禁用缓存(清理需要实时数据)

5.5 错误处理策略

  • 初始化阶段:os.Exit(1) 快速失败
  • 运行时阶段:通过 channel 传播错误,优雅关闭
  • 重复注册 GCP auth:兼容性考虑,确保不同 K8s 发行版都能正常认证
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值