0%
Containers and Kubernetes: From cgroups to Operators
What a Container Actually Is
Working with Docker
Kubernetes Architecture
Workloads
Scheduling and Resources
Extending Kubernetes
Services, the Proxy and Cluster DNS
A service is a stable name and address in front of a changing set of pods. Nothing serves traffic at that address: it is a fiction maintained by rules on every node, and the set it fronts is published as a separate object.
The cluster address is allocated, not assigned
One allocation step happens at creation and explains several oddities.
When a service is created, a virtual address is allocated from a range configured on the interface server, recorded on the object, and never changes for the service's lifetime. The range is cluster-wide and finite, so exhausting it means no new services, which is a real limit on large clusters with many small services.
Nothing listens on that address. No interface holds it, no process binds it, and a packet sent to it is rewritten by rules on the originating node before it leaves. That is why the address is unreachable from outside the cluster and why a packet capture on a pod shows traffic to a pod address rather than to the service address.
The selector produces endpoint slices
One controller turns a selector into a published set.
A controller watches pods matching the service's selector and writes endpoint slice objects listing their addresses, ports and per-address conditions. Each slice holds up to a hundred endpoints by default, so a service with a thousand pods has ten slices, and a pod changing state rewrites only the slice containing it.
That sharding exists for a specific reason. The older single-object representation meant every pod change rewrote a list of every endpoint, which every node then received, so a large service produced enormous update traffic on every rollout. Slices bound the size of each update to a hundred endpoints.
The three endpoint conditions
Each address carries three booleans and the distinction between them matters.
Ready means the pod passed its readiness probe and should receive traffic. Serving means the pod can handle traffic, which can be true while the pod is terminating. Terminating means the pod is shutting down.
The pair that matters is serving and terminating both true, which describes a pod in its grace period that is still accepting connections. A proxy honouring that state can keep sending traffic to a draining pod, which is what allows a long-lived connection to finish, while a proxy that only looks at ready removes it immediately.
Ports, names and the target
One mapping is a frequent source of confusion.
A service port declares the port clients connect to and a target port naming the port on the pod, which may be a number or the name of a port declared in the pod's specification. Using the name is the more robust choice, because a pod can change its listening port without the service changing.
The trap is a target port naming a port the pod does not declare, which produces endpoint slices with no addresses and a service that accepts connections and refuses them. The symptom is a connection refused with a healthy pod behind it, and the cause is a name mismatch rather than anything in the network.
Headless services
One variant turns off the fiction entirely.
Setting the cluster address to none means no virtual address is allocated and no rules are programmed. Instead, resolving the service's name returns the pod addresses directly, one record per ready endpoint, so the client sees the individual pods.
That is what stateful workloads need: a client that must address a specific replica, a database driver that wants to know the members, or a protocol that does its own load balancing. It moves the balancing decision from the kernel to the client, which is more capable but means the client must handle membership changes itself.
Services without selectors
One shape exists for pointing at something outside.
A service with no selector gets no endpoint controller, so its endpoint slices can be written by hand to any addresses, including addresses outside the cluster. That gives an in-cluster name and address for an external database or a legacy system, with the same resolution path as any other service.
The external-name variant is different and often confused with it: it creates no address and no rules at all, only a DNS alias returning a canonical name. That means no load balancing, no port mapping and no help if the target name resolves to something the pod cannot reach, and it fails in a way that looks like a DNS problem.
The multiple-port case
One rule applies when a service exposes several ports.
Every port must be named once more than one exists, because the names appear in the endpoint slice ports and in DNS service records. An unnamed second port is rejected.
The less obvious consequence is that the ports share one set of endpoints and one selector, so all ports go to the same pods. A service fronting two different pod sets is not possible: that requires two services, which is usually the right model anyway since the two sets have different readiness.
How a client finds a service
Two mechanisms exist and only one is in common use.
Environment variables are injected into every container for every service that existed when the pod started, which means they are stale for anything created afterwards and they are injected for every service in the namespace, a list that becomes absurd on a large cluster. They are a legacy mechanism.
DNS is the real mechanism: a name resolving to the service's address, discussed in detail later. The practical implication is that application configuration should use names rather than the injected variables, both for freshness and because the variable names depend on the service name in a way that breaks when a service is renamed.
Topology and what the endpoint slice carries
One field enables locality-aware routing.
Each endpoint records the node and zone it is on, and optionally a set of hints saying which zones should prefer it. That lets a proxy prefer endpoints in its own zone, which saves cross-zone traffic cost and reduces latency.
The hints are computed by a controller that tries to balance load against locality, so a zone with few endpoints and many clients still receives traffic from elsewhere. That balancing is the part that makes the feature safe, and it is also why enabling it does not simply pin all traffic to the local zone.
Reading the state of a service
Three objects answer any question about whether a service is wired correctly.
The service itself shows the allocated address, the ports and the selector. The endpoint slices show which pod addresses are published and with what conditions, which is where a service with no backends is diagnosed. And the pods show their labels and readiness, which is where a selector mismatch or a failing probe appears.
The order matters: a service with empty slices is a selector or readiness problem and no amount of network debugging will help, while a service with populated slices and failing traffic is a proxy or routing problem. Checking the slices first separates those two worlds in one command.
What the abstraction costs
One honest accounting is worth making.
The service gives a stable name, a stable address, load balancing without a client library, and health-aware membership for free. In exchange it adds a layer of address rewriting on every packet, a cluster-wide address range that can be exhausted, a set of rules on every node that must be kept current, and a propagation delay between a pod becoming ready and traffic reaching it.
Every subsequent section in this lesson is about one of those costs, because the abstraction is simple and its failure modes all live in the machinery underneath it.
A strong answer explains that nothing listens on the service address: it is allocated from a cluster range, recorded on the object, and implemented by rewriting rules on every node, which is why a packet capture shows traffic to a pod address. It then notes that the membership is a separate set of objects sharded at a hundred endpoints, carrying ready, serving and terminating per address, and that the serving plus terminating combination is what lets a draining pod finish its work.
Loading problem...