flowchart LR
subgraph N1["node worker-1"]
A["pod web<br/>192.168.1.5"]
end
subgraph N2["node worker-2"]
B["pod api<br/>192.168.2.7"]
end
A <-- "pod network (CNI), no NAT" --> B
S["Service api<br/>ClusterIP 10.96.0.20"] -. "kube-proxy" .-> B
Two networks in a cluster
- Pod network — a flat cluster-wide network for pod-to-pod traffic, built by a CNI plugin that also assigns IPs (Calico, Cilium, Flannel…).
- Service network — a range of virtual IPs (ClusterIPs) for Services, implemented by kube-proxy as rules on each node.
Four rules
- Containers in the same pod talk over
localhost. - Every pod reaches every other pod without NAT, even on another node.
- Every node reaches every pod without NAT.
- The IP a pod sees for itself is the IP other pods see.
Common traps
- After
kubeadm init, nodes stay NotReady until a CNI plugin is installed; CoreDNS staysPendingtoo. - Namespaces do not isolate the network. A pod in
devcan still reach a pod inprod. To block it, use a NetworkPolicy, and the CNI must support it (Calico and Cilium do; Flannel does not enforce NetworkPolicy).
| Command | Meaning |
|---|---|
kubectl get pods -o wide |
Pod IPs and the node running each pod |
kubectl get networkpolicies -n dev |
NetworkPolicies in the dev namespace |
kubectl apply -f calico.yaml |
Install a CNI (manifest from the official Calico site) |
CNI plugins compared
| Plugin | Notes |
|---|---|
| Flannel | Simple overlay, easy to start, no NetworkPolicy enforcement |
| Calico | Routing or overlay, full NetworkPolicy support, widely used on-premises |
| Cilium | eBPF-based, NetworkPolicy plus L7 rules and observability, can replace kube-proxy |
| Cloud CNIs (AWS VPC CNI, Azure CNI) | Pods get addresses from the cloud network, integrating with its routing and security |
How a Service is reached
kube-proxy watches Services and their EndpointSlices (the list of ready pod IPs) and programs each node. In iptables mode it writes NAT rules; ipvs and the newer nftables modes scale better with thousands of Services. Some CNIs such as Cilium handle Services themselves with eBPF and run without kube-proxy.
DNS inside the cluster
CoreDNS gives every Service a name: api from the same namespace, api.prod from another namespace, and the full form api.prod.svc.cluster.local. A pod's /etc/resolv.conf points to CoreDNS and lists search domains so short names work.
A default-deny NetworkPolicy
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-ingress
namespace: prod
spec:
podSelector: {}
policyTypes: ["Ingress"]
This blocks all incoming traffic to pods in prod; further policies then allow only what is needed, for example from pods labelled app: web to pods labelled app: api on port 8080. Policies are additive: traffic is allowed if any policy allows it.
Traffic from outside
The pod network is internal. Traffic from users enters through a Service of type LoadBalancer or NodePort, an Ingress controller, or the newer Gateway API, covered in the Services module. Service meshes such as Istio or Linkerd add mutual TLS, retries, and traffic splitting between services, at the cost of extra components.