The Kubernetes network model and where a second CNI fits
S1·E1Eight nodes, thirty days, one interface · A Dell customer lab in Round Rock, day four of a thirty-day proof of concept
Before you read: what do you already know?
3 quick questions. Wrong answers are fine and expected; trying first makes the lesson stick.
After this lesson you can
- State the four requirements of the Kubernetes network model and name which component satisfies each one.
- Explain why Kubernetes itself implements none of the model and what the CNI API actually plugs in.
- Describe the division of labour between the primary CNI and the Multus-attached secondary interface in an AI cluster.
- Name the four software pieces that build a pod's second interface and the order they run in.
Episode 1 — Eight nodes, thirty days, one interface
The customer builds same-day change-detection maps from aerial imagery, and racks two and three depend on one acceptance run finishing inside a contracted window. Eight PowerEdge GPU nodes, day four, the run crawling. Every pod is reachable, DNS is clean, NetworkPolicy is enforced. Their network lead has already filed a ticket against the CNI, grid-ruled notebook open at page one. The Dell SE beside you, coffee cold since the morning stand-up, keeps a spreadsheet with a row per promise; row 14 says “line rate on the rail”.
Nothing here is broken. Kubernetes promises four things - “Every pod gets its own IP address”, containers in a pod share it, “Pods can communicate with all other pods in the cluster using pod IP addresses (without NAT)”, and “Isolation … is defined using network policies” - and implements none of them itself; an implementation plugs in through the CNI API as a network plugin plus an IPAM plugin.[1] Read the list again for what is absent: bandwidth, latency, MTU, direct memory access. The model is a portability contract, so a fully compliant cluster can still be useless for training.
That is why a second network exists. RDMA and GPU east-west traffic rides a separate interface attached by Multus onto an SR-IOV VF, a host device or a macvlan child link, and NVIDIA’s Network Operator manages exactly that path - “Fast networking, RDMA and GPUDirect for workloads” - and nothing else.[2][4]
A healthy cluster and a fast cluster are two different achievements.
So start where the customer starts: those four sentences.
1Four requirements, and Kubernetes implements none of them
Start where a customer starts. The Kubernetes network model is four sentences, and Calico writes them out: “Every pod gets its own IP address”; “Containers within a pod share the pod IP address and can communicate freely with each other”; “Pods can communicate with all other pods in the cluster using pod IP addresses (without NAT)”; and “Isolation (restricting what each pod can communicate with) is defined using network policies”.[1]
Now the part that surprises people in a design review: Kubernetes itself implements none of it. Implementations plug in through the CNI API, and an implementation consists of two things - a network plugin that attaches pods to the network, and an IPAM plugin that assigns their addresses.[1] Service load balancing is a third, separate job, handled by kube-proxy, which spreads traffic from virtual IPs across the backing pods.[1]
Read the four requirements again with an AI cluster in mind and notice what is missing. Nothing in the model mentions bandwidth, latency, MTU, hardware queues, or direct memory access. The model is about reachability and isolation. A cluster can satisfy every requirement perfectly and still be useless for distributed training, because a 1500-byte software-switched veth pair is a correct implementation of “pods can communicate without NAT”.
That gap is the entire reason this course exists. The rest of the module is about the second network you add because the first one is doing a different job well.
2What the primary CNI owns
Whichever plugin you install - Calico, Cilium, OpenShift’s OVN-Kubernetes - it becomes the primary CNI. It owns eth0 inside every pod, and with it the Service ClusterIPs, cluster DNS, and NetworkPolicy enforcement.[1] Calico’s component list shows how much machinery that is: Felix “Programs routes and ACLs, and anything else required on the host to provide desired connectivity for the endpoints on that host”, BIRD distributes routes to BGP peers, confd regenerates BIRD’s configuration from the datastore, and Typha shields the datastore from a few hundred Felix instances.[9]
None of that machinery is in the path of an ibv_post_send. In an AI cluster the RDMA and GPU east-west traffic rides a secondary interface, attached by Multus onto an SR-IOV virtual function, a whole host device, or a macvlan or IPoIB child link - and it never goes through the primary CNI.[2][4]
Diagram source (Mermaid)
flowchart LR
subgraph POD[Training pod]
E0[eth0 from primary CNI]
N1[net1 from Multus delegate]
end
E0 --> CNI[Calico or Cilium datapath]
CNI --> SVC[Services / DNS / NetworkPolicy]
SVC --> MGMT[(Management fabric)]
N1 --> DEL[macvlan / host-device / SR-IOV VF]
DEL --> PF[ConnectX or BlueField-3 PF]
PF --> RAIL[(RDMA fabric)]
DP[Device plugin] -. advertises resource .-> N1The practical consequence for triage is a sorting rule, and it is worth internalising before any of the plumbing. Symptoms on the top path - pods cannot resolve a Service, a NetworkPolicy is not taking effect, a node’s pods are unreachable - belong to the primary CNI. Symptoms on the bottom path - a job’s collective stalls, ib_write_bw will not run between two pods, a second interface never appears - belong to Multus, the delegate CNI, the device plugin or the driver.[2][3]
The failure that breaks this rule is the one to remember: when the primary CNI has autodetected an RDMA-capable PF as its own uplink, both layers share one interface, and a routine Calico or Cilium reconfiguration takes the fast path down with it.[9]
3The four pieces that build the second interface
The second path is built by four cooperating pieces, and knowing the order they run in is most of the debugging skill.
Multus is first. “Multus CNI enables attaching multiple network interfaces to pods in Kubernetes” - it is a meta-plugin that delegates to other CNI plugins for every attachment beyond the cluster network.[3] It reads a pod annotation, resolves the named NetworkAttachmentDefinition, and calls the plugin named inside it.
The delegate CNI is second, and it is the piece that actually creates or moves an interface: macvlan, ipvlan, host-device, sriov, ib-sriov or ipoib. NVIDIA’s own quick start pairs them explicitly, offering “IPoIB with Shared Device” and “MacVLAN Network with Shared Device” against “SR-IOV with RDMA” and “Host Device with RDMA”.[7]
The IPAM plugin is third, named inside the delegate’s config, and it is a genuinely separate program.[1] Lesson 5 is about why host-local is the wrong answer for a cluster-wide fabric.
The device plugin runs beside all of this rather than in the chain. Device plugins let vendors advertise hardware to kubelet “without modifying Kubernetes core code”, using the resource naming convention vendor-domain/resourcetype, and the resources they advertise are integer-only, not overcommittable, and cannot be shared between containers.[6] That is what makes a pod’s resources block able to ask for a NIC at all, and why a pod that asks for a resource nobody advertises stays Pending forever with no CNI error to read.
Checks
no forbidden combination- infomacvlan mtu has range 0 to the master’s MTU — raise the PF first, then the pod. One master cannot be enslaved by macvlan and ipvlan at once. [macvlan mtu is capped at the master]
- infoCalico owns eth0 only. Confirm it has not autodetected ens1f0 as its uplink: sharing one interface with the fast path works until Felix reconfigures it, and then RDMA disappears. [primary CNI must not claim the fast-path interface]
Delegate — macvlan
Functions like a switch already connected to the host interface; each virtual interface gets a distinct MAC, so existing DHCP servers work.
- Required: name, type, ipam (may be empty for an address-less interface). Optional: master (defaults to the default-route interface), mode, mtu, linkInContainer.
- Modes: bridge (default), private, vepa, passthru. mtu range is 0 to the master’s MTU — you cannot raise a pod above the host interface.
- One master cannot be enslaved by macvlan and ipvlan at once; most wireless cards cannot be enslaved at all.
- Operator route: MacvlanNetwork (mellanox.com/v1alpha1: networkNamespace, master, mode, mtu, ipam) generates the NAD for you.
FAE angle: macvlan gives an IP-level second interface only. RDMA over it exists solely through the shared RDMA device plugin (rdma/rdma_shared_device_a) — a scheduling counter, not isolation.
sourceapiVersion: k8s.cni.cncf.io/v1
kind: NetworkAttachmentDefinition
metadata:
name: macvlan-net
namespace: default
spec:
config: |
{
"cniVersion": "0.3.1",
"name": "macvlan-net",
"type": "macvlan",
"master": "ens1f0",
"mode": "bridge",
"mtu": 1500,
"ipam": {
"type": "whereabouts",
"range": "192.168.2.225/28",
"exclude": [
"192.168.2.229/30",
"192.168.2.236/32"
]
}
}Above all of it sits the NVIDIA Network Operator, whose scope statement is a precise description of this second path and nothing else: it “leverages Kubernetes CRDs and Operator SDK to manage Networking related Components in order to enable Fast networking, RDMA and GPUDirect for workloads in a Kubernetes cluster”.[2] The components it can deploy are the pieces above - the DOCA-OFED driver container, the RDMA shared device plugin, the SR-IOV network device plugin, InfiniBand Kubernetes, Multus, the container networking plugins, the IPoIB CNI and NVIDIA’s own IPAM.[2] There is no primary CNI on that list, and that absence is the answer to the customer’s first question.
4Scope boundaries: what the hardware and the certification actually say
Two boundaries are worth fixing now because both come up in the first customer meeting.
The hardware boundary. Network Operator 26.7.0 supports Kubernetes >=1.32 and <=1.36, NICs from ConnectX-6 through ConnectX-9, and BlueField-3 DPU and SuperNIC in NIC mode only, up to 800 Gb/s.[5] The mode restriction matters directly in a Dell lab: a BlueField-3 sitting in DPU mode is outside the supported configuration for everything this course builds, and the fix is a mode change, not a CNI change.[5]
The certification boundary. This course maps against the NVIDIA-Certified Associate: AI Infrastructure and Operations blueprint, whose Domain 2 objectives include 2.5 “Identify key components and considerations of a cluster of an accelerated infrastructure” and 2.7 “Determine networking requirements for AI workloads”.[8] Be honest about the fit: the blueprint never names CNI, Multus, SR-IOV or the Network Operator anywhere, and the two domains this course touches least carry 60 percent of the exam.[8] This module goes far deeper than 2.5 and 2.7 ask. Treat it as over-serving a subset of the blueprint, not as exam preparation.
The port with two owners
You do not touch Calico. You ask which interface it autodetected on the GPU nodes, and there it is: ens1f0 is both the Calico uplink and the port the night-shift operator has already labelled for the rail - and Felix programs routes and ACLs on the interfaces it believes are its own.[9] The run is slow because the second network does not exist yet; the collective is going over the management path. Row 14 gets a note: the primary CNI is not the fast path. Then the platform lead asks the question that will fill the next meeting - if Calico is in the way, do we replace it with Cilium and switch on the fastest datapath it has?
Lab
Goal: the same inventory on the Dell BF-3 host, plus the one check that decides whether the rest of this module will work there. Read-only: no firmware change, no mode change, no interface reconfiguration.
- Pre-flight inventory, before touching Kubernetes. Record all of it in your lab log:
Expected: the ConnectX or BlueField-3 functions appear inuname -r lspci | grep -i mellanox ip -br link ibstat | head -20 ibv_devinfo | head -20lspci, andibstatnames at least one HCA. Ifibstatis missing or empty, the driver is not loaded and no Kubernetes work will help - stop and fix the host first. - Confirm the card is in a mode this course supports. Network Operator 26.7.0 supports BlueField-3 DPU and SuperNIC in NIC mode only.[5] Record which mode the card reports. If it is in DPU mode, note that as a blocker in your log; changing it is a separate mutating procedure that belongs to the DOCA course, not this lesson.
- Bring up a single-node cluster on the host (k3s or kind, whichever you already run) and install the same primary CNI you use at customer sites.
- Now the check that matters. Find out which physical interface the primary CNI autodetected as its uplink:
Expected: the interface carrying the node IP is your management NIC - not the RDMA-capable PF you intend to use for the fast path later.kubectl -n kube-system get ds calico-node -o yaml | grep -A3 IP_AUTODETECTION_METHOD ip -br addr show ip route get 1.1.1.1 - Write the finding down explicitly: “primary CNI uplink =
<iface>; intended RDMA PF =<iface>; same interface? yes/no”. A “yes” here is the single most valuable thing you can carry into a customer conversation, because it is a problem that produces no error message until the day it does.[9] - Repeat step 3 of the no-hardware lab inside a pod on this host and compare: one
eth0, one address, no second interface. That absence is what lesson 4 fixes.
Rollback: delete the pod and, if you created the cluster for this lab, uninstall it. No host interface, firmware or mode setting was changed at any step.
Goal: see all four model requirements satisfied by real artifacts on a single-node cluster, and write down which artifact satisfies which requirement. Nothing here mutates hardware.
- Create a single-node cluster with the default CNI disabled so you install Calico yourself:
Expected:kind create cluster --name k8snet-m1 --config - <<'EOF' kind: Cluster apiVersion: kind.x-k8s.io/v1alpha4 networking: disableDefaultCNI: true podSubnet: "192.168.0.0/16" EOFkubectl get nodesshows one node inNotReady. That is correct - there is no network plugin yet, which is the point of the lesson.[1] If not: check thatkindis on PATH and Docker is running. - Install Calico with the manifest install, then wait:
Expected: the node goeskubectl apply -f https://raw.githubusercontent.com/projectcalico/calico/v3.30.0/manifests/calico.yaml kubectl -n kube-system rollout status ds/calico-node --timeout=180sReady. If not:kubectl -n kube-system logs ds/calico-node -c calico-nodeand read the first error, not the last. - Run a pod and take the inventory:
Expected: exactly one non-loopback interface,kubectl run probe --image=nicolaka/netshoot --restart=Never -- sleep 3600 kubectl exec probe -- ip addr kubectl exec probe -- ip route kubectl exec probe -- cat /etc/resolv.confeth0, with one address from the pod CIDR, a default route through it, and the cluster DNS Service IP inresolv.conf. - Read the CNI configuration the kubelet actually used:
Expected: a conflist whose plugin list namesdocker exec k8snet-m1-control-plane ls -l /etc/cni/net.d/ docker exec k8snet-m1-control-plane cat /etc/cni/net.d/10-calico.conflistcalicoand, inside it, anipamblock namingcalico-ipam. Find both by name - that is the network plugin and the IPAM plugin the model requires, in one file.[1] - Prove requirement three by hand. Start a second pod, get its pod IP with
kubectl get pod -o wide, and from the first pod runcurlorpingto that address. Then from the second pod check what source address the first one arrived from. Expected: the source address equals the first pod’s own pod IP - no NAT.[1] - Write the deliverable. Four lines, one per model requirement, each naming the artifact from steps 3 to 5 that proves it. Then add a fifth line: which requirement, if any, says anything about how fast the interface is.
Rollback: kind delete cluster --name k8snet-m1.
Retrieval check
8 questions from memory. Answer before looking anything up; misses become flashcards.
Explain it to a Dell SE
Explain to a Dell SE, in four sentences, why an AI cluster has two networks per node instead of one, and which one their Calico or Cilium install is actually responsible for.
Sources
Facts in this lesson were checked against Calico latest docs, Multus CNI master README, NVIDIA Network Operator v26.7.0 platform-support and deployment guide, all re-fetched 2026-09-07. Dates are when each page was fetched.
- Calico - About Kubernetes networking · fetched 2026-09-07
- GitHub - Mellanox/network-operator (NVIDIA Network Operator) · fetched 2026-09-07
- GitHub - k8snetworkplumbingwg/multus-cni · fetched 2026-09-07
- NVIDIA Network Operator v26.7.0 - Deployment Guide with Kubernetes · fetched 2026-09-07
- NVIDIA Network Operator v26.7.0 - Platform Support · fetched 2026-09-07
- Kubernetes - Device Plugins · fetched 2026-09-07
- NVIDIA Network Operator v26.7.0 - Quick Start Guide for Kubernetes · fetched 2026-09-07
- NVIDIA-Certified Associate - AI Infrastructure and Operations Exam Study Guide (doc 4694224, Jan26) · fetched 2026-09-07
- Calico - Component architecture · fetched 2026-09-07
The same idea elsewhere
Other lessons that cover this ground, sometimes from another course's angle.
- RDMA in Kubernetes: the NVIDIA Network OperatorRoCE course · Same ground: certification, nv-ipam and bluefield
- SR-IOV plumbing: device plugin, CNI and operatorElsewhere in this course · Same ground: nv-ipam, Multus and versions
- Schedulers on top: MPI Operator, Run:ai and Slurm on KubernetesElsewhere in this course · Same ground: scheduling, fae and triage