Services
Pods are ephemeral and their IPs change every time they are recreated. A Service solves this: it is a stable virtual IP and DNS name in front of a changing set of Pods, selected by label. Clients talk to the Service; Kubernetes load-balances to whichever Pods currently match.
Service is the object that makes microservices on Kubernetes practical. Without it, every client would need to track Pod IPs as they come and go. With it, a name like orders just works, no matter how the Pods behind it churn.
Why Pod IPs Are Not Enough
Each Pod has an IP, but it is transient — reschedule the Pod and the IP changes; scale out and there are several. A Service gives the group one stable virtual IP (the ClusterIP) and a DNS name that persists for the life of the Service. Behind it, an EndpointSlice tracks the current set of ready Pod IPs, updated continuously as Pods come and go.
The selector does the matching. A Service with selector app=orders routes to every ready Pod labeled app=orders. Add a Pod with that label and it joins; remove the label and it leaves. The client never knows or cares.
Service Types
Four types cover the range from internal to external. ClusterIP (the default) exposes the Service on an internal IP reachable only inside the cluster. NodePort opens a fixed port on every node. LoadBalancer provisions an external cloud load balancer pointing at the Service. ExternalName is a DNS alias to an external host, with no proxying.
| Type | Reachable from | Typical use |
|---|---|---|
| ClusterIP | Inside the cluster only | Service-to-service (the default) |
| NodePort | Any node's IP at a fixed port | Dev, or behind an external LB |
| LoadBalancer | The internet, via a cloud LB | Exposing a service externally |
| ExternalName | DNS alias, no proxy | Pointing at an external dependency |
apiVersion: v1 kind: Service metadata: name: orders spec: selector: app: orders # routes to ready Pods with this label ports: - port: 80 # the Service port targetPort: 8080 # the container port
How kube-proxy Routes Traffic
A Service IP is virtual — no process listens on it. On each node, kube-proxy programs the kernel (via iptables or IPVS) so that packets to the ClusterIP are rewritten to one of the backing Pod IPs. Load balancing is per-connection and effectively random; it is not application-aware. For richer routing you reach up to Ingress or a service mesh.
A headless Service (clusterIP: None) skips the virtual IP entirely and instead returns the individual Pod IPs via DNS. This is what stateful systems use when a client needs to address specific Pods rather than a load-balanced pool — the companion to StatefulSets.
Service vs Ingress
A Service is a layer-4 construct: it balances TCP/UDP connections to Pods. It does not understand HTTP, hostnames, paths, or TLS. When you need host- and path-based routing, a single external entry point for many services, or HTTP features, that is the job of Ingress or the Gateway API (Topic 09), which sit in front of Services.
ClusterIP — internal-only stable IP; the building block the others extend.
NodePort — exposes a port on every node; rarely the right external answer on its own.
LoadBalancer — one cloud load balancer per Service; simple but multiplies cost across many services.
Ingress — one HTTP entry point routing to many Services by host/path — usually what you actually want externally.
- Exposing production traffic via NodePort directly instead of a LoadBalancer or Ingress.
- Expecting a LoadBalancer Service to work without a cloud controller to provision the external LB.
- A selector that matches no Pods (a typo), leaving the Service with empty endpoints and silent failures.
- Provisioning one LoadBalancer per service and paying for dozens of cloud LBs instead of fronting them with one Ingress.
- Assuming kube-proxy gives HTTP-aware or session-sticky routing — it balances connections, not requests.
- Default to ClusterIP for internal services; expose externally through Ingress, not a LoadBalancer per service.
- Verify a Service's EndpointSlice is populated when debugging — empty endpoints almost always mean a selector mismatch.
- Use a headless Service for stateful workloads that need to address individual Pods.
- Keep
portandtargetPortstraight: the Service port is what clients use, targetPort is the container's. - Reach for Ingress or a mesh when you need HTTP routing, TLS, or host/path rules — not more Services.
Knowledge Check
What problem does a Service primarily solve?
- It gives a stable IP and DNS name in front of Pods whose own IPs change as they are recreated
- It builds the container images and pushes them to the registry for its Pods
- It automatically encrypts all traffic between the backing Pods with mutual TLS
- It schedules each backing Pod onto whichever cluster node currently has the most free capacity
A Service has empty endpoints and clients get connection failures. What is the most likely cause?
- The Service selector does not match any ready Pod labels
- kube-proxy has crashed on every node
- The Service type is left at its ClusterIP default instead of LoadBalancer
- The Pods have far too much memory allocated in their resource requests
You need host- and path-based HTTP routing into the cluster. Which object fits?
- Ingress or the Gateway API, which sit in front of Services
- A separate NodePort Service defined for each host and path route
- A headless Service returning Pod IPs directly to clients
- An ExternalName Service aliased to an external DNS name
You got correct