Wiring a Pod via CNI

Who actually runs the CNI plugins, and in what order? The Kubelet asks the CRI runtime for a Pod sandbox, then the runtime, not the Kubelet, reads the plugin list from /etc/cni/net.d and runs each as an executable, handing every one after the first the previous result as prevResult. The bridge plugin delegates addressing to its own host-local IPAM plugin, the last plugin prints one result of interfaces, addresses, routes and DNS, and DEL runs the list in reverse.

Play the animation 6 steps, animated, best on a wide screen

Wiring a Pod via CNI diagram: CNI plugin invocation

Wiring a Pod via CNI, step by step

  1. The Kubelet asks the CRI runtime, here containerd, for a Pod sandbox. The runtime first creates the Pod network namespace, with loopback only and no Pod IP, runs the plugin chain inside that same RunPodSandbox call, and only then starts the pause container in it.

    Play from step 1
  2. The runtime, not the Kubelet, reads the plugin list from /etc/cni/net.d and runs the first plugin as a plain executable. CNI_COMMAND=ADD, the sandbox namespace path and CNI_IFNAME=eth0 arrive on its environment, and the network config arrives on stdin.

    Play from step 2
  3. The bridge plugin does not hand out addresses itself. Its config names an IPAM plugin, host-local, which it runs as a delegate of its own, and host-local picks 10.244.1.5 out of the range for this Node and returns it to the caller.

    Play from step 3
  4. The runtime then runs the next plugin in the list. It passes the previous result in as prevResult, so portmap adds to what bridge already produced instead of starting over. That ordered list is what the word chain means here.

    Play from step 4
  5. The last plugin prints one result on stdout: the interfaces the chain made, the addresses on them, the routes and the DNS. The one the chain made inside the sandbox namespace comes up as eth0 carrying 10.244.1.5, and the operation finishes as ADD ok.

    Play from step 5
  6. Only now does the Kubelet start the app containers, and they join the namespace that is already wired, so all of them answer on that one Pod IP. On delete the runtime runs the same list with CNI_COMMAND=DEL, in reverse order, and each plugin releases what it made.

    Play from step 6

Sources