The default Kubernetes service provides a stable IP address that allows clients to connect to pods.

However, you don’t control which pod you are connecting to. The service forwards the connection to a randomly selected backing pod.

Such a service is known as the Cluster IP service.

But what if the client has a need to connect to all the pods?

Maybe, each individual pod needs to connect to all the other pods behind a service?

This is made possible using the Kubernetes Headless Service.

1 – Uses of Kubernetes Headless Service

Here are a few use-cases of the Kubernetes Headless Service:

  • Stateful services that preserve the state of a request and it’s important for subsequent connections to connect to the same pod
  • Deploying relational databases with replicas that need to talk with each other
  • Deploying message brokers like RabbitMQ
  • Implementing custom load balancing logic based on individual pod capacity
  • Setting up advanced health checks

By the way, if you are interested in Kubernetes & Cloud Native concepts, you’d love the Progressive Coder newsletter where I explain such topic in a fun & interesting manner.

Subscribe now to join along.

2 – Create a Headless Service in Kubernetes

A typical service (ClusterIP) looks like this:

apiVersion: v1
kind: Service
metadata:
  name: demo
spec:
  ports:
  - port: 80
    targetPort: 3000
  selector:
    app: hello-service

Even if you don’t mention a service type value, Kubernetes defaults to ClusterIP.

To create a headless service in Kubernetes, you can simply specify the clusterIP as None.

See the below example:

apiVersion: v1
kind: Service
metadata:
  name: demo-headless
spec:
  clusterIP: None
  ports:
  - port: 80
    targetPort: 3000
  selector:
    app: hello-service

This makes the service headless.

You can apply the service using the below command:

$ kubectl apply -f demo-headless.yaml

After it is successfully applied, you can check the details of the service.

saurabhdashora@Saurabhs-MacBook-Air basic-pod-demo % kubectl describe svc demo-headless
Name:              demo-headless
Namespace:         default
Labels:            <none>
Annotations:       <none>
Selector:          app=hello-service
Type:              ClusterIP
IP Family Policy:  SingleStack
IP Families:       IPv4
IP:                None
IPs:               None
Port:              <unset>  80/TCP
TargetPort:        3000/TCP
Endpoints:         10.1.0.124:3000,10.1.0.128:3000,10.1.0.131:3000
Session Affinity:  None
Events:            <none>

As you can notice, the IP address is None. The Endpoints field shows the IP addresses of the backing pods. If interested, you can read more about the concept of endpoints in Kubernetes.

In contrast, if you describe the properties of a ClusterIP service, you see the below output.

saurabhdashora@Saurabhs-MacBook-Air basic-pod-demo % kubectl describe svc demo         
Name:              demo
Namespace:         default
Labels:            <none>
Annotations:       <none>
Selector:          app=hello-service
Type:              ClusterIP
IP Family Policy:  SingleStack
IP Families:       IPv4
IP:                10.100.68.216
IPs:               10.100.68.216
Port:              <unset>  80/TCP
TargetPort:        3000/TCP
Endpoints:         10.1.0.124:3000,10.1.0.128:3000,10.1.0.131:3000
Session Affinity:  None
Events:            <none>

In this case, the IP address field has a specific value.

3 – Testing the Kubernetes Headless Service

For testing purpose, you can perform a DNS lookup. For that, you can use the nslookup command.

Since we want to do the testing within the context of our Kubernetes cluster, we can create a pod with the necessary binaries.

See the below YAML:

apiVersion: v1
kind: Pod
metadata:
  name: dnsutils
  namespace: default
spec:
  containers:
  - name: dnsutils
    image: registry.k8s.io/e2e-test-images/jessie-dnsutils:1.3
    command:
      - sleep
      - "infinity"
    imagePullPolicy: IfNotPresent
  restartPolicy: Always

Basically, when you apply this YAML, you get a pod that runs the jessie-dnsutils image that contains the necessary binaries to run the nslookup command.

You can now execute the nslookup command for the demo-headless service as below:

saurabhdashora@Saurabhs-MacBook-Air basic-pod-demo % kubectl exec dnsutils -- nslookup demo-headless
Server:		10.96.0.10
Address:	10.96.0.10#53

Name:	demo-headless.default.svc.cluster.local
Address: 10.1.0.124
Name:	demo-headless.default.svc.cluster.local
Address: 10.1.0.131
Name:	demo-headless.default.svc.cluster.local
Address: 10.1.0.128

As you can see, the output contains a list of the IP address of 3 different pods.

In contrast, if you execute the same command for the normal ClusterIP service, you get only one IP address.

saurabhdashora@Saurabhs-MacBook-Air basic-pod-demo % kubectl exec dnsutils -- nslookup demo
Server:		10.96.0.10
Address:	10.96.0.10#53

Name:	demo.default.svc.cluster.local
Address: 10.100.68.216

Note that Kubernetes headless service isn’t so different from a regular service. Clients can still connect to the pods using the service’s DNS name.

However, they can also connect to individual pods.

Conclusion

That’s all for this post.

Kubernetes Headless Service is a great tool for some specific requirements with regards to connecting directly with pods sitting behind a service.

In this post, you learned how to setup such a service and test it.

If you have any comments or queries, please mention them in the comments section below.

Anyways, before we end this post, a quick reminder about the Progressive Code Newsletter where I explain software concepts using a storytelling approach so that you never forget what you’ve learned. I’m 100% sure you’d love it.

Subscribe now and see you over there.

Categories: BlogKubernetes

Saurabh Dashora

Saurabh is a Software Architect with over 12 years of experience. He has worked on large-scale distributed systems across various domains and organizations. He is also a passionate Technical Writer and loves sharing knowledge in the community.

0 Comments

Leave a Reply

Your email address will not be published. Required fields are marked *