Feedback

Chat Icon

Cloud-Native Microservices With Kubernetes - 2nd Edition

A Comprehensive Guide to Building, Scaling, Deploying, Observing, and Managing Highly-Available Microservices in Kubernetes

Understanding and Using StatefulSets and Headless Services
29%

Headless Services: The No-Middleman Approach to Pod Networking

In the previous sections, we understood how a database like PostgreSQL, using a production-ready topology (primary-replica), works and how StatefulSets provide the necessary features for this architecture. We also mentioned that the Pods of our database need to have a stable network identity. Here is the use case:

  • The primary database Pod needs to reach each replica Pod using a stable hostname.
  • If a replica Pod restarts, the primary Pod should still be able to reach it using the same name.
  • The Deployment controller does not guarantee this behavior, but the StatefulSet controller does.

In PostgreSQL replication, each replica connects to the primary to fetch WAL logs. If we tried to use a single ClusterIP Service with a selector like app=postgres to represent all database Pods, it would load balance connections across every Pod that matches that label. That means when a replica tries to connect to the primary through that Service, it might randomly hit another replica - or even itself - because a ClusterIP Service never guarantees which Pod it routes to.

To guarantee replicas always connect to the right primary, we need stable, Pod-specific network identities. That’s why StatefulSets use a headless Service instead of a regular ClusterIP.

A headless Service is, in reality, a ClusterIP Service with no cluster IP assigned (clusterIP: None). This means that the Service does not load balance traffic or have a stable IP address. Instead, it provides DNS records for each Pod in the StatefulSet and allows individual Pods to be addressed directly.

Here is a test to illustrate how this type of Service works:

kubectl apply -f - <
# Create a hello-world StatefulSet
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: hello-world
spec:
  # Define the headless service name
  serviceName: "hello-world-headless"
  replicas: 3
  selector:
    matchLabels:
      app: hello-world
  template:
    metadata:
      labels:
        app: hello-world
    spec:
      containers:
      - name: hello-world
        image: gcr.io/google-samples/hello-app:1.0
        ports:
        - containerPort: 8080
---
# Create a headless service for the hello-world StatefulSet
apiVersion: v1
kind: Service
metadata:
  name: hello-world-headless
spec:
  clusterIP: None
  selector:
    app: hello-world
  ports:
  - port: 8080
---
# Create a ClusterIP service for the hello-world StatefulSet
apiVersion: v1
kind: Service
metadata:
  name: hello-world-clusterip
spec:
  selector:
    app:

Cloud-Native Microservices With Kubernetes - 2nd Edition

A Comprehensive Guide to Building, Scaling, Deploying, Observing, and Managing Highly-Available Microservices in Kubernetes

Enroll now to unlock all content and receive all future updates for free.