kubeovn non-primary-cni-mode

3 阅读3分钟

分析 - --non-primary-cni-mode=true 使用场景和相关代码作用位置和基本原理。 我想把 kube-ovn 以这种模式部署于一个 calico 作为 主 cni 的环境

官方文档



  ---
  --non-primary-cni-mode=true 完整分析

  一、结论先行

  核心定位:该模式让 kube-ovn 作为辅助 CNI(secondary CNI)运行,通过 Multus 为 Pod 提供额外的网络接口,而不干预主 CNI(如 Calico/Cilium)管理的 eth0。

  部署架构:
  Pod
  ├── eth0       ← Calico 管理(主 CNI)
  └── net1/net2  ← kube-ovn 管理(通过 Multus + NetworkAttachmentDefinition)

  二、代码级行为变更(5 个关键位置)

  1. Controller - Pod 网络分配(核心差异)

  pkg/controller/pod.go:448-460

  func (c *Controller) getPodKubeovnNets(pod *v1.Pod) ([]*kubeovnNet, error) {
      attachmentNets, err := c.getPodAttachmentNet(pod)
      podNets := attachmentNets
      // 关键分支
      if c.config.EnableNonPrimaryCNI {
          return podNets, nil  // 只返回 NAD 定义的网络,不添加默认子网
      }
      // 主 CNI 模式下,还会添加默认子网给 eth0
      defaultSubnet, _ := c.getPodDefaultSubnet(pod)
      podNets = append(attachmentNets, &kubeovnNet{...IsDefault: true})
      return podNets, nil
  }

  原理:主 CNI 模式下,kube-ovn 会为每个 Pod 的 eth0 分配默认子网 IP。non-primary 模式下,kube-ovn 只管理通过 NetworkAttachmentDefinition (NAD) 显式声明的附加网络接口,不碰 eth0。

  2. Controller - EndpointSlice IP 替换

  pkg/controller/endpoint_slice.go:134-148

  if c.config.EnableNonPrimaryCNI && serviceHasSelector(svc) {
      pods, _ := c.podsLister.Pods(namespace).List(labels.Set(svc.Spec.Selector).AsSelector())
      c.replaceEndpointAddressesWithSecondaryIPs(endpointSlices, pods)
  }

  原理:Kubernetes 默认用 eth0 的 IP 构建 EndpointSlice。但在 non-primary 模式下,如果 Service 要路由到 kube-ovn 管理的网络,endpoint 地址需要替换为辅助接口 IP(从 Pod 的
  ovn.kubernetes.io/xxx annotation 获取)。

  3. Controller - VPC NAT 网关接口发现

  pkg/controller/vpc_nat_gateway.go:348-380

  non-primary 模式下,NAT 网关的接口名不再是固定的 eth0,而是从 Pod 的 k8s.v1.cni.cncf.io/network-status annotation 中动态提取:
  - 从 NAD 名称映射到实际接口名(如 net1, net2)
  - 区分 VPC 内部接口和外部 NAD 接口

  pkg/controller/vpc_nat_gateway.go:926-932

  额外网络可通过 VpcNatGateway CR 的 annotation k8s.v1.cni.cncf.io/networks 指定。

  4. Daemon - 跳过 NetworkUnavailable 条件设置

  pkg/daemon/ovs_linux.go:725-740

  if !enableNonPrimaryCNI {
      // 设置 node condition: NetworkUnavailable = false/true
      util.SetNodeNetworkUnavailableCondition(cs, nodeName, status, reason, message)
  } else {
      klog.Infof("running in non-primary CNI mode, skipping NetworkUnavailable condition update")
  }

  原理:NetworkUnavailable condition 是 kubelet 判断节点网络是否就绪的依据。作为辅助 CNI,kube-ovn 不应设置这个条件——这是主 CNI(Calico)的职责。

  5. Daemon - 跳过 ovn0 健康检查

  pkg/daemon/ovs_linux.go:747-751

  func (c *Controller) loopOvn0Check() {
      if c.config.EnableNonPrimaryCNI {
          return  // 跳过 ovn0 检查,不会因 ovn0 down 而重启
      }
      // ...主 CNI 模式下,ovn0 down 会触发 fatal exit
  }

  原理:主 CNI 模式下 ovn0 是节点到 Pod 网络的关键路径,down 了必须重启修复。non-primary 模式下 ovn0 不承载主网络流量,不需要这么激进的自愈。

  三、部署架构总览

  ┌─────────────────────────────────────────────┐
  │                  Node                        │
  │                                              │
  │  ┌──────────┐  ┌──────────┐  ┌───────────┐ │
  │  │  Multus   │  │  Calico   │  │  kube-ovn  │ │
  │  │ (meta-CNI)│  │ (primary) │  │(secondary) │ │
  │  └──────────┘  └──────────┘  └───────────┘ │
  │       │              │              │        │
  │       │         eth0 (Pod)     net1/net2     │
  │       │              │              │        │
  │  调度 CNI 调用    管理主网络    管理附加网络    │
  │                  设置 Node     不设置 Node    │
  │                  Condition     Condition      │
  └─────────────────────────────────────────────┘

  Multus 是关键中间层:
  - Multus 作为 meta-plugin 安装在 /etc/cni/net.d/ 下(优先级最高)
  - kubelet 调用 Multus → Multus 先调用主 CNI (Calico) 创建 eth0 → 再根据 Pod 的 NAD annotation 调用 kube-ovn 创建附加接口

  四、你的 Calico + kube-ovn 部署建议

  参考 CI 中的部署流程(makefiles/kind.mk:843-858),步骤为:

  1. 安装 Calico 作为主 CNI(替代 CI 中的 Cilium)
  2. 安装 Multus:kubectl apply -f <multus-daemonset.yaml>
  3. 安装 kube-ovn,Helm values 关键配置:

  # kube-ovn-v2 chart
  cni:
    nonPrimaryCNI: true
    # CNI 配置文件优先级要低于 Calico(Calico 默认 10-calico.conflist)
    # kube-ovn 的配置文件名应类似 01-kube-ovn.conflist

  3. 或旧 chart:
  cni_conf:
    NON_PRIMARY_CNI: true
    CNI_CONF_FILE: "/kube-ovn/01-kube-ovn.conflist"

  3. CI 中还设置了 CNI_CONFIG_PRIORITY=10,确保 kube-ovn 的 CNI 配置文件不会抢占 Calico 的主 CNI 地位。
  4. 创建 NetworkAttachmentDefinition 来声明 kube-ovn 管理的网络:

  apiVersion: k8s.cni.cncf.io/v1
  kind: NetworkAttachmentDefinition
  metadata:
    name: ovn-vpc-net
    namespace: default
  spec:
    config: '{...kube-ovn CNI config...}'
  5. Pod 通过 annotation 请求附加网络:
  annotations:
    k8s.v1.cni.cncf.io/networks: ovn-vpc-net

  五、注意事项

  ┌──────────────┬──────────────────────────────────────────────────────────────────────────────┐
  │     方面     │                                     说明                                     │
  ├──────────────┼──────────────────────────────────────────────────────────────────────────────┤
  │ Service 路由 │ kube-ovn 管理的网络上的 Service 需要 endpoint IP 替换逻辑(代码已内置)      │
  ├──────────────┼──────────────────────────────────────────────────────────────────────────────┤
  │ NAT 网关     │ non-primary 模式支持 VPC NAT 网关,但接口发现依赖 network-status annotation  │
  ├──────────────┼──────────────────────────────────────────────────────────────────────────────┤
  │ 节点状态     │ kube-ovn 不会干预 Node 的 NetworkUnavailable condition                       │
  ├──────────────┼──────────────────────────────────────────────────────────────────────────────┤
  │ ovn0 接口    │ 仍会创建,但健康检查被跳过,不影响节点可用性判断                             │
  ├──────────────┼──────────────────────────────────────────────────────────────────────────────┤
  │ CNI 优先级   │ Multus 必须是 /etc/cni/net.d/ 下优先级最高的配置,Calico 其次,kube-ovn 最低 │
  └──────────────┴──────────────────────────────────────────────────────────────────────────────┘