Linux To Kubernetes Networking - Complete Mental Model
Kubernetes networking is not a separate networking stack.
Kubernetes uses Linux networking.
Everything eventually becomes:
Packet → Linux Kernel → Routing → Netfilter → Socket → Application
Kubernetes components such as CoreDNS, kube-proxy, Calico, Services, and Ingress only add rules and automation on top of Linux networking.
Suppose a packet arrives from the network cable.
The Network Interface Card (NIC) receives the Ethernet frame.
The NIC places the frame into its RX Ring Buffer. This buffer is memory that allows packets to be temporarily stored until the kernel can process them.
The NIC uses DMA (Direct Memory Access) to copy the packet directly into RAM without involving the CPU. This improves performance because the CPU does not need to manually move every packet.
After copying the packet to memory, the NIC notifies the kernel.
Modern Linux uses NAPI instead of processing every packet immediately. NAPI allows packets to be processed in batches, preventing interrupt storms during high traffic.
The kernel then creates an SKB (Socket Buffer).
The SKB is the most important structure in Linux networking.
An SKB contains:
Think of an SKB as the kernel's internal representation of a packet.
The kernel first examines the Ethernet header.
The Ethernet header contains:
The EtherType tells Linux what protocol is inside the frame.
Examples:
0x0800 = IPv4 0x86DD = IPv6 0x0806 = ARP
If EtherType indicates IPv4, Linux passes the packet to the IP layer.
At the IP layer Linux examines:
Source IP Destination IP
Example:
Source IP = 192.168.1.10 Destination IP = 192.168.241.141
Before Linux decides where to send the packet, Netfilter hooks are executed.
Netfilter is a framework inside the Linux kernel.
It allows firewall, NAT and packet modification logic.
The five major hooks are:
PREROUTING INPUT FORWARD OUTPUT POSTROUTING
This hook runs before Linux makes a routing decision.
Typical uses:
Example:
Packet arrives:
Destination = 10.96.0.25
kube-proxy may change:
10.96.0.25
to
172.16.212.50
before routing happens.
INPUT is used when the packet is meant for the local machine itself.
Examples:
SSH Destination Port 22
Kubelet Destination Port 10250
The packet eventually reaches a socket owned by a process running on the host.
FORWARD is used when the packet is not for the host.
The host acts like a router.
Examples:
PodA → PodB
NodePort → Pod
Ingress Controller → Application Pod
Most Kubernetes traffic goes through FORWARD.
Used for packets created by the host itself.
Examples:
curl from the node
kubelet talking to API Server
Executed after routing decision and immediately before the packet leaves the system.
Used mainly for:
SNAT MASQUERADE
Routing determines where a packet should go.
Linux checks:
Destination IP
and consults:
Routing Table
The routing table tells Linux:
Which interface should carry the packet.
Which next hop should be used.
Routing happens after PREROUTING.
This is why DNAT must happen before routing.
If the destination IP changes, Linux needs the new destination before choosing the route.
NAT means modifying addresses.
There are three major types.
Destination NAT changes the destination address.
Example:
Destination = 10.96.0.25
becomes
Destination = 172.16.212.50
Used by:
Services NodePort Ingress
Source NAT changes the source address.
Example:
Source = 172.16.212.50
becomes
Source = 192.168.241.141
Used when traffic leaves the node.
MASQUERADE is dynamic SNAT.
Used when the external IP can change.
Commonly used when Pods access the Internet.
Conntrack is the kernel's connection tracking database.
It remembers:
Source IP Source Port Destination IP Destination Port Protocol
Suppose:
172.16.212.50:50000
connects to
10.96.0.25:80
After DNAT:
10.96.0.25
becomes
172.16.212.10
Conntrack remembers this translation.
When the response returns, conntrack ensures the reverse translation happens correctly.
Without conntrack, NAT would not work reliably.
Every Pod gets its own Network Namespace.
Inside the namespace the Pod has:
To connect the Pod to the host, Linux creates a veth pair.
Think of a veth pair as a virtual Ethernet cable.
One side exists inside the Pod.
The other side exists on the host.
Traffic leaving the Pod travels through this virtual cable.
A Service is not an application.
A Service is not a network interface.
A Service is not a process.
A Service is a virtual IP managed by kube-proxy.
Example:
rabbitmq-service
ClusterIP:
10.96.0.25
No application actually listens on 10.96.0.25.
kube-proxy intercepts packets destined for 10.96.0.25 and translates them to a real Pod IP.
kube-proxy watches the API Server.
It learns:
Services Endpoints
It then creates iptables rules.
Example:
10.96.0.25
becomes
172.16.212.50
This translation is performed using DNAT.
Therefore the primary job of kube-proxy is:
Service IP → Pod IP
Applications prefer names rather than IP addresses.
Example:
rabbitmq
instead of
10.96.0.25
CoreDNS watches the API Server and maintains DNS records in memory.
Example:
rabbitmq
→
10.96.0.25
When a Pod performs DNS lookup, CoreDNS returns the Service IP.
CoreDNS performs:
Name → Service IP
kube-proxy performs:
Service IP → Pod IP
Calico provides Pod networking and Network Policies.
Calico does not forward packets itself.
Linux performs the forwarding.
Calico programs:
Routes iptables rules Network Policies
Think:
Linux does the work.
Calico tells Linux what rules to use.
An Ingress object is only a routing rule.
Example:
myrabbitmqui.com
→
rabbitmq-service
The Ingress object itself receives no traffic.
An Ingress Controller such as NGINX reads the Ingress object and performs the routing.
NGINX receives the request, examines the Host header, and chooses the correct Service.
Browser opens:
https://myrabbitmqui.com
DNS returns:
192.168.241.141
Packet arrives at Worker Node.
NIC receives frame.
DMA copies frame into RAM.
NAPI schedules packet processing.
Kernel creates SKB.
Ethernet layer processes frame.
IP layer processes packet.
PREROUTING executes.
kube-proxy translates WorkerIP:443 to Ingress Controller Pod IP.
Routing decision occurs.
Packet traverses FORWARD chain.
Calico policy is checked.
Packet reaches NGINX Ingress Controller.
NGINX reads:
Host: myrabbitmqui.com
NGINX chooses:
rabbitmq-service
NGINX sends a new request to:
10.96.0.25
PREROUTING executes again.
kube-proxy translates:
10.96.0.25
to
172.16.212.50
Routing occurs.
Packet traverses FORWARD chain.
Packet reaches RabbitMQ Pod.
TCP delivers the packet to RabbitMQ's socket.
RabbitMQ application processes the request and sends the response back.