Document contract

  • Role: concept
  • Truth boundary: portable Kubernetes behavior observed through devata
  • Last verified: Kubernetes documentation, 2026-07-26

Prerequisites: kubernetes

Pods are replaceable. A rescheduled Pod gets a new IP, so its address is not a durable contract for callers. A Service supplies a stable DNS name and virtual address in front of a changing set of eligible Pods.

The Service does not proxy bytes by itself. Three mechanisms cooperate:

flowchart LR
  caller --> dns[CoreDNS name]
  dns --> service[Service and ClusterIP]
  selector[Label selector] --> endpoints[EndpointSlice backends]
  endpoints -. eligible IPs .-> dataplane[Cilium or kube-proxy]
  service --> dataplane --> pod[Ready Pod]
  1. the Service selector describes which Pods belong behind it;
  2. EndpointSlice controllers publish their current addresses and readiness;
  3. a dataplane implements translation from the virtual Service address to one backend.

In devata, cilium implements the dataplane with eBPF because kube-proxy is disabled. On many clusters kube-proxy programs iptables or IPVS rules for the same contract.

These commands are read-only:

kubectl config current-context
kubectl -n monitoring get service kps-grafana -o wide
kubectl -n monitoring get service kps-grafana -o jsonpath='{.spec.selector}'
kubectl -n monitoring get endpointslice \
  -l kubernetes.io/service-name=kps-grafana -o wide
kubectl -n monitoring get pods --show-labels -o wide

Expected observations:

  • the Service has a stable clusterip and a label selector;
  • the EndpointSlice contains current backend IPs;
  • those IPs match Pods whose labels satisfy the selector;
  • backend readiness can remove a Pod from normal traffic without deleting it.

A Service with no endpoints is a valid object with no usable backends. A Service with endpoints can still fail because of DNS, network policy, dataplane, port mapping, or application behavior. Test the transaction, not only the objects.

Service types are exposure choices

  • ClusterIP is reachable through the cluster network and is the default.
  • NodePort reserves a port on nodes and forwards it to the Service.
  • LoadBalancer asks an implementation such as metallb for an external address.
  • ExternalName returns a DNS alias and does not select Pods.

Gateway API and Ingress can route HTTP traffic to a Service, but they do not replace the Service’s stable backend contract.

Check yourself

  1. Why does deleting and recreating a backend Pod not require callers to learn a new address?
  2. Which object proves the current backend set, and which field links it to the Service?
  3. What evidence distinguishes a selector mistake from a broken dataplane?

References