Services: ClusterIP, NodePort, LoadBalancer
The idea in one sentence
Section titled “The idea in one sentence”A Service gives a shifting set of Pods a single stable virtual IP and DNS name, load-balancing traffic across whichever matching Pods are currently healthy.
The problem: Pod IPs are not stable
Section titled “The problem: Pod IPs are not stable”Pods are disposable. A Deployment reschedules a crashed Pod onto a different node, a rollout replaces every Pod with new ones, and the cluster autoscaler adds and removes nodes — every one of these events can give a Pod a brand new IP address. Anything that hardcodes a Pod IP breaks the moment that Pod is replaced.
A Service solves this by sitting in front of a group of Pods. It picks its members using a label selector, and it tracks which of those Pods are currently ready through an Endpoints object (or the newer, scalable EndpointSlice objects). As Pods come and go, the controller updates the Endpoints/EndpointSlice automatically — clients never need to know.
apiVersion: v1kind: Servicemetadata: name: paymentsspec: selector: app: payments ports: - port: 80 targetPort: 8080# See which Pods this Service is currently routing tokubectl get endpoints paymentskubectl get endpointslices -l kubernetes.io/service-name=paymentsAny Pod whose labels match app: payments and that is passing its readiness probe is added to that list automatically. Delete the Pod and it drops out; a replacement Pod with the same labels is added back in — the Service’s IP and DNS name never change.
Three Service types, each building on the last
Section titled “Three Service types, each building on the last”spec.type controls how the Service is reachable, and the three common types form a ladder — each one builds on the one before it.
- ClusterIP (the default) — a virtual IP reachable only from inside the cluster. This is what most Pod-to-Pod communication uses.
- NodePort — everything ClusterIP does, plus the same port is opened on every node in the cluster, in the
30000–32767range by default. Hitting<any-node-ip>:<nodePort>reaches the Service from outside the cluster. - LoadBalancer — everything NodePort does, plus the cluster asks the cloud provider to provision an external load balancer that forwards to the NodePort.
apiVersion: v1kind: Servicemetadata: name: payments-publicspec: type: LoadBalancer selector: app: payments ports: - port: 80 targetPort: 8080 nodePort: 30080kubectl get svc payments-public# EXTERNAL-IP column is populated once the cloud provider finishes provisioningkube-proxy makes the virtual IP real
Section titled “kube-proxy makes the virtual IP real”A Service’s ClusterIP is not backed by a real network interface anywhere — it is a virtual address. kube-proxy, running on every node, watches the API server for Services and Endpoints/EndpointSlices and programs local rules (typically iptables or IPVS, depending on the configured mode) so that any packet sent to the virtual IP gets transparently rewritten and delivered to one of the backing Pods. This is why a Service works identically no matter which node a client Pod happens to be on.
flowchart LR client["Client Pod"] --> vip["Service virtual IP\n(ClusterIP)"] vip --> kp["kube-proxy rules\n(iptables / IPVS)"] kp --> p1["Pod 1"] kp --> p2["Pod 2"] kp --> p3["Pod 3"]