Networking, CNI, Services and DNS
Networking, CNI, Services and DNS
The Kubernetes network model
Three rules, implemented by the CNI plugin:
- Every Pod gets its own IP.
- Every Pod reaches every other Pod - on any node - without NAT.
- Agents on a node reach all Pods on that node.
Nothing else is promised: how packets travel is the CNI’s choice.
CNI: overlays and routing
graph LR
subgraph worker01
PA[Pod 10.244.1.5]
end
subgraph worker02
PB[Pod 10.244.2.7]
end
PA -->|"encapsulated in node-to-node packet<br/>(VXLAN / IP-in-IP)"| PB
| Mode | How | Trade-off |
|---|---|---|
| Overlay - VXLAN, IP-in-IP | Pod packet wrapped in a node packet | works on any network; small overhead |
| Native routing - BGP | nodes announce Pod routes to each other / the router | no overhead; network must cooperate |
| eBPF data plane | packet handling in the kernel, replaces kube-proxy | fastest; newer kernels |
| Plugin | Overlay / routing | NetworkPolicy | Notes |
|---|---|---|---|
| Calico (this cluster) | VXLAN, IP-in-IP, BGP, eBPF | yes + own policy CRDs | most common on-prem |
| Cilium | VXLAN, native, eBPF | yes + L7 policies | eBPF-first, Hubble observability |
| Flannel | VXLAN | no | simple, no policies |
| Cloud CNIs (Azure, AWS VPC) | VPC-native IPs | via Calico / Cilium | managed clusters |
A CNI without NetworkPolicy support silently ignores your policies. Policies are Day 4.
Services
Pods come and go; a Service is the stable address in front of them.
graph LR
C[Client] --> S[Service web<br/>ClusterIP 10.96.x.x]
S --> P1[Pod 10.244.1.5]
S --> P2[Pod 10.244.2.7]
S --> P3[Pod 10.244.3.9]
The Service selects Pods by label. Ready Pod IPs land in an EndpointSlice; kube-proxy (iptables, IPVS or nftables mode) programs every node to translate the ClusterIP to a Pod IP.
| Type | Reachable from | Note |
|---|---|---|
ClusterIP | inside the cluster | default |
NodePort | <any-node>:30000-32767 | opens the port on every node |
LoadBalancer | outside | cloud or MetalLB - not in this cluster |
ExternalName | - | DNS CNAME only |
headless (clusterIP: None) | - | DNS returns Pod IPs (StatefulSets) |
spec:
type: NodePort
selector:
app: web # must match the Pod labels
ports:
- port: 80 # Service port
targetPort: 8080 # container port
DNS (CoreDNS)
web # same namespace
web.shop # namespace shop
web.shop.svc.cluster.local # fully qualified
db-0.db.shop.svc.cluster.local # one StatefulSet Pod
When it does not work
kubectl get endpointslices -l kubernetes.io/service-name=web
No endpoints = selector matches no ready Pod. Check labels, readiness,
then targetPort.