Networking Deep Dive›04 · Your host's network model
Learning Hub / Kubernetes & Platform / Networking Deep Dive

Lesson 04 of 12 · Host & Wire

Your host's network model

Build pod networking by hand: a network namespace, a veth pair and a bridge, exactly the pieces a CNI plugin assembles for every pod, then find them on a real Kubernetes node.

Practitioner
Key wordsnetwork namespaceveth pairbridgepod networkingip netnsnsenterhostNetwork
node A · 192.168.1.11 pod a 10.244.1.5 veth pair eth0 ↔ vethXYZ node stack routes / bridge, CNI: overlay or BGP node B · 192.168.1.12 pod b 10.244.2.5 veth pair eth0 ↔ vethXYZ node stack routes / bridge, CNI: overlay or BGP physical network (VXLAN/Geneve or routed) encapsulated or routed
Pod to pod across nodes: veth pairs, the node stack and the CNI.

A pod's network, from first principles

Every pod gets its own network namespace: its own interfaces, IPs, routes and ports. The CNI plugin connects that namespace to the node with a veth pair (a virtual cable), and the node bridges or routes traffic onwards.

Each pod is a room with its own phone (network namespace). The CNI runs a phone cable (veth pair) from each room to a switchboard in the hallway (the bridge or the node's routing). Calls between rooms go through the switchboard; calls to other buildings go out through the building's main line.

Build a "pod network" by hand

On a lab VM (root required):

$ sudo ip netns add pod1
$ sudo ip netns add pod2
$ sudo ip link add br0 type bridge && sudo ip addr add 10.200.0.1/24 dev br0 && sudo ip link set br0 up

$ sudo ip link add veth-pod1 type veth peer name veth-host1
$ sudo ip link set veth-pod1 netns pod1
$ sudo ip link set veth-host1 master br0 && sudo ip link set veth-host1 up
$ sudo ip netns exec pod1 ip addr add 10.200.0.11/24 dev veth-pod1
$ sudo ip netns exec pod1 ip link set veth-pod1 up
$ sudo ip netns exec pod1 ip link set lo up
$ sudo ip netns exec pod1 ip route add default via 10.200.0.1

Repeat for pod2 with 10.200.0.12, then:

$ sudo ip netns exec pod1 ping -c1 10.200.0.12
64 bytes from 10.200.0.12: icmp_seq=1 ttl=64 time=0.061 ms
$ sudo ip netns exec pod1 ping -c1 10.200.0.1     # the "node"

To reach the internet, add IP forwarding and masquerading on the host: sysctl -w net.ipv4.ip_forward=1 and iptables -t nat -A POSTROUTING -s 10.200.0.0/24 ! -o br0 -j MASQUERADE. That's SNAT from lesson 02.

You've just built what a simple bridge CNI does for every pod.

If the pings fail on a machine with Docker

Docker sets the iptables FORWARD policy to DROP, and with br_netfilter loaded, bridged traffic passes through that chain. Use a VM without Docker, or allow the bridge explicitly: sudo iptables -I FORWARD -i br0 -j ACCEPT && sudo iptables -I FORWARD -o br0 -j ACCEPT.

What a CNI plugin does

When a pod starts, the kubelet asks the container runtime to create the pod sandbox. The runtime reads /etc/cni/net.d/ and runs the plugin binary from /opt/cni/bin/, which:

  1. Creates the veth pair; moves one end into the pod's namespace as eth0.
  2. Assigns an IP from the node's pod range (IPAM).
  3. Sets up routes, bridge membership or eBPF programs so traffic reaches other pods and the outside world.

Different CNIs make different choices here (bridges, pure routing, eBPF). That's lesson 05.

Find it on a real node

$ docker exec -it lab-worker bash                 # kind node
# ip -br link | grep veth
vethc4a1f2e3@if2   UP   …
# ls /etc/cni/net.d/
10-kindnet.conflist

From the node, enter a pod's network namespace with nsenter (find the PID with crictl, see Linux, lesson 14):

# PID=$(crictl inspect --output go-template --template '{{.info.pid}}' $(crictl ps --name web -q | head -1))
# nsenter -t "$PID" -n ip addr show eth0
# nsenter -t "$PID" -n ip route
default via 10.244.1.1 dev eth0

hostNetwork: no namespace at all

hostNetwork: true puts a pod straight into the node's network namespace: node IP, node ports, no isolation. CNI agents, some ingress controllers and node exporters use it. For applications it's a security and port-conflict risk, and Pod Security baseline forbids it.

Try it: build, then compare

  1. Build the two-namespace bridge network above and ping between the "pods".
  2. Add forwarding and masquerading and reach the internet from pod1 (ip netns exec pod1 ping -c1 1.1.1.1).
  3. In kind, find a pod's host-side veth: match the pod's eth0 interface index (cat /sys/class/net/eth0/iflink inside the pod) with ip link on the node.
  4. Compare ip addr inside a normal pod and a hostNetwork: true pod.
  5. Clean up your lab: sudo ip netns del pod1 pod2; sudo ip link del br0.

Going deeper: beyond veth

  • Multus attaches extra interfaces to pods (e.g. a second network for storage or telecom traffic) by chaining several CNIs.
  • macvlan/ipvlan give pods interfaces directly on the physical network; SR-IOV hands them a NIC virtual function for near-hardware performance.
  • eBPF CNIs (Cilium) still use veth pairs, but move forwarding and policy into eBPF programs attached to them, skipping bridges and iptables.

Recap

  • Pod = network namespace; connected by a veth pair; forwarded via a bridge or routes; NAT for egress.
  • The CNI plugin (config in /etc/cni/net.d, binaries in /opt/cni/bin) does this for every pod, including IPAM.
  • Debug from the node with nsenter -t <pid> -n.
  • hostNetwork shares the node's namespace. Avoid it for apps.

This site is a public version of my personal engineering knowledge hub. It intentionally excludes confidential company information and internal operational details.