分析 - --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 最低 │
└──────────────┴──────────────────────────────────────────────────────────────────────────────┘