The Container Network Interface (CNI) is the plugin specification that defines how networking is set up for Pods. The kubelet doesn't know how to configure networking — it delegates entirely to a CNI plugin. Understanding CNI is essential for troubleshooting node-level networking issues and making architectural decisions about cluster networking.

1. The CNI Specification

CNI is deliberately minimal — a spec for executing a binary that sets up (or tears down) networking for a container. The kubelet calls CNI at two points in a Pod's life:

OperationWhenWhat the Plugin Does
ADDPod sandbox createdAssign IP, create veth pair, configure routes, attach to bridge/overlay
DELPod sandbox destroyedRelease IP, remove network interface, clean up routes
CHECKPeriodic health checkVerify network setup is still correct

How It's Invoked

kubelet CRI calls exec CNI Binary /opt/cni/bin/calico stdin: config JSON Network Setup veth, bridge, routes IPAM: assign IP Pod online Binaries: /opt/cni/bin/ Config: /etc/cni/net.d/*.conflist

File Locations on a Node

# CNI plugin binaries:
ls /opt/cni/bin/
# bridge  calico  flannel  host-local  loopback  portmap  ...

# CNI configuration (kubelet reads first file alphabetically):
ls /etc/cni/net.d/
# 10-calico.conflist    (or 10-flannel.conflist, etc.)

CNI Config Example

# /etc/cni/net.d/10-calico.conflist
{
  "name": "k8s-pod-network",
  "cniVersion": "1.0.0",
  "plugins": [
    {
      "type": "calico",              // Plugin binary name
      "ipam": {
        "type": "calico-ipam"        // IP Address Management plugin
      },
      "policy": {
        "type": "k8s"
      }
    },
    {
      "type": "portmap",             // Chained plugin: port mapping for hostPort
      "capabilities": {"portMappings": true}
    },
    {
      "type": "bandwidth",           // Chained plugin: traffic shaping
      "capabilities": {"bandwidth": true}
    }
  ]
}
CNI plugins are chained. A conflist runs multiple plugins in sequence. The first (main) plugin creates the network. Additional plugins add features (port mapping, bandwidth limits, IPAM). Each plugin gets the previous one's output as input.

IPAM — IP Address Management

IPAM PluginHow It Assigns IPsUsed By
host-localAllocates from a local range per node (file-based state)Flannel, bridge
calico-ipamAllocates from Calico IP pools (etcd/datastore-backed)Calico
aws-cniAllocates real VPC IPs via ENI (Elastic Network Interface)AWS VPC CNI
whereaboutsCluster-wide IP allocation (for multi-network)Multus

2. CNI Plugins: Deep Comparison

Flannel — Simple Overlay

AspectDetail
Data planeVXLAN overlay (encapsulates L2 in UDP)
NetworkPolicy❌ None (must add Calico for policy)
RoutingEach node gets a /24 from cluster CIDR
PerformanceModerate (VXLAN overhead ~50 bytes/packet)
Best forLearning, simple clusters, when you don't need policies
Installkubectl apply -f kube-flannel.yml (one-liner DaemonSet)

Calico — Flexible & Enterprise-Ready

AspectDetail
Data planeLinux routing (BGP for cross-node) OR VXLAN OR eBPF
NetworkPolicy✅ Full K8s NetworkPolicy + Calico-specific extensions (L7, DNS)
RoutingBGP peering between nodes (no encap overhead) or VXLAN fallback
PerformanceExcellent with BGP (native routing); good with VXLAN
Advanced featuresGlobal NetworkPolicies, DNS-based policies, WireGuard encryption, Egress gateways
Best forBare metal, hybrid cloud, enterprise (most deployed CNI)
# Calico in BGP mode — no overlay, native routing:
# Node 1 announces: "10.244.1.0/24 via 192.168.1.10"
# Node 2 announces: "10.244.2.0/24 via 192.168.1.11"
# Traffic flows directly via learned routes — no encapsulation

Cilium — eBPF-Powered

AspectDetail
Data planeeBPF (bypasses iptables entirely)
NetworkPolicy✅ K8s NetworkPolicy + L7 policies (HTTP, gRPC, Kafka, DNS)
RoutingNative routing, VXLAN/Geneve overlay, or DSR (Direct Server Return)
PerformanceBest (eBPF: kernel-native, no iptables chain traversal)
Can replace kube-proxy✅ Yes (eBPF-based service load balancing)
Advanced featuresL7 visibility (HTTP metrics per path), Hubble observability, transparent encryption, service mesh, Gateway API
Best forHigh-performance clusters, observability-focused, platform engineering
eBPF is the key differentiator. Traditional CNIs (Flannel, Calico in iptables mode) program iptables rules — these are O(n) per packet and become a bottleneck at scale. Cilium programs eBPF maps in the kernel — O(1) lookups, no iptables, and programmable at L3-L7. It can replace kube-proxy entirely.

Head-to-Head Comparison

FeatureFlannelCalicoCilium
K8s NetworkPolicy
L7 Policy (HTTP)⚠️ Extension✅ Native
eBPF dataplane✅ (optional)✅ (primary)
Replace kube-proxy✅ (eBPF mode)
Encryption (WireGuard)
Multi-cluster✅ (Federation)✅ (Cluster Mesh)
ObservabilityBasicFlow logs✅ Hubble (L3-L7 flow viz)
ComplexityLowMediumMedium-High
Resource usageLowMediumMedium (eBPF is efficient at runtime)
Current industry trend (2024): Cilium is rapidly gaining adoption. It's the default CNI in GKE Dataplane v2, EKS Anywhere, and many platform engineering teams. Its observability (Hubble) and Gateway API support make it a "batteries-included" networking stack. Calico remains the safe enterprise choice with the largest existing install base.

3. eBPF — The Modern Data Plane

eBPF (extended Berkeley Packet Filter) lets you run sandboxed programs inside the Linux kernel without modifying kernel source or loading kernel modules. For networking, this means:

Traditional (iptables) vs eBPF

Traditional (iptables) Packet in PREROUTE FORWARD POSTROUTE Output O(n) rules traversed linearly per packet eBPF (Cilium) Packet in eBPF map lookup O(1) hash table Output Bypasses entire netfilter/iptables stack

What eBPF Enables in Cilium

FeatureHow eBPF Does It
Service load balancingeBPF map of ServiceIP → Pod IPs. Replaces kube-proxy entirely.
NetworkPolicyeBPF programs attached to Pod's veth enforce allow/deny per-packet
L7 visibilityeBPF parses HTTP/gRPC headers in-kernel — no proxy sidecar needed
Transparent encryptioneBPF triggers WireGuard/IPsec encryption at the interface level
Observability (Hubble)eBPF exports flow events (src, dst, protocol, HTTP status) in real-time
# Cilium without kube-proxy:
# Install Cilium with kube-proxy replacement:
helm install cilium cilium/cilium \
  --set kubeProxyReplacement=true \
  --set k8sServiceHost=API_SERVER_IP \
  --set k8sServicePort=6443

# Verify kube-proxy is not needed:
kubectl -n kube-system delete ds kube-proxy
# Cilium handles all Service routing via eBPF
Removing kube-proxy eliminates tens of thousands of iptables rules in large clusters. At scale (10,000+ Services), this dramatically improves Service routing latency and node CPU usage. GKE Dataplane v2 runs Cilium without kube-proxy by default.

4. CNI Troubleshooting

Common Issues

SymptomLikely CauseDebug Command
Pod stuck in ContainerCreatingCNI plugin failed to assign IP or create interfacekubectl describe pod → events; journalctl -u kubelet
Pods can't communicate cross-nodeOverlay not working or routes missingkubectl debug node/ -- ip route; CNI DaemonSet logs
All new Pods fail networkingCNI binary missing or config file absentls /opt/cni/bin/; ls /etc/cni/net.d/
IP exhaustionNode's Pod CIDR fullkubectl get nodes -o jsonpath='{.items[*].spec.podCIDR}'
NetworkPolicies not enforcedCNI doesn't support policies (Flannel)Check CNI docs for policy support

Key Debug Commands

# Check CNI DaemonSet health:
kubectl get pods -n kube-system -l k8s-app=calico-node   # Calico
kubectl get pods -n kube-system -l app.kubernetes.io/name=cilium  # Cilium

# CNI plugin logs:
kubectl logs -n kube-system -l k8s-app=calico-node --tail=50
kubectl logs -n kube-system -l app.kubernetes.io/name=cilium --tail=50

# Cilium-specific:
kubectl exec -n kube-system cilium-xxxxx -- cilium status
kubectl exec -n kube-system cilium-xxxxx -- cilium endpoint list

# Check node networking:
kubectl debug node/worker-1 -it --image=nicolaka/netshoot -- bash
  # Inside: ip route, ip link, bridge fdb, iptables-save

# Calico-specific:
kubectl exec -n kube-system calico-node-xxxxx -- calicoctl node status
kubectl exec -n kube-system calico-node-xxxxx -- calicoctl get ippool
CKA networking troubleshooting: if Pods are stuck in ContainerCreating with "network not ready" events, check: (1) Is the CNI DaemonSet running? (2) Are CNI binaries present at /opt/cni/bin/? (3) Is the config at /etc/cni/net.d/? A missing or misconfigured CNI is a common exam scenario.

5. Choosing a CNI for Your Cluster

# Decision tree:
Need NetworkPolicy?
├─ NO → Flannel (simplest, lowest resource)
└─ YES → Need L7 policies or replace kube-proxy?
          ├─ NO → Calico (battle-tested, BGP or VXLAN)
          └─ YES → Cilium (eBPF, L7 visibility, kube-proxy replacement)

Running on AWS EKS?
└─ Default: AWS VPC CNI (native VPC IPs, best integration)
   └─ Add Calico for NetworkPolicy enforcement

Running on GKE?
└─ Default: GKE Dataplane v2 (Cilium-based, eBPF)

Bare metal / on-prem?
└─ Calico with BGP (native routing, no overlay overhead)
   or Cilium (eBPF + Hubble observability)
Changing CNI on a running cluster is disruptive — it typically requires draining all nodes and reinstalling. Choose carefully upfront. If unsure, Calico is the safest bet (most documentation, largest user base, flexible modes). If you're building a new platform, Cilium is the modern choice.

Summary

ConceptKey Point
CNI specSimple binary interface: ADD (create network), DEL (remove), CHECK (verify)
File locationsBinaries: /opt/cni/bin/, Config: /etc/cni/net.d/
Plugin chainingconflist runs plugins in sequence (main + IPAM + extras)
FlannelVXLAN overlay, simple, no NetworkPolicy
CalicoBGP routing (or VXLAN), full NetworkPolicy, enterprise-grade
CiliumeBPF dataplane, L7 policy, replaces kube-proxy, Hubble observability
eBPFO(1) kernel-native processing, bypasses iptables, programmable
IPAMHandles IP allocation: host-local, calico-ipam, VPC ENI

📝 Quiz: CNI Fundamentals

Q1: A newly joined node's Pods are all stuck in ContainerCreating. What CNI-related things do you check first?

Check: (1) Is the CNI DaemonSet Pod running on that node? (kubectl get pods -n kube-system -o wide | grep <node>). (2) Are CNI binaries present? (ls /opt/cni/bin/). (3) Is the config file present? (ls /etc/cni/net.d/). (4) Check kubelet logs for CNI errors: journalctl -u kubelet | grep cni.

Q2: What's the advantage of Calico's BGP mode over VXLAN overlay?

No encapsulation overhead. BGP mode programs real routes between nodes — packets travel at native speed without being wrapped in UDP (no extra 50-byte VXLAN header). Lower latency, higher throughput, smaller MTU impact. The trade-off: requires infrastructure that supports BGP peering (routers, or nodes acting as BGP peers).

Q3: Why can Cilium replace kube-proxy but Flannel cannot?

Cilium uses eBPF programs that can intercept packets and perform Service load balancing (DNAT from ClusterIP to Pod IP) directly in the kernel — the same job kube-proxy does with iptables. Flannel only handles Pod-to-Pod connectivity (overlay) and doesn't implement Service routing at all — it still relies on kube-proxy for Services.

Q4: What is IPAM and why does it matter?

IP Address Management — the subsystem that assigns and tracks Pod IPs. It matters because: (1) IPs must be unique cluster-wide (no collisions). (2) Each node needs a pool of IPs to allocate from. (3) IPs must be released when Pods die. Different IPAM plugins have different trade-offs — host-local is simple (per-node range), calico-ipam is cluster-aware, and aws-cni allocates real VPC IPs.

Q5: How does eBPF improve over iptables for NetworkPolicy enforcement?

iptables evaluates rules linearly (O(n)) — adding NetworkPolicies means more rules every packet traverses. eBPF uses hash maps (O(1)) — policy lookup is constant-time regardless of how many policies exist. Additionally, eBPF can enforce L7 policies (HTTP path, gRPC method) without a proxy, while iptables only works at L3/L4.

Q6: You're on AWS EKS. Pods are getting IPs from the VPC subnet (172.31.x.x) instead of a Pod CIDR (10.244.x.x). Which CNI is being used and why?

AWS VPC CNI. It allocates real VPC IP addresses to Pods (from the node's subnet) by attaching secondary IPs to the node's Elastic Network Interface (ENI). Pods are directly routable within the VPC — no overlay needed. This means Pod IPs are "real" VPC IPs, which is why they come from the VPC CIDR range.