16.1 The Pod Network Model: Every Pod Gets Its Own IP
Alright, let’s get our hands dirty with the one networking concept Kubernetes absolutely nails: the Pod Network Model. Forget everything you know about Docker’s janky port-mapping circus. In Kubernetes, every pod—yes, every single pod—gets its own, bona fide IP address. This isn’t a suggestion; it’s the entire foundation of the network model, and it’s brilliant in its simplicity.
Think about it. If every pod has a unique IP on a flat network, your application doesn’t need to care about where it’s running. It just needs to know the IP of the pod it wants to talk to. No more wrestling with environment variables for port numbers or some convoluted discovery service just to find a neighboring container. A frontend pod can connect to a backend pod at 10.244.1.15:8080 just as easily as you can ping 8.8.8.8. This eliminates a massive class of network address translation (NAT) headaches and makes debugging a dream. You can curl a pod’s IP directly from any node, or even from your laptop if your network is set up right. It’s the network as it was meant to be: dumb, predictable, and transparent.
The Crucial Role of the CNI
So, how does this magic happen? It’s not Kubernetes itself. The schedulers and controllers are busy with other things. The actual grunt work of giving a pod its IP address and connecting it to the network is outsourced to a CNI plugin. CNI, the Container Network Interface, is a delightfully simple spec: a bunch of executables (typically written in Go) that the kubelet on each node calls at specific times, like when a pod is born or dies.
When a new pod is scheduled to a node, the kubelet sees it and says, “Right, a new pod for net=default.” It then looks at its --cni-conf-dir (usually /etc/cni/net.d/) to figure out which plugin to use for that network, grabs the pod’s namespace path, and executes the plugin’s ADD command with a JSON blob of configuration. The plugin’s job is to:
- Create a network interface (like a
vethpair). - Place one end inside the pod’s network namespace.
- Assign the IP address from its allocated pool.
- Set up routes.
- And finally, return the assigned IP and other details to the kubelet in a JSON response.
The kubelet then passes that IP to the control plane, and voilà, your pod is on the network. The beauty is that Kubernetes doesn’t care if the plugin is Calico, Cilium, Flannel, or something you cooked up in your garage over a weekend. As long as it adheres to the CNI spec, it’ll work.
Here’s a simplistic look at a CNI configuration file that tells the plugin what to do. This is what you’d find in /etc/cni/net.d/:
// /etc/cni/net.d/10-my-awesome-cni.conf
{
"cniVersion": "0.4.0",
"name": "awesomenet",
"type": "bridge", // This specifies the actual CNI plugin binary to call, e.g., /opt/cni/bin/bridge
"bridge": "cni0",
"isGateway": true,
"ipMasq": true,
"ipam": {
"type": "host-local", // The "IP Address Management" plugin that doles out IPs
"subnet": "10.244.1.0/24",
"routes": [
{ "dst": "0.0.0.0/0" }
]
}
}
The Reality of Multi-Homed Pods (The “Gotcha!”)
Here’s where the designers made a choice that causes endless confusion. The spec says “every pod gets its own IP,” but it subtly implies “only one IP.” The Kubernetes runtime only expects one IP address back from the CNI plugin. It shoves that single IP into the PodStatus and that’s what everything (like Services and Endpoints) uses for networking.
So what happens if your CNI plugin is an overachiever and assigns a pod two IPs? Tough. Kubernetes will pick one, seemingly at random, and ignore the other. This is the multi-homed pod problem, and it’s a fantastic way to spend an afternoon tearing your hair out. If your application desperately needs multiple interfaces, you need to use a CNI plugin like Multus that works as a meta-plugin, tricking Kubernetes by presenting only one “main” interface while adding additional ones on the side. It’s a clever hack, but it feels like one because it is.
Peeking Under the Hood: The veth Pair
Let’s get concrete. You create a pod. What actually appears? On the node, you can see the virtual ethernet (veth) device that the CNI plugin created. One end is in the pod’s network namespace, and the other is hanging out on the node’s root network namespace, typically plugged into a bridge.
Want to see it? Find your pod’s full ID with crictl or docker for older runtimes, then use nsenter to jump into its network namespace. Here’s how you can see the network devices from inside the pod’s isolated view:
# Find the container ID of a running pod (using containerd/runc)
sudo crictl ps --name my-app-pod
# Get the PID of the container
sudo crictl inspect <container-id> | grep pid
# Enter the network namespace of that PID and list the devices
sudo nsenter -n -t <PID> ip addr show
You’ll see the eth0 device inside the pod, with its precious Kubernetes IP. On the node’s root namespace (ip addr show), you’ll see its corresponding veth sibling, with a lovely name like vetha1b2c3d4. This is the plumbing that makes the magic work. Traffic from eth0 goes out through the veth pair, onto the node’s bridge, and from there, off to the rest of your cluster network.