Kubernetes starts from one strict rule — the flat network model — and everything else on this page (Services, Ingress, NetworkPolicy) exists to make that flat, wide-open network usable and safe at scale.
This page covers Kubernetes networking specifically. For the rest of the platform — architecture, control plane, workloads, scheduling, storage, and policy — see the standalone Kubernetes subject.
Kubernetes requires: every Pod gets its own cluster-wide IP; any Pod can reach any other Pod's IP directly, with no NAT, even across nodes; and a Pod sees the same IP for itself that everyone else sees for it. Unlike the Docker bridge network you saw on the previous page, where a container's IP is only meaningful inside its own host, a Pod IP is routable cluster-wide from the moment it's created.
frontend-x7d on Node A can open a connection straight to 10.244.2.3 on Node B — no port mapping, no translation, exactly like two hosts on the same LAN.
Kubernetes itself doesn't implement the flat network — it delegates to a CNI plugin. When a Pod is scheduled, kubelet calls the plugin, which creates the Pod's network namespace, wires up a veth pair (the same primitive again), assigns an IP, and configures whatever mechanism makes that IP reachable from every other node.
| CNI plugin | How it makes Pod IPs reachable across nodes |
|---|---|
| Flannel | Wraps Pod traffic in VXLAN tunnels between nodes — simple to run, with tunneling overhead on every packet. |
| Calico | Uses real routing (often BGP) so Pod IPs are just normal routes — no tunnel encapsulation overhead, but requires routable infrastructure. |
| Cilium | Uses eBPF to move packets without iptables at all, adding rich L3-L7 visibility and policy enforcement along the way. |
Pods are disposable — rescheduled, restarted, replaced, each time with a new IP. A Service gives callers one stable thing to talk to, no matter how many times the Pods behind it change.
| Type | What it adds | Reachable from |
|---|---|---|
| ClusterIP | A stable virtual IP, load-balanced across matching Pods (the default type) | Inside the cluster only |
| NodePort | ClusterIP, plus the same static port (30000-32767) opened on every node's IP | Anywhere that can reach any node |
| LoadBalancer | NodePort, plus a real external load balancer provisioned by the cloud provider pointing at it | The public internet (if the LB is public) |
Headless (clusterIP: None) | No virtual IP at all — DNS returns the individual Pod IPs directly | Callers that specifically want to address Pods themselves, e.g. StatefulSets |
Step through what actually happens when a browser hits a URL that's ultimately served by one Pod out of several.
Every Service automatically gets a name in cluster DNS, served by CoreDNS. Try it:
An Ingress is just a set of routing rules — host and path to Service — not a running proxy by itself. It only works because an Ingress Controller is deployed to actually read those rules and act on them, letting one entry point route to many Services instead of paying for a LoadBalancer Service per Service.
| Host | Path | Routed to Service |
|---|---|---|
| shop.example.com | /api | orders-svc:80 |
| shop.example.com | / | storefront-svc:80 |
| admin.example.com | / | admin-svc:80 |
The flat network model means, by default, any Pod can reach any other Pod — there's no isolation at all until you add one. The moment a NetworkPolicy selects a Pod for ingress, that Pod flips from default-allow to default-deny-except-what's-listed.