Overview

Traces the complete network setup sequence for a new pod — from kubelet creating a network namespace through CNI plugin invocation, IPAM address allocation, veth pair creation, and the pod having a routable IP.

CNI Architecture

kubelet is NOT responsible for networking.
CNI plugins are responsible — kubelet calls them via the CNI spec interface.

Pod network setup:
  1. kubelet creates network namespace (pause container)
  2. kubelet calls CNI ADD with netns path + config
  3. CNI plugin configures: IP, routes, veth pair, iptables/BPF
  4. kubelet starts app containers in the configured namespace

CNI plugin location:
  /opt/cni/bin/<plugin>        — executables
  /etc/cni/net.d/<config>.conf — JSON configuration

CNI call interface:
  Environment variables:
    CNI_COMMAND    = ADD | DEL | CHECK | VERSION
    CNI_CONTAINERID = pause container ID
    CNI_NETNS      = /proc/<pid>/ns/net (or /var/run/netns/...)
    CNI_IFNAME     = eth0
    CNI_PATH       = /opt/cni/bin
  stdin: JSON config (from /etc/cni/net.d/)

Full CNI Setup Sequence — Cilium

kubelet          containerd      CNI Plugin (Cilium)    Cilium Agent    Linux Kernel
   │                 │                  │                    │               │
   │  [Pod scheduled to this node]      │                    │               │
   │                 │                  │                    │               │
   │─RunPodSandbox──►│                  │                    │               │
   │  (create pause  │                  │                    │               │
   │   container)    │                  │                    │               │
   │                 │                  │                    │               │
   │                 │──clone(NEWNET) ──────────────────────────────────────►│
   │                 │  (create network namespace)           │               │
   │                 │◄── netns: /proc/12345/ns/net ─────────────────────────│
   │                 │                  │                    │               │
   │◄── SandboxID ───│                  │                    │               │
   │                 │                  │                    │               │
   │  [kubelet calls CNI ADD]           │                    │               │
   │──CNI_ADD ───────────────────────►  │                    │               │
   │  CNI_COMMAND=ADD                   │                    │               │
   │  CNI_CONTAINERID=abc123            │                    │               │
   │  CNI_NETNS=/proc/12345/ns/net      │                    │               │
   │  CNI_IFNAME=eth0                   │                    │               │
   │  stdin: cilium CNI config          │                    │               │
   │                 │                  │                    │               │
   │                 │          ┌─── Cilium CNI plugin ───────────────────┐  │
   │                 │          │  1. Contact cilium-agent via UNIX socket │  │
   │                 │          │     /var/run/cilium/cilium.sock          │  │
   │                 │          └──────────────────────────────────────────┘  │
   │                 │                  │──CmdAdd RPC ──────►│               │
   │                 │                  │  (containerID,     │               │
   │                 │                  │   netns, ifName)   │               │
   │                 │                  │                    │               │
   │                 │                  │           ┌─── Cilium Agent ──────┐│
   │                 │                  │           │  Allocate IP from IPAM││
   │                 │                  │           │  (via CiliumNode CRD  ││
   │                 │                  │           │   or AWS ENI pool)    ││
   │                 │                  │           │  → 10.0.1.47/32       ││
   │                 │                  │           └───────────────────────┘│
   │                 │                  │                    │──ip addr add──►│
   │                 │                  │                    │  10.0.1.47     │
   │                 │                  │                    │  in netns      │
   │                 │                  │                    │               │
   │                 │                  │                    │──ip link add──►│
   │                 │                  │                    │  veth pair:    │
   │                 │                  │                    │  lxcXXX (host) │
   │                 │                  │                    │  eth0 (pod ns) │
   │                 │                  │                    │               │
   │                 │                  │                    │──BPF programs─►│
   │                 │                  │                    │  attached to   │
   │                 │                  │                    │  lxcXXX        │
   │                 │                  │                    │  (tc ingress + │
   │                 │                  │                    │   egress)      │
   │                 │                  │                    │               │
   │                 │                  │◄── CmdAdd OK ──────│               │
   │                 │                  │  {IP: 10.0.1.47,   │               │
   │                 │                  │   GW: 10.0.1.1,    │               │
   │                 │                  │   routes: [...]}   │               │
   │◄── CNI result ──────────────────── │                    │               │
   │  {ips:[{address:"10.0.1.47/32"}]}  │                    │               │
   │                 │                  │                    │               │
   │  [kubelet writes pod.status.podIP = 10.0.1.47]         │               │
   │  [kubelet starts app containers in the namespace]       │               │

Network Namespace — What Gets Created

Inside pod network namespace (after CNI ADD):

Interfaces:
  eth0      — veth end, IP 10.0.1.47/32
  lo        — loopback 127.0.0.1

Routes:
  default via 169.254.1.1 dev eth0
  169.254.1.1 dev eth0 scope link    (Cilium gateway IP, ARP proxied)

On host (node):
  lxcXXXXXX  — veth peer (host end)
  BPF programs attached:
    tc filter ingress → cilium_to_container
    tc filter egress  → cilium_from_container

Traffic flow (pod → pod on different node):
  pod eth0 → kernel routes to 169.254.1.1
  → BPF at lxcXXX: lookup destination in tunnel/route map
  → VXLAN/Geneve encapsulation OR direct routing (depends on mode)
  → node NIC → wire → destination node NIC → BPF decap → destination pod

IPAM Modes

Cluster-scope IPAM (default Cilium):
  - Cilium agent manages a pool of pod CIDRs per node
  - Assigned via CiliumNode CRD: .spec.ipam.podCIDRs
  - No cloud API calls per pod IP
  - Fast allocation (in-memory pool)

AWS ENI IPAM (Cilium ENI mode / aws-vpc-cni):
  - Each pod gets a real VPC IP from an ENI secondary IP
  - Slower: requires EC2 API calls to warm IP pool
  - Benefit: pods are routable in VPC without overlay

AWS VPC CNI (amazon-vpc-cni / aws-node DaemonSet):
  - IPAMD daemon manages ENI IP pools per node
  - L-IPAM: warm pool pre-allocates IPs in background
  - Pod gets VPC IP → routable natively in AWS VPC, to RDS, etc.

CNI Configuration Files

// /etc/cni/net.d/05-cilium.conf (Cilium)
{
  "cniVersion": "0.3.1",
  "name": "cilium",
  "type": "cilium-cni",
  "enable-debug": false
}
// Cilium CNI binary delegates everything to cilium-agent
// via UNIX socket — config is minimal

// /etc/cni/net.d/10-aws.conf (aws-vpc-cni)
{
  "cniVersion": "0.3.1",
  "name": "aws-cni",
  "type": "aws-cni",
  "vethPrefix": "eni",
  "mtu": 9001,
  "pluginLogLevel": "DEBUG",
  "pluginLogFile": "/var/log/aws-routed-eni/plugin.log"
}

// /etc/cni/net.d/10-flannel.conf (Flannel — no NetworkPolicy)
{
  "cniVersion": "0.3.1",
  "name": "cbr0",
  "plugins": [
    {
      "type": "flannel",
      "delegate": {
        "hairpinMode": true,
        "isDefaultGateway": true
      }
    },
    {
      "type": "portmap",
      "capabilities": {"portMappings": true}
    }
  ]
}

Pod Deletion — CNI DEL Sequence

kubelet          CNI Plugin         Cilium Agent    Linux Kernel
   │                 │                   │               │
   │  [Pod deleted]  │                   │               │
   │──CNI_DEL ──────►│                   │               │
   │  CNI_COMMAND=DEL│                   │               │
   │  CNI_CONTAINERID=abc123             │               │
   │  CNI_NETNS=/proc/12345/ns/net       │               │
   │                 │──CmdDel RPC ─────►│               │
   │                 │                   │               │
   │                 │          [Cilium agent:]           │
   │                 │          Remove BPF endpoint maps  │
   │                 │          Release IP 10.0.1.47 back to pool
   │                 │          Delete veth pair lxcXXX  ─────────►│
   │                 │          Remove from endpoint list │         │
   │                 │◄── DEL OK ────────│               │         │
   │◄── CNI DEL OK ──│                   │               │         │
   │                 │                   │               │         │
   │  [kubelet deletes network namespace]│               │──del ns─►│

Multi-Network Pods (Multus)

Multus is a "meta" CNI plugin that calls multiple CNI plugins:

/etc/cni/net.d/00-multus.conf
  → Multus reads Pod annotation: k8s.v1.cni.cncf.io/networks
  → Calls primary CNI (Cilium) → eth0 with cluster IP
  → Calls secondary CNI (SRIOV) → net1 with DPDK interface
  → Returns combined result

Use cases:
  - Storage traffic on separate interface (SRIOV)
  - SR-IOV high-throughput for NFV workloads
  - Telco 5G pods needing dedicated NIC

Debugging CNI Issues

# Pod stuck in ContainerCreating — check CNI
kubectl describe pod <pod> -n <ns>
# Events: Failed to create pod sandbox: rpc error: code = Unknown
#         failed to setup network for sandbox ... CNI plugin returned error

# Check CNI plugin logs (Cilium)
kubectl logs -n kube-system -l k8s-app=cilium --tail=50 | grep -i "cni\|ipam\|error"

# Check aws-vpc-cni IPAMD logs
kubectl logs -n kube-system -l k8s-app=aws-node -c aws-node --tail=50

# Verify CNI binary exists on node
kubectl debug node/<node-name> -it --image=ubuntu -- \
  ls /opt/cni/bin/

# Check CNI config
kubectl debug node/<node-name> -it --image=ubuntu -- \
  cat /etc/cni/net.d/05-cilium.conf

# Verify pod IP assigned
kubectl get pod <pod> -o jsonpath='{.status.podIP}'

# Cilium: check endpoint status for pod
CILIUM_POD=$(kubectl get pod -n kube-system -l k8s-app=cilium \
  --field-selector spec.nodeName=<node> \
  -o jsonpath='{.items[0].metadata.name}')
kubectl exec -n kube-system $CILIUM_POD -- cilium endpoint list

# Check IP allocation pool usage
kubectl exec -n kube-system $CILIUM_POD -- cilium ip list

# AWS VPC CNI: check ENI warm pool
kubectl exec -n kube-system <aws-node-pod> -- \
  /app/grpc-health-probe -addr=localhost:50051
kubectl logs -n kube-system <aws-node-pod> -c aws-node | grep "warm pool"