Skip to content

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

S1·E1Understand~20 minsources checked todayverified 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

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 situation · A Dell customer lab in Round Rock, day four of a thirty-day proof of concept

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]

Rendering diagram…
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 .-> N1
Two interfaces, two fabrics, two owners. Trace which box a symptom lives in before you name a suspect: a DNS failure is on the top path, a stalled NCCL all-reduce on the bottom one.

The 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.

primary
delegate
ipam
options
pod ns: default · securityContext IPC_LOCKeth0primary CNInet1macvlan-netMultus CNI (meta-plugin)thick: multus-daemon + multus-shimCalicoowns eth0 + policymacvlanIP-levelrdmanot chainedno device-plugin resourceIP-level onlywhereaboutsaddress assignmenthost: ens1f0 · VF 0000:03:02.3 · mlx5_core

Checks

no forbidden combination
selected block

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.

source
apiVersion: 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"
        ]
      }
    }
Operator route: MacvlanNetwork (mellanox.com/v1alpha1: networkNamespace, master, mode, mtu, ipam) generates the NAD for you. Network Operator deployment guide · Multus how-to-use · quick start
Build the simplest possible second interface: Calico primary, macvlan delegate, whereabouts IPAM. Read the three artifacts it emits - the NAD, the pod annotation, the resources block - and note which of them the device plugin is responsible for.

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

How it ended

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.

  1. Pre-flight inventory, before touching Kubernetes. Record all of it in your lab log:
    uname -r
    lspci | grep -i mellanox
    ip -br link
    ibstat | head -20
    ibv_devinfo | head -20
    Expected: the ConnectX or BlueField-3 functions appear in lspci, and ibstat names at least one HCA. If ibstat is missing or empty, the driver is not loaded and no Kubernetes work will help - stop and fix the host first.
  2. 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.
  3. 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.
  4. Now the check that matters. Find out which physical interface the primary CNI autodetected as its uplink:
    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
    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.
  5. 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]
  6. 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.

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.

14 flashcards for this lesson — 0 in deck. Spaced review lives at /review.

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.

  1. Calico - About Kubernetes networking · fetched 2026-09-07
  2. GitHub - Mellanox/network-operator (NVIDIA Network Operator) · fetched 2026-09-07
  3. GitHub - k8snetworkplumbingwg/multus-cni · fetched 2026-09-07
  4. NVIDIA Network Operator v26.7.0 - Deployment Guide with Kubernetes · fetched 2026-09-07
  5. NVIDIA Network Operator v26.7.0 - Platform Support · fetched 2026-09-07
  6. Kubernetes - Device Plugins · fetched 2026-09-07
  7. NVIDIA Network Operator v26.7.0 - Quick Start Guide for Kubernetes · fetched 2026-09-07
  8. NVIDIA-Certified Associate - AI Infrastructure and Operations Exam Study Guide (doc 4694224, Jan26) · fetched 2026-09-07
  9. Calico - Component architecture · fetched 2026-09-07

The same idea elsewhere

Other lessons that cover this ground, sometimes from another course's angle.