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.
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:
- Creates the veth pair; moves one end into the pod's namespace as
eth0. - Assigns an IP from the node's pod range (IPAM).
- 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
- Build the two-namespace bridge network above and ping between the "pods".
- Add forwarding and masquerading and reach the internet from
pod1(ip netns exec pod1 ping -c1 1.1.1.1). - In kind, find a pod's host-side veth: match the pod's
eth0interface index (cat /sys/class/net/eth0/iflinkinside the pod) withip linkon the node. - Compare
ip addrinside a normal pod and ahostNetwork: truepod. - 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. hostNetworkshares 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.