K8s Networking
K8s Networking
CNI · Services · Ingress
1. The Kubernetes Network Model2. CNI Plugins Compared3. Service Types4. Ingress & Gateway API5. DNS Inside the Cluster6. NetworkPolicy = Microsegmentation7. Troubleshooting Toolkit
1. The Kubernetes Network Model
- •Every Pod gets its own IP — no NAT between pods
- •All pods can reach all pods across nodes (flat network)
- •Agents on a node (kubelet) can reach all pods on that node
- •Implementation is delegated to a CNI plugin
Mental model
Think of the cluster as one big flat L3 network where every pod is a host. Everything else (Services, Ingress, NetworkPolicy) is built on top.
2. CNI Plugins Compared
| CNI | Dataplane | Notable |
|---|---|---|
| Calico | L3 / eBPF option | BGP to physical network, rich NetworkPolicy |
| Cilium | eBPF | L7 policy, Hubble observability, kube-proxy replacement |
| Flannel | VXLAN overlay | Simple, no NetworkPolicy by itself |
| AWS VPC CNI | Native VPC IPs | Pods get real VPC IPs — subnet sizing matters |
| Azure CNI | Native VNet IPs | Same idea in AKS |
For network engineers
Calico speaks BGP — you can peer top-of-rack switches with cluster nodes and route pod CIDRs natively. Familiar territory.
3. Service Types
| Type | What it does | Use |
|---|---|---|
| ClusterIP | Virtual IP reachable only inside the cluster | Default — service-to-service |
| NodePort | Opens a port (30000-32767) on every node | Quick external access, labs |
| LoadBalancer | Provisions an external LB (cloud or MetalLB) | Production external services |
| ExternalName | DNS CNAME to an external service | Aliasing external dependencies |
| Headless | No VIP; DNS returns pod IPs directly | StatefulSets, databases |
Service + how kube-proxy implements it
kubectl get svc my-api # kube-proxy programs iptables/IPVS (or Cilium eBPF) rules: # ClusterIP 10.96.4.7:80 -> pod endpoints 10.244.1.5:8080, 10.244.2.9:8080
4. Ingress & Gateway API
- •Ingress = L7 (HTTP) routing: host/path rules to Services
- •Needs an ingress controller (NGINX, Traefik, HAProxy, cloud-native)
- •TLS termination happens at the controller
- •Gateway API is the successor — role-separated, protocol-aware, more expressive
Minimal Ingress
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: web
spec:
rules:
- host: app.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: web
port:
number: 805. DNS Inside the Cluster
| Name | Resolves to |
|---|---|
| my-svc.my-ns.svc.cluster.local | Service ClusterIP |
| my-svc.my-ns | Same (short form from another namespace) |
| my-svc | Same (from the same namespace) |
| pod-ip.my-ns.pod.cluster.local | Individual pod |
CoreDNS runs as a Deployment; every pod's /etc/resolv.conf points at its Service VIP. Most "app can't reach database" tickets are DNS or NetworkPolicy.
6. NetworkPolicy = Microsegmentation
Allow only frontend -> backend on 8080
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: backend-allow-frontend
spec:
podSelector:
matchLabels:
app: backend
ingress:
- from:
- podSelector:
matchLabels:
app: frontend
ports:
- port: 8080- •Default = all traffic allowed; policies are additive allow-lists
- •An empty ingress policy on a pod = deny all inbound
- •Enforced by the CNI — Flannel alone ignores them
- •Think of it as distributed stateful firewall rules per label
7. Troubleshooting Toolkit
| Command | Purpose |
|---|---|
| kubectl get pods -o wide | Pod IPs and nodes |
| kubectl get endpoints my-svc | Does the Service actually have backends? |
| kubectl exec -it pod -- nslookup my-svc | DNS resolution from inside |
| kubectl exec -it pod -- curl -v ip:port | Direct pod-to-pod reachability |
| kubectl logs -n kube-system -l k8s-app=kube-dns | CoreDNS logs |
| kubectl describe networkpolicy | What policy is blocking you |
Debug order
Endpoints exist? → DNS resolves? → direct pod IP reachable? → NetworkPolicy? → CNI/node routing. Bottom of that list is rarely the problem.